背景

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

demo预览

代码结构

  代码还是按照常规的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的资料请关注www.887551.com其它相关文章!