欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c# WPF中通过双击编辑DataGrid中Cell的示例(附源码)

程序员文章站 2022-03-07 23:50:01
背景  在很多的时候我们需要编辑datagrid中每一个cell,编辑后保存数据,原生的wpf中的datagrid并没有提供这样的功能,今天通过一个具体的例子来实现这一个功能,在这个例子中datagr...

背景

  在很多的时候我们需要编辑datagrid中每一个cell,编辑后保存数据,原生的wpf中的datagrid并没有提供这样的功能,今天通过一个具体的例子来实现这一个功能,在这个例子中datagrid中的数据类型可能是多种多样的,有枚举、浮点类型、布尔类型、datetime类型,每一种不同的类型需要双击以后呈现不同的效果,本文通过使用xceed.wpf.datagrid这个动态控件库来实现这个功能,当前使用的dll版本是2.5.0.0,不同的版本可能实现上面有差别,这个在使用的时候需要特别注意。

demo预览

c# WPF中通过双击编辑DataGrid中Cell的示例(附源码)

代码结构

  代码还是按照常规的mvvm结构来进行编写,主要包括views、models、mainwindowviewmodel、converters这些常规的结构,在介绍这些之前来说一下我们的整体结构,在demo中我们准备了一个四行三列的datagrid,其中第一列主要是表示当前行的名称、后面的step列是根据代码动态进行添加的,这个step部分是我们通过代码动态调整的,调整完成后能够将数据保存到数据源中,我们还是按照mvvm的结构来进行进行代码的介绍。

  1 mainwindow

<window x:class="datagridcelldoubleclickdemo.mainwindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:xceed="http://schemas.xceed.com/wpf/xaml/datagrid"
        xmlns:models="clr-namespace:datagridcelldoubleclickdemo.models"
        xmlns:views="clr-namespace:datagridcelldoubleclickdemo.views"
        mc:ignorable="d"
        title="datagriddemo" height="450" width="800">
    <window.resources>
        <datatemplate x:key="customtemplate">
            <border borderthickness="1" borderbrush="blue">
                <textblock text="{binding path=display }"  fontweight="bold"
                           horizontalalignment="stretch" verticalalignment="stretch" />
            </border>
        </datatemplate>
        <datatemplate x:key="rowheadtemplate" datatype="{x:type models:recipecontrolvariable}">
            <textblock text="{binding displayname}"  horizontalalignment="stretch" verticalalignment="stretch" foreground="black" fontsize="12"/>
        </datatemplate>
        <xceed:datagridcollectionviewsource x:key="recipedata" source="{binding recipevariables}"></xceed:datagridcollectionviewsource>
    </window.resources>
    <grid>
        <xceed:datagridcontrol x:name="datagridcontrol"
                               autocreatecolumns="false"
                               fontsize="13"
                               verticalcontentalignment="center"
                               borderbrush="gray"
                               borderthickness="1"
                               itemsprimaryaxis="horizontal"
                               pagingbehavior="lefttoright"
                               updatesourcetrigger="cellcontentchanged"
                               selectionunit="cell"
                               selectionmode="single"                              
                               itemssource="{binding  source={staticresource recipedata}}">
            <xceed:datagridcontrol.view>
                <xceed:tableflowview fixedcolumncount="1" containerheight="30" x:name="tblview"
                                        verticalgridlinethickness="0.5" horizontalgridlinebrush="gray"
                                        horizontalgridlinethickness="1" verticalgridlinebrush="black"
                                        rowfadeinanimationduration="0"
                                        scrollinganimationduration="0" columnstretchminwidth="10"
                                        detailindicatorwidth="20" showrowselectorpane="false"
                                        showscrolltip="false" usedefaultheadersfooters="false">
                    <xceed:tableflowview.fixedheaders>
                        <datatemplate>
                            <xceed:columnmanagerrow allowcolumnreorder="false" allowcolumnresize="true" allowdrop="false" allowsort="false" />
                        </datatemplate>
                    </xceed:tableflowview.fixedheaders>
                </xceed:tableflowview>
            </xceed:datagridcontrol.view>
 
            <xceed:datagridcontrol.defaultcelleditors>
                <xceed:celleditor x:key="{x:type models:smartcellviewmodel}">
                    <xceed:celleditor.edittemplate>
                        <datatemplate>
                            <views:smartcelleditor content="{xceed:celleditorbinding}"  verticalalignment="center"
                                                   height="{binding actualheight,relativesource={relativesource ancestortype={x:type border},ancestorlevel=1}}"></views:smartcelleditor>
                        </datatemplate>
                    </xceed:celleditor.edittemplate>
                </xceed:celleditor>
            </xceed:datagridcontrol.defaultcelleditors>
 
        </xceed:datagridcontrol>
    </grid>
