如果您打算动态修改DataGrid 的列,则应选择DataTable 作为网格的数据模型。
请注意,添加/删除列需要更新每一行以便为新列提供值,这可能会对性能产生显着影响,具体取决于表模型的大小(尤其是行数)。
在这种情况下,您应该实施数据虚拟化。您将跟踪视口,即视图中的当前项目,并根据需要更新待处理的行数据更改。
示例
这是一个非常基本的示例,展示了如何在运行时借助 DataTable class 对 DataGrid 的列进行修改。
该示例使用了数据绑定(极大地简化了逻辑)和命令:
此外,该示例依赖于 DataGrid 的列自动生成(此功能默认启用) - 否则解决方案的复杂性当然会增加。如果没有自动列生成,您将不得不向视图添加(简单)逻辑。
MainViewModel.cs
此类使用RelayCommand。您可以在 Microsoft Docs: Relaying Command Logic 找到示例实现。
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.TableData = new DataTable();
AddColumn<int>("ID");
AddColumn<string>("Username");
AddColumn<string>("Mail");
AddRow(1, "Me", "me@mail.com");
}
private void AddColumn<TColumnData>(string columnName, int columnIndex = -1)
{
var newColumn = new DataColumn(columnName, typeof(TColumnData));
this.TableData.Columns.Add(newColumn);
if (columnIndex > -1)
{
newColumn.SetOrdinal(columnIndex);
}
int newColumnIndex = this.TableData.Columns.IndexOf(newColumn);
// Initialize existing rows with a default value for the new column.
// In this example the default value of the column's data type is used.
foreach (DataRow row in this.TableData.Rows)
{
row[newColumnIndex] = default(TColumnData);
}
}
// Ensure that column values are ordered by their column's index
private void AddRow(params object[] columnValues)
{
DataRow rowModelWithCurrentColumns = this.TableData.NewRow();
this.TableData.Rows.Add(rowModelWithCurrentColumns);
for (int columnIndex = 0; columnIndex < this.TableData.Columns.Count; columnIndex++)
{
rowModelWithCurrentColumns[columnIndex] = columnValues[columnIndex];
}
}
// Force the binding target (DataGrid) to create new column templates
// by changing the binding source instance
private void OnDataTableColumnsChanged() => this.TableData = this.TableData.DefaultView.ToTable();
// Add columns dynamically via an ICommand
public ICommand AddColumnCommand => new RelayCommand(commandParameter =>
{
AddColumn<DateTime>($"Timestamp {this.TableData.Columns.Count}");
OnDataTableColumnsChanged();
});
public ICommand AddRowCommand => new RelayCommand(commandParameter => AddRow(2, "You", "you@mail.com", DateTime.Now));
private DataTable tableData;
public DataTable TableData
{
get => this.tableData;
set
{
this.tableData = value;
OnPropertyChanged();
}
}
/*** Implementation of INotifyPropertyChanged ***/
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
=> this.PropertyChanged?.Invoke(new PropertyChangedEventArgs(propertyName));
}
备注:
或者,成员AddColumn 和AddRow 可以实现为扩展方法。
您可以从任何来电者那里致电AddColumn 和AddRow - 不仅来自ICommand。只需确保在添加/删除所有列后调用 OnDataTableColumnsChanged 将列更改提升到视图即可。
在视图中,行不需要创建任何视图模板,因此这些行数据更改立即可见。行的单元格如何呈现在列的模板中定义。
另一方面,视图列需要一个视图模板来告诉DataGrid 如何呈现列及其单元格。调用 OnDataTableColumnsChanged 会强制绑定目标(DataGrid)为新列生成列模板。
如果DataTable 的数据源实际上是一个数据库,则每次查询后都会创建一个DataTable 的新实例,这会使对OnDataTableColumnsChanged 的调用变得多余。
MainWindow.xaml
<Window>
<Window.DataContext>
<!-- Don't forget to qualify the type with the defined XAML namespace (xmlns) -->
<MainViewModel />
</Window.DataContext>
<StackPanel>
<Button Content="Add Column" Command="{Binding AddColumnCommand}" />
<Button Content="Add Row" Command="{Binding AddRowCommand}" />
<DataGrid ItemsSource="{Binding TableData}" />
</StackPanel>
</Window>