DataGrid动态生成

简单方式

动态生成实现需要设置AutoGenerateColumns="True",

<DataGrid Name="dataGrid" AutoGenerateColumns="True" />

后台dataGrid.ItemsSource = infoList;

infoList,就是一个类似Json的数组结构数据:

[{"columnChName": "孔宽1","nominalDim": 17.28,"tolMax": 0.02,"tolMin": 0.02,"usl": 17.3,"lsl": 17.26},{"columnChName": "孔从中心偏移3","nominalDim": 17.28,"tolMax": 0.02,"tolMin": 0.02,"usl": 17.3,"lsl": 17.26},{"columnChName": "孔从中心偏移6","nominalDim": 17.28,"tolMax": 0.02,"tolMin": 0.02,"usl": 17.3,"lsl": 17.26},{"columnChName": "孔从中心偏移5","nominalDim": 13.84,"tolMax": 0.05,"tolMin": 0.05,"usl": 13.89,"lsl": 13.79},{"columnChName": "孔从中心偏移2","nominalDim": 13.84,"tolMax": 0.05,"tolMin": 0.05,"usl": 13.89,"lsl": 13.79},{"columnChName": "孔长3","nominalDim": 52.94,"tolMax": 0.02,"tolMin": 0.02,"usl": 52.96,"lsl": 52.92},{"columnChName": "孔从中心偏移7","nominalDim": 49.75,"tolMax": 0.02,"tolMin": 0.02,"usl": 49.77,"lsl": 49.73}]

固定不变的key会自动变成表格的列标题。

那界面直接就会出现这样的结果,十分方便:

如果不想动态生成,要自己定义标题头,

首先需要 AutoGenerateColumns="False" ,然后:

<Window><Grid><DataGrid ItemsSource="{Binding Customers}"><DataGrid.Columns><DataGridTextColumn Header="Name" Binding="{Binding Name}" /><DataGridTextColumn Header="Age" Binding="{Binding Age}" /><DataGridTextColumn Header="Address" Binding="{Binding Address}" /></DataGrid.Columns></DataGrid></Grid>
</Window>

这样也是可以的~~~~

通用的方式

这样的简单是简单,但是受到的数据格式的限制,而且无法对表格进行更精细的设置,下面介绍一下更通用的做法。

数据源DataTable

首先我们新建一个DataTable ,DataTable dt = new DataTable(); 让 dt 作为我们DataGrid的数据源,好处是DataTable作为数据源之后,数据源的变换可以直接通知到界面(类似ObservableCollection)。

数据关联

将 DataGrid 的数据源与 DataTable 关联起来:

datagrid.ItemsSource = dt.AsDataView();

标题头部分构造(示例代码)

foreach (var item in _data_model.result.titleColumn)
{dt.Columns.Add(item.columnChName);
}

内容构造(示例代码)

添加一行的伪代码:

//添加一行的伪代码
DataRow row = dt.NewRow();
foreach (var cd in (JsonObject)r.contentData)
{try{row[new_key] = cd.Value.ToString(); }catch (ArgumentException ex){logger.Info("ArgumentException:" + ex.Message);}}
dt.Rows.Add(row);

我们需要注意这里的new_key,必须是标题头中包含的内容,不然是会报ArgumentException的异常。new_key对应的就是一个列标题的内容。

如何动态的配置每个单元格

在阐述这个话题之前,我需要做一些铺垫。

1 DataGrid如何获取当前列数

testDateGrid.Columns.Count

还有一个方法,DataTable dt 是DataGrid的数据源,所以通过dt.Columns.Count获取到列数

2 DataGrid如何获取当前行数

之前一直找Rows这个属性,所以一直找不到如何获取行数,其实这里的Items就相当于Rows

testDateGrid.Items.Count

同理,DataTable dt 是DataGrid的数据源,所以通过 dt.Rows.Count可以获取到行数

3 基于prism的事件时如何获取sender对象

<DataGrid  x:Name="testDateGrid" CanUserSortColumns="False"  ><i:Interaction.Triggers><i:EventTrigger EventName="LoadingRow"><prism:InvokeCommandAction Command="{Binding EventLoadingRow}"/></i:EventTrigger><i:EventTrigger EventName="UnloadingRow"><prism:InvokeCommandAction Command="{Binding EventUnloadingRow}"/></i:EventTrigger></i:Interaction.Triggers>
</DataGrid>

