c# WPF中通过双击编辑DataGrid中Cell的示例(附源码)
背景
在很多的时候我们需要编辑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的资料请关注猪先飞其它相关文章!
相关文章
- 我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
- 这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
- 这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
- 这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
- 本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
- 本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25- 这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
- 这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
- 最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
- 本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
- 轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
- 这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
- 这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
- 这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
- 下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
- 这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25