赞
踩
动态生成实现需要设置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 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对应的就是一个列标题的内容。
在阐述这个话题之前,我需要做一些铺垫。
testDateGrid.Columns.Count
还有一个方法,DataTable dt 是DataGrid的数据源,所以通过dt.Columns.Count获取到列数
之前一直找Rows这个属性,所以一直找不到如何获取行数,其实这里的Items就相当于Rows
testDateGrid.Items.Count
同理,DataTable dt 是DataGrid的数据源,所以通过 dt.Rows.Count可以获取到行数
- <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 cell
- grid.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;
- }
-
- }
- }
这里还有一个问题,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”
到这里,所以的准备工作就绪了,我得到了对象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添加了两个事件吗?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}]";
- });
过程中涉及动态的添加和删除,注意观察序号的变化:
需要要求有的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;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。