这里为了实现MVVM我用prism的方式添加了两个事件LoadingRow,UnloadingRow。如果是普通的事件添加是自动传入sender的,也就是DataGrid对象本身。而通过prism的方式,只会传参数e。

后台代码如下,这里只能传参数DataGridRowEventArgs:

public DelegateCommand<DataGridRowEventArgs> EventLoadingRow { get; set; }

不过我们有办法通过e来获取sender,不够稍微有点麻烦:


testDateGrid = Tool.GetChildObjectWithName<DataGrid>((DependencyObject)e.OriginalSource, "testDateGrid");

"testDateGrid"这个是我在前台xaml中配置的名字,然后通过GetChildObjectWithName这个方法获取到DateGrid对象。方法的具体实现,我放到最后的 附录 中吧。

有了DateGrid对象,如何获取DateGrid的每一行,以及每一行中的每个单元格呢?这里我们为DataGrid添加两个扩展方法,分别用来获取DataGridRow和DataGridCell:

namespace BaseLibrary.MyExtensions
{public static class DataGridExtensions{public static DataGridCell GetCell(this DataGrid grid, DataGridRow row, int columnIndex = 0){if (row == null) return null;var presenter = Tools.Tool.GetChildObjectFirst<DataGridCellsPresenter>(row);if (presenter == null) return null;var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);if (cell != null) return cell;// now try to bring into view and retreive the cellgrid.ScrollIntoView(row, grid.Columns[columnIndex]);cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);return cell;}/// <summary>/// 获取DataGrid的行/// </summary>/// <param name="dataGrid">DataGrid控件</param>/// <param name="rowIndex">DataGrid行号</param>/// <returns>指定的行号</returns>public static DataGridRow GetRow(this DataGrid dataGrid, int rowIndex){DataGridRow rowContainer = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex);if (rowContainer == null){dataGrid.UpdateLayout();dataGrid.ScrollIntoView(dataGrid.Items[rowIndex]);rowContainer = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex);}return rowContainer;}}
}

4 事件LoadingRow

这里还有一个问题,testDateGrid中的Row我们是动态添加的,而且是通过变更数据源DataTable实现的,那我们需要一个事件:每添加一行之后就会触发。但是DateGrid并么有提供这样一个事件,只提供了LoadingRow,它在添加一行之前触发。这样的话,就没办法在事件中获取到当前新增的行对象。那边我们可以利用prism中的事件,利用LoadingRow构建 “LoadedRow” 事件

我接下来的操作会借助prism的事件,当然你可以替换成其他操作。prism的事件可参见博客:【Prism系列】Prism事件聚合器_code bean的博客-CSDN博客_prism 事件聚合器

namespace BaseLibrary.MyEvent
{public class WaitEvent : PubSubEvent{}
}//行增加事件
EventLoadingRow = new DelegateCommand<DataGridRowEventArgs>((e) => {_eventAggregator.GetEvent<WaitEvent>().Publish();
});//确保在行增加之后发生:
_eventAggregator.GetEvent<WaitEvent>().Subscribe(() => {//------省略代码}, ThreadOption.UIThread);

思路就是,在LoadingRow再发布一个事件,然后订阅这个事件的时候,使用ThreadOption.UIThread,这样就可以将 LoadingRow 变为 “LoadedRow”

5 最后的设置

到这里,所以的准备工作就绪了,我得到了对象DateGrid testDateGrid,于是通过扩展方法GetRow就能得到行对象:

//获取每一行
var rowContainer = testDateGrid.GetRow(dtTest.Rows.Count - 1); 

有了行对象,我们就能得到具体的cell:

// 根据每一行获取每一列
DataGridCell cell = testDateGrid.GetCell(rowContainer, i);

有了cell对象,我们就可以对每个cell进行设置:

比如,设置某些cell不可编辑:cell.IsEnabled = false;

比如,设置某些cell背景或者前景颜色:cell.Foreground = new SolidColorBrush(Colors.Green);

还有一点需要注意的是:这些设置会在表格排序后失效,所以我们一般需要禁用DateGrid的排序功能(将属性  CanUserSortColumns="False")。

DateGrid添加行自增序号

动态添加行的时候,如何给DateGrid添加行自增序号呢?这里我介绍一种简单的方法。

记得之前,我给DateGrid添加了两个事件吗?LoadingRow 和 UnloadingRow。

LoadingRow用于行数增加时,序号的递增。UnloadingRow用于行被删除时序号的重新排列:

//用于散出后的重新排序
EventUnloadingRow = new DelegateCommand<DataGridRowEventArgs>((e) => {if (testDateGrid.Items != null){for (int i = 0; i < testDateGrid.Items.Count; i++){try{DataGridRow row = testDateGrid.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;if (row != null){row.Header = $"[{ (i + 1)}]" ;}}catch { }}}
});//行增加事件
EventLoadingRow = new DelegateCommand<DataGridRowEventArgs>((e) => {e.Row.Header = $"[{e.Row.GetIndex() + 1}]";
});

效果展示

过程中涉及动态的添加和删除,注意观察序号的变化:

2022年11月2日,内容更新

如何实现Cell单元格的不可以编辑

需要要求有的cell可编辑,有个不可以,当获取到cell对象后,可直接这种cell.IsEnabled = false

这样整个cell确实不可以编辑了,但是这样的话,整个cell不可用,会有一些副作用。

比如,即使你设置了cell.ToolTip,也无法显示ToolTip提示了

你可能会想到isReadOnly属性,但整个数据本身就是只读的不可设置!(我TM也奇了怪了)

最后,发现有个属性能满足我的需求就是Focusable(cell.Focusable = false;)

既能实现不可编辑,又不妨碍ToolTip显示。

2022年12月30日,内容更新:

编辑单元格时获取单元格的行和列

CellEditEnding事件中,本来这样可以的:

var d = sender as DataGrid;
int row_num = d.SelectedIndex;
int colum_num = e.Column.DisplayIndex;

但是,当DataGrid支持多选之后,SelectedIndex就永远为-1了(testDateGrid.SelectionMode = DataGridSelectionMode.Extended; //支持单元格多选)

找了很多属性,发现就是没有此时行对应的属性!!!!!

结果只能这样了:

就是新建行的时候把序号保存到row的Tag属性中!

最后就变成在CellEditEnding事件中:

int row_num = (int)e.Row.Tag;
int colum_num = e.Column.DisplayIndex;

附录

public class Tool{/// <summary>/// 根据类型查找子元素/// 调用形式:  List<StackPanel> initToolBarWeChatUserSp = GetChildObjects<StackPanel>(name, typeof(StackPanel));/// </summary>/// <typeparam name="T">查找类型</typeparam>/// <param name="obj">查询对象</param>/// <returns></returns>static public List<T> GetChildObjects<T>(DependencyObject obj) where T : FrameworkElement{DependencyObject child = null;List<T> childList = new List<T>();Type typename = typeof(T);for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++){child = VisualTreeHelper.GetChild(obj, i);if (child is T && (((T)child).GetType() == typename)){childList.Add((T)child);}childList.AddRange(GetChildObjects<T>(child));}return childList;}static public T GetChildObjectFirst<T>(DependencyObject obj) where T : FrameworkElement{List<T> childList = new List<T>();childList = GetChildObjects<T>(obj);if (childList.Count > 0){return childList[0];}else{return null;}}/// <summary>/// 获取父可视对象中第一个指定类型的子可视对象/// </summary>/// <typeparam name="T">可视对象类型</typeparam>/// <param name="parent">父可视对象</param>/// <returns>第一个指定类型的子可视对象</returns>public static T GetVisualChild<T>(Visual parent) where T : Visual{T child = default(T);int numVisuals = VisualTreeHelper.GetChildrenCount(parent);for (int i = 0; i < numVisuals; i++){Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);child = v as T;if (child == null){child = GetVisualChild<T>(v);}if (child != null){break;}}return child;}static public T GetChildObjectWithName<T>(DependencyObject obj, string name) where T : FrameworkElement{List<T> childList = new List<T>();childList = GetChildObjects<T>(obj);foreach (var item in childList){if (item.Name == name){ return item;}}return null;}}

【wpf】DataGrid的使用相关推荐

  1. WPF Datagrid with some read-only rows - Stack Overflow

    原文:WPF Datagrid with some read-only rows - Stack Overflow up vote 21 down vote accepted I had the sa ...

  2. WPF DataGrid 如何将被选中行带到视野中

    WPF DataGrid 如何将被选中行带到视野中 目录 前言 准备工作 方法一 方法二 总结 独立观察员 2021 年 12 月 11 日 前言 在 WPF 开发中,显示表格一般使用 DataGri ...

  3. WPF DataGrid 通过自定义表头模拟首行固定

    WPF DataGrid 通过自定义表头模拟首行固定 独立观察员 2021 年 9 月 25 日 最近工作中要在 WPF 中做个表格,自然首选就是 DataGrid 控件了.问题是,UI 设计的表格是 ...

  4. WPF DataGrid:解决排序、ScrollIntoView、刷新和焦点问题

    目录 介绍 第一种方法:记住选定的行,刷新DataGrid,再次选择行 最终方法:使用OneWay绑定,避免调用Refresh() 改进1:使ScrollIntoView()起作用 改进2:将选定的行 ...

  5. 使用绑定进行WPF DataGrid格式化的指南

    目录 介绍 WPF DataGrid结构 WPF绑定基础 使用的业务数据 将DataGrid与业务数据连接 DataGrid格式 格式化列 格式化完整行 根据显示的值格式化单元格 根据业务逻辑数据格式 ...

  6. WPF DataGrid 和LINQ to SQL示例程序之一 (提供源代码下载)

    WPF DataGrid 和LINQ to SQL示例程序之一 (提供源代码下载) WPF DataGrid 系列示例程序,由浅入深逐步介绍如何在WPF 应用程序中使用新的DataGrid 控件.本篇 ...

  7. WPF DataGrid 主从表 数据绑定方式

    昨天在网上搜了一下午没有看到一个关于WPF DataGrid主从表数据绑定的示例,但是我坚信这个简单的功能肯定是支持的,经研究问题解决. 现把相关方法共享下,给现在还在郁闷的兄弟们一点参考.重点在于定 ...

  8. wpf DataGrid主从表,DataGrid嵌套DataGrid主从结构rowdetailtemplate实现,绑定DataTable数据源,使用Visual Studio 2017

    wpf DataGrid主从表,DataGrid嵌套DataGrid主从结构rowdetailtemplate实现,绑定DataTable数据源,使用Visual Studio 2017 . 子表绑定 ...

  9. wpf datagrid设置右键菜单打开时选中项的背景色

    原文:wpf datagrid设置右键菜单打开时选中项的背景色 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/huangli321456/artic ...

  10. C# WPF DataGrid控件的详细介绍和推荐一些样式设计

    前面介绍过使用DataGrid简单绑定一个数据模型,接着介绍DataGrid的一些详细操作. 参考:C# WPF DataGrid的使用 定制DataGrid控件基本外观属性 RowBackgroun ...

最新文章

  1. Android开发之自定义TabHost文字及背景(源代码分享)
  2. Linux的DNS配置2-主从服务器
  3. UVA11624大火蔓延的迷宫
  4. 币对交易所_比特币向1万4大涨,OK交易所的比特币为什么反而贬值7折?
  5. python for in语句 index_使用for循环查找句子中的单词及其索引位置
  6. Ubuntu 平滑升级到PHP7
  7. 基于bitbucket中央库的Git操作
  8. Python排序算法[二]:测试数据的迷雾散去
  9. Oracle单实例数据库迁移到Oracle RAC 环境之(3)--主备库Switchover
  10. relocation R_X86_64_PC32 against symbol can not be used when making a shared object recompile with
  11. vscode编写php好用吗,vscode可以编写php吗
  12. Word2016目录自动生成+页码从目录页后面显示
  13. IIC上拉电阻的注意事项
  14. 判断是否是ie浏览器 前端js_JavaScript判断IE浏览器版本IE6,IE7,IE8
  15. 突破同一账号不能同时在不同电脑登录限制程序软件(多电脑端登录器多开软件)
  16. 台式机就是指什么的计算机,什么是台式机操作系统
  17. 使用Enterprise Architect设计数据库-赋操作截图
  18. 新鲜新奇事物_尝试新鲜事物的唯美句子
  19. 30. 攻城狮的自我营销
  20. 查看linux系统是centos还是ubuntu的方法

热门文章

  1. python mooc水仙花_智慧职教mooc的APP计算机应用基础(西安航空职业技术学院)答案公众号...
  2. SpringBoot项目中Druid自动登录
  3. android 控制台命令大全,发送模拟器控制台命令
  4. vue TypeError: Cannot read properties of null (reading 'insertBefore')
  5. 研究了34个跨境独立站 总结出了20条成功经验
  6. postgreSQL 的generate_series函数
  7. 趣味点名软件_大学老师“花样点名”,为保证出勤率也是拼了!这回看谁还敢翘课...
  8. mysql一个表能写2个auto_in_MySQL基础(二)操作表记录
  9. portswigger之SQL注入实验室笔记
  10. html需要电脑什么配置,AutoCAD2018对电脑配置要求 需要什么配置的电脑