</window>

  在view部分主要是通过引用xceed中的datagridcontrol控件进行扩展的,这个里面主要是需要设置datagridcontrol的view和defaultcelleditor这个里面defaultcelleditor是本文的重点,这个就是单元格cell双击后进行编辑的主体,在这个里面我们需要指定celleditor的edittemplate,这里面需要匹配一个datatemplate,这个里面是一个smartcelleditor的子view,下面我们来看看这个smartcelleditor里面是什么内容?

  2 smartcelleditor

<usercontrol x:class="datagridcelldoubleclickdemo.views.smartcelleditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:conv="clr-namespace:datagridcelldoubleclickdemo.converters"
             xmlns:xceed="clr-namespace:xceed.wpf.toolkit;assembly=xceed.wpf.toolkit"
             mc:ignorable="d"
             d:designheight="450" d:designwidth="800">
    <usercontrol.resources>
        <conv:visibilityconverter x:key="visconverter" />
        <conv:timespanconverter x:key="timespanconverter" />
        <conv:numconverter x:key="numconverter" />
        <conv:boolconverter x:key="boolconverter" />
    </usercontrol.resources>
 
    <stackpanel margin="0">
        <!--textblock-->
        <textblock x:name="textblock"
                   background="{binding background}"
                   foreground="{binding foreground}"
                   text="{binding path=display,mode=oneway}" 
                   tooltip="{binding tooltip}"
                   fontweight="{binding fontweight}"
                   verticalalignment="stretch" 
                   visibility="{binding path=., converter={staticresource visconverter},converterparameter=textblock}"/>
 
 
        <!--editable combobox-->
        <combobox x:name="editablecombobox" itemssource="{binding smartcelldata.selections}" iseditable="true"  verticalalignment="stretch" verticalcontentalignment="center"
                  displaymemberpath="selectiondisplayname" text="{binding cellvalue,mode=twoway, updatesourcetrigger=propertychanged}"
                  visibility="{binding path=., converter={staticresource visconverter},converterparameter=editablecombobox}" />
 
        <!--readonly combobox-->
        <combobox x:name="readonlycombobox"  verticalalignment="center" verticalcontentalignment="stretch"  itemssource="{binding smartcelldata.selections}" iseditable="false"
                  displaymemberpath="selectiondisplayname" selectedvaluepath="controlname" selectedvalue="{binding path=cellvalue,mode=twoway,updatesourcetrigger=propertychanged}"
                  visibility="{binding path=., converter={staticresource visconverter},converterparameter=readonlycombobox}" />
 
 
        <!--text input textbox-->
        <textbox horizontalcontentalignment="left"  verticalalignment="stretch" verticalcontentalignment="center" text="{binding cellvalue}"
                 visibility="{binding path=., converter={staticresource visconverter},converterparameter=textbox}" textalignment="left" />
 
 
        <!--number input textbox-->
        <xceed:decimalupdown horizontalcontentalignment="left"  verticalalignment="stretch" verticalcontentalignment="center"  formatstring="g" value="{binding path=cellvalue,converter={staticresource numconverter},mode=twoway,updatesourcetrigger=propertychanged}"
                             increment="1" visibility="{binding path=., converter={staticresource visconverter},converterparameter=decimalupdown}" textalignment="left" />
 
 
        <!--checkbox-->
        <checkbox x:name="checkbox"  verticalalignment="stretch" verticalcontentalignment="center"  content="{binding tag}" ischecked="{binding path=cellvalue,converter={staticresource boolconverter},mode=twoway,updatesourcetrigger=propertychanged}"
                  visibility="{binding path=., converter={staticresource visconverter},converterparameter=checkbox}" />
 
 
        <!--timepicker-->
        <xceed:datetimeupdown format="custom" formatstring="hh:mm:ss"  verticalalignment="stretch" verticalcontentalignment="center"  value="{binding path=cellvalue,converter={staticresource timespanconverter},mode=twoway,updatesourcetrigger=propertychanged}"
                              visibility="{binding path=., converter={staticresource visconverter},converterparameter=timepicker}" cultureinfo="uk-ua" />
 
    </stackpanel>
</usercontrol>

  在这个里面我们在一个stackpanel中放置了匹配各种数据类型的template,并且每一个的visibility都是由visconverter这个自定义的converter来实现的,后面我们会分析这个converter里面的内容,这些代码的整体思想就是每次这个stackpanel里面的template都只有一个可以显示,其它的都是隐藏的,哪一个会显示是根据当前的数据类型决定的,每一个注释表示每一个类型的数据,比如我们如果定义的是bool类型,那么当我们双击单元格cell的时候会出现一个checkbox供我们编辑,所以这个里面我们需要根据我们定义的数据类型来扩展对应的模板,当我们双击单元格的时候就会显示这个模板从而进行编辑数据。

  3 mainwindowviewmodel

