【wpf】DataGrid的使用
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的使用相关推荐
- 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 ...
- WPF DataGrid 如何将被选中行带到视野中
WPF DataGrid 如何将被选中行带到视野中 目录 前言 准备工作 方法一 方法二 总结 独立观察员 2021 年 12 月 11 日 前言 在 WPF 开发中,显示表格一般使用 DataGri ...
- WPF DataGrid 通过自定义表头模拟首行固定
WPF DataGrid 通过自定义表头模拟首行固定 独立观察员 2021 年 9 月 25 日 最近工作中要在 WPF 中做个表格,自然首选就是 DataGrid 控件了.问题是,UI 设计的表格是 ...
- WPF DataGrid:解决排序、ScrollIntoView、刷新和焦点问题
目录 介绍 第一种方法:记住选定的行,刷新DataGrid,再次选择行 最终方法:使用OneWay绑定,避免调用Refresh() 改进1:使ScrollIntoView()起作用 改进2:将选定的行 ...
- 使用绑定进行WPF DataGrid格式化的指南
目录 介绍 WPF DataGrid结构 WPF绑定基础 使用的业务数据 将DataGrid与业务数据连接 DataGrid格式 格式化列 格式化完整行 根据显示的值格式化单元格 根据业务逻辑数据格式 ...
- WPF DataGrid 和LINQ to SQL示例程序之一 (提供源代码下载)
WPF DataGrid 和LINQ to SQL示例程序之一 (提供源代码下载) WPF DataGrid 系列示例程序,由浅入深逐步介绍如何在WPF 应用程序中使用新的DataGrid 控件.本篇 ...
- WPF DataGrid 主从表 数据绑定方式
昨天在网上搜了一下午没有看到一个关于WPF DataGrid主从表数据绑定的示例,但是我坚信这个简单的功能肯定是支持的,经研究问题解决. 现把相关方法共享下,给现在还在郁闷的兄弟们一点参考.重点在于定 ...
- wpf DataGrid主从表,DataGrid嵌套DataGrid主从结构rowdetailtemplate实现,绑定DataTable数据源,使用Visual Studio 2017
wpf DataGrid主从表,DataGrid嵌套DataGrid主从结构rowdetailtemplate实现,绑定DataTable数据源,使用Visual Studio 2017 . 子表绑定 ...
- wpf datagrid设置右键菜单打开时选中项的背景色
原文:wpf datagrid设置右键菜单打开时选中项的背景色 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/huangli321456/artic ...
- C# WPF DataGrid控件的详细介绍和推荐一些样式设计
前面介绍过使用DataGrid简单绑定一个数据模型,接着介绍DataGrid的一些详细操作. 参考:C# WPF DataGrid的使用 定制DataGrid控件基本外观属性 RowBackgroun ...
最新文章
- Android开发之自定义TabHost文字及背景(源代码分享)
- Linux的DNS配置2-主从服务器
- UVA11624大火蔓延的迷宫
- 币对交易所_比特币向1万4大涨,OK交易所的比特币为什么反而贬值7折?
- python for in语句 index_使用for循环查找句子中的单词及其索引位置
- Ubuntu 平滑升级到PHP7
- 基于bitbucket中央库的Git操作
- Python排序算法[二]:测试数据的迷雾散去
- Oracle单实例数据库迁移到Oracle RAC 环境之(3)--主备库Switchover
- relocation R_X86_64_PC32 against symbol can not be used when making a shared object recompile with
- vscode编写php好用吗,vscode可以编写php吗
- Word2016目录自动生成+页码从目录页后面显示
- IIC上拉电阻的注意事项
- 判断是否是ie浏览器 前端js_JavaScript判断IE浏览器版本IE6,IE7,IE8
- 突破同一账号不能同时在不同电脑登录限制程序软件(多电脑端登录器多开软件)
- 台式机就是指什么的计算机,什么是台式机操作系统
- 使用Enterprise Architect设计数据库-赋操作截图
- 新鲜新奇事物_尝试新鲜事物的唯美句子
- 30. 攻城狮的自我营销
- 查看linux系统是centos还是ubuntu的方法
热门文章
- python mooc水仙花_智慧职教mooc的APP计算机应用基础(西安航空职业技术学院)答案公众号...
- SpringBoot项目中Druid自动登录
- android 控制台命令大全,发送模拟器控制台命令
- vue TypeError: Cannot read properties of null (reading 'insertBefore')
- 研究了34个跨境独立站 总结出了20条成功经验
- postgreSQL 的generate_series函数
- 趣味点名软件_大学老师“花样点名”,为保证出勤率也是拼了!这回看谁还敢翘课...
- mysql一个表能写2个auto_in_MySQL基础(二)操作表记录
- portswigger之SQL注入实验室笔记
- html需要电脑什么配置,AutoCAD2018对电脑配置要求 需要什么配置的电脑