这个里面是定义的mainwindow对应的datacontext,在这里面我们会初始化绑定到mainwindow中datagridcontrol的itemssource,我们先来看看这个里面核心的代码并就其中的要点进行分析。

using datagridcelldoubleclickdemo.models;
using system;
using system.collections.objectmodel;
using system.windows;
 
namespace datagridcelldoubleclickdemo
{
    public class mainwindowviewmodel : notificationobject
    {
        public mainwindowviewmodel(xceed.wpf.datagrid.datagridcontrol datagridcontrol)
        {
            datagridcontrol = datagridcontrol;
            initrecipevariables();
        }
 
 
        #region properties
        private observablecollection<recipecontrolvariable> _recipevariables = new observablecollection<recipecontrolvariable>();
 
        public observablecollection<recipecontrolvariable> recipevariables
        {
            get { return _recipevariables; }
            set
            {
                if (value != _recipevariables)
                {
                    _recipevariables = value;
                    onpropertychanged(nameof(recipevariables));
                }
 
            }
        }
 
        public xceed.wpf.datagrid.datagridcontrol datagridcontrol { get; set; }
 
        #endregion
 
        #region private methods
        private void initrecipevariables()
        {
            _recipevariables.add(new recipecontrolvariable
            {
                controlname = "name",
                displayname = "name",
                stepvalues = new observablecollection<smartcellviewmodel>
                {
                    new smartcellviewmodel
                    {
                        cellvalue="step",
                        errorinfo=null,
                        smartcelldata=new recipevariableitem
                        {
                             controlname = "name",
                             displayname = "name",
                             variableeditortype=recipevariableeditortype.textinput
                        }
                    },
                    new smartcellviewmodel
                    {
                        cellvalue="step",
                        errorinfo=null,
                        smartcelldata=new recipevariableitem
                        {
                             controlname = "name",
                             displayname = "name",
                             variableeditortype=recipevariableeditortype.textinput
                        }
                    }
                }
 
            });
            _recipevariables.add(new recipecontrolvariable
            {
                controlname = "time",
                displayname = "process time(sec)",
                stepvalues = new observablecollection<smartcellviewmodel>
                {
                    new smartcellviewmodel
                    {
                        cellvalue="0",
                        errorinfo=null,
                        smartcelldata=new recipevariableitem
                        {
                             controlname = "time",
                             displayname = "process time(sec)",
                             variableeditortype=recipevariableeditortype.numinput
                        }
                    },
                    new smartcellviewmodel
                    {
                        cellvalue="0",
                        errorinfo=null,
                        smartcelldata=new recipevariableitem
                        {
                             controlname = "time",
                             displayname = "process time(sec)",
                             variableeditortype=recipevariableeditortype.numinput
                        }
                    }
                }
 
            });
            _recipevariables.add(new recipecontrolvariable
            {
                controlname = "frontchemical",
                displayname = "frontchemical",
                stepvalues = new observablecollection<smartcellviewmodel>
                {
                    new smartcellviewmodel
                    {
                        cellvalue="none",
                        errorinfo=null,
                        smartcelldata=new recipevariableitem
                        {
                             controlname = "frontchemical",
                             displayname = "frontchemical",
                             variableeditortype=recipevariableeditortype.readonlyselection,
                             selections=new observablecollection<selectionitem>
                             {
                                 new selectionitem
                                 {
                                     selectioncontrolname="chem1",
                                     selectiondisplayname="chem1",
                                 },
                                 new selectionitem
                                 {
                                     selectioncontrolname="n2",
                                     selectiondisplayname="n2",
                                 },
                                 new selectionitem
                                 {
                                     selectioncontrolname="cdiw",
                                     selectiondisplayname="cdiw",
                                 },
                                 new selectionitem
                                 {
                                     selectioncontrolname="",
                                     selectiondisplayname="none",
                                 }
                             }
                        }
                    },
                    new smartcellviewmodel
                    {
                        cellvalue="none",
                        errorinfo=null,
                        smartcelldata=new recipevariableitem
                        {
                             controlname = "frontchemical",
                             displayname = "frontchemical",
                             variableeditortype=recipevariableeditortype.readonlyselection,
                             selections=new observablecollection<selectionitem>
                             {
                                 new selectionitem
                                 {
                                     selectioncontrolname="chem1",
                                     selectiondisplayname="chem1",
                                 },
                                 new selectionitem
                                 {
                                     selectioncontrolname="n2",
                                     selectiondisplayname="n2",
                                 },
                                 new selectionitem
                                 {
                                     selectioncontrolname="cdiw",
                                     selectiondisplayname="cdiw",
                                 },
                                 new selectionitem
                                 {
                                     selectioncontrolname="",
                                     selectiondisplayname="none",
                                 }
                             }
                        }
                    }
                }
 
            });
            _recipevariables.add(new recipecontrolvariable
            {
                controlname = "nozzlebindingsetting",
                displayname = "nozzle scan",
                stepvalues = new observablecollection<smartcellviewmodel>
                {
                    new smartcellviewmodel
                    {
                        cellvalue="default",
                        errorinfo=null,
                        smartcelldata=new recipevariableitem
                        {
                             controlname = "nozzlebindingsetting",
                             displayname = "nozzle scan",
                             variableeditortype=recipevariableeditortype.textinput
                        }
                    },
                    new smartcellviewmodel
                    {
                        cellvalue="default",
                        errorinfo=null,
                        smartcelldata=new recipevariableitem
                        {
                             controlname = "nozzlebindingsetting",
                             displayname = "nozzle scan",
                             variableeditortype=recipevariableeditortype.textinput
                        }
                    }
                }
 
            });
        }
        #endregion
 
        /// <summary>
        /// reload datagrid content
        /// </summary>
        public void refreshdatagrid()
        {
            try
            {
                if (null == datagridcontrol) return;
                //generate columns in grid
                datagridcontrol.currentcolumn = null;
                if (datagridcontrol.columns.count > 0)
                    datagridcontrol.columns.clear();
 
                var template = (datatemplate)this.datagridcontrol.findresource("customtemplate");
                var rowtemplate = (datatemplate)this.datagridcontrol.findresource("rowheadtemplate");
 
                datagridcontrol.columns.add(new xceed.wpf.datagrid.column()
                {
                    width = 140,
                    title = "name",
                    fieldname = ".",
                    cellcontenttemplate = rowtemplate
                });
 
                var celleditor = datagridcontrol.defaultcelleditors[typeof(smartcellviewmodel)];
 
                for (int index = 0; index < recipevariables[0].stepvalues.count; index++)
                {
                    int width = 1;
                    for (int n = 0; n < recipevariables.count; n++)
                    {
                        string display = recipevariables[n].stepvalues[index].display;
                        if (!string.isnullorwhitespace(display))
                        {
                            int temp = display.length * 7;
                            width = math.max(temp, width);
                        }
                    }
                    width = (int)(width * 1.1);
                    width = math.max(width, 80);
                    datagridcontrol.columns.add(new xceed.wpf.datagrid.column()
                    {
 
                        title = string.format("step {0}", index + 1),
                        fieldname = string.format("stepvalues[{0}]", index),
                        cellcontenttemplate = template,
                        allowsort = false,
                        width = width,
                        maxwidth = width * 2,
                        celleditor = celleditor
                    });
                }
 
            }
            catch (exception ex)
            {
 
            }

  在这个里面我们重点分析下refreshdatagrid这个子函数,在我们的mainwindowviewmodel中我们定义的recipevariables是最终绑定到mainwindow中定义的datagridcontrol中的itemssource,是整个控件的数据源,由于我们这个datagird的第一列和后面的step列数据类型不同,所以我们的refreshdatagrid函数中增加column列的时候是分作两个部分,第一个部分是单独增加一列,后面的列是通过循环stepvalues这个集合来动态进行增加的,代码中我们定义了多少个stepvalue,那么后面就会有多少列,这个里面的重点是增加column的时候fieldname的赋值,这个是十分关键的,这个关系到能够让每一列获取到正确的数据源,例如第一列赋值filed= “.” 表示直接从当前绑定的数据源获取数据,另外后面的step列的每一个fieldname是动态进行赋值的,赋值语句是:fieldname = string.format("stepvalues[{0}]", index),这个里面index是一个动态值,这个是非常关键的一步,另外后面的step列由于需要通过双击进行编辑所以每一个column是需要赋值celleditor对象的,另外这个viewmodel中的datagridcontrol是通过构造函数进行赋值的,构造函数中的赋值就是mainwindow中定义的datagridcontrol对象,这个在阅读代码时需要特别注意。

  4 models

models主要是定义的数据集合,我们的代码中主要包括recipecontrolvariable和smartviewmodel这两个部分,这两个部分分别对应datagridcontrol的数据源以及双击进行编辑的smartcelleditor两个部分一一对应。

   更多代码方面的细节需要仔细去分析阅读源码,需要源码请点击此处进行下载

以上就是c# wpf中通过双击编辑datagrid中cell的示例(附源码)的详细内容,更多关于c# wpf双击编辑datagrid的资料请关注其它相关文章!