【问题标题】:Dynamically add a column in WPF DataGrid在 WPF DataGrid 中动态添加一列
【发布时间】:2021-11-14 20:11:57
【问题描述】:

我正在开发一个 MVVM WPF DataGrid 应用程序。我有一个 DataGrid 和一个多选 CheckBox 下拉菜单。每当我在菜单中选择一个选项时,我都想在 DataGrid 中添加一列。有什么办法吗?

ComboBox 项目模板背后的代码如下所示:

<ComboBox.ItemTemplate>
        <DataTemplate>
               <CheckBox
                     VerticalAlignment="Center"
                     ClickMode="Press"
                     Content="{Binding Position}"
                     IsChecked="{Binding IsSelected}" />
        </DataTemplate>
</ComboBox.ItemTemplate>

【问题讨论】:

  • 您可以使用 DataTable 作为数据源并对其进行操作。
  • 你似乎有一些严重错误的看法。首先,DataTable 是一个简单的类,它实现了一个普通的表数据结构——它以行和列的形式构造数据。它是数据的容器。数据用于模型(持久化,应用业务逻辑),视图模型(从模型中获取数据,应用转换逻辑,充当视图的数据源,通过数据绑定交换数据)和视图(呈现数据,应用表示逻辑,例如将数据包装/映射到 UI 容器)。数据无处不在。
  • DataTable 可以在任何地方使用,就像您也可以在任何地方使用其他数据结构(如集合)一样。 ObservableCollection 和 DataTable 一样是一种数据结构。您可以将 DataTable 类型的属性作为绑定源公开给视图。 DataGrid.ItemsSource 可以同时处理:IEnumerable 和 DataTable,或者更准确地说是 ICollectionView 和 DataView。我可以向您保证,将 DataTable 作为绑定源是完全可以的。没有限制属性必须是什么类型才能成为有效的绑定源。您甚至可以绑定到 XML 数据。
  • 如果将数据存储在 Model 中的 DataTable 结构中没有意义,则使用 View Model 将 Model 数据对象转换为 DataTable。然后将您的 DataGrid 直接绑定到此表。这是非常干净的 MVVM。
  • 不客气。很高兴能帮到你。

标签: c# wpf mvvm datagrid


【解决方案1】:

这是一个如何使用DataTable 作为DataGrid 的数据源的简单示例。

ViewModel.cs

class VeiwModel : INotifyPropertyChanged
{
  // Let this property set() raise the INotifyPropertyChanged.PropertyChanged event
  // if its value/instance will change after instantiation
  public DataTable TableData { get; set; }

  public ViewModel()
  {
    var changedTable = new DataTable();
    AddColumn<int>("ID", changedTable);
    AddColumn<string>("Username", changedTable);
    AddColumn<string>("Mail", changedTable);

    // Add column values in order of their columns
    AddRow(changedTable, 1, "Me", "me@mail.com");

    // Update the DataGrid with changes
    this.TableData = changedTable; 
  }

  // Appends a new column. 
  // Use 'columnIndex' parameter to assign an other column index than the last
  private void AddColumn<TData>(string columnName, DataTable targetDataTable, int columnIndex = -1)
  {
    DataColumn newColumn = new DataColumn(columnName, typeof(TData));

    targetDataTable.Columns.Add(newColumn);
    if (columnIndex > -1)
    {
      newColumn.SetOrdinal(columnIndex);
    }

    int newColumnIndex = targetDataTable.Columns.IndexOf(newColumn);

    // Initialize existing rows with default value for the new column
    foreach (DataRow row in targetDataTable.Rows)
    {
      row[newColumnIndex] = default(TData);
    }
  }

  private void AddRow(DataTable targetDataTable, params object[] columnValues)
  {
    DataRow rowModelWithCurrentColumns = targetDataTable.NewRow();
    targetDataTable.Rows.Add(rowModelWithCurrentColumns);

    for (int columnIndex = 0; columnIndex < targetDataTable.Columns.Count; columnIndex++)
    {
      rowModelWithCurrentColumns[columnIndex] = columnValues[columnIndex];
    }
  }
}

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContex>

  <DataGrid ItemsSource="{Binding TableData}" />
</Window>

【讨论】:

    【解决方案2】:

    我最近在 C# uwp 应用程序中遇到了类似的问题。这对我有用。


    首先,我创建了一个列表来跟踪所有选中的复选框:

    private List<CheckBox> checkedCheckboxes = new List<CheckBox>();
    

    然后,我创建了复选框并将它们链接到相同的事件,就像这样(您可能已经有这部分的代码):

    foreach (foo blah in random)
            {
                var checkbox = new CheckBox(); //creating new checkbox
                checkbox.Content = blah.name; //setting content
                checkbox.Name = $"chk_{blah.name}"; //naming it
                checkbox.Tag = "blah"; //adding the tag
    
                checkbox.Checked += CheckBox_Checked; //adding the checked event
                checkbox.Unchecked += CheckBox_Unchecked; //adding the unchecked event
    
                ClassCheckboxes.Add(checkbox);
            }
    

    对于“CheckBox_Checked”事件,我执行了以下操作:

    private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
            checkedCheckboxes.Add((CheckBox)sender);
            //Here you can put some code to update your datagrid
        }
    

    对于“CheckBox_Unchecked”事件,我做了以下操作:

    private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            checkedCheckboxes.Remove((CheckBox)sender);
            //Here you can put some code to update your datagrid
        }
    

    要向数据网格添加新列,您可以参考answer。那里有一些不错的策略可能对您有用。为方便起见,这是投票最高的:

    DataGridTextColumn textColumn = new DataGridTextColumn(); 
    textColumn.Header = "First Name"; 
    textColumn.Binding = new Binding("FirstName"); 
    dataGrid.Columns.Add(textColumn);
    

    对不起,如果我在这里做错了什么,这是我第一次发布答案,所以请放轻松:)

    【讨论】:

    • 在 WPF 中,几乎所有修改视图而不是数据的解决方案都是糟糕的解决方案——尤其是在 MVVM 上下文中。发现自己在 C# 中创建控件和数据绑定应该会敲响一些警钟。干净的解决方案是修改数据源。如有必要,通过DataGrid.AutoGeneratingColumn 事件手动调整专用视图列:Microsoft Docs: How to: Customize Auto-Generated Columns in the DataGrid Control
    • @BionicCode 不幸的是,我无法使用自动生成的列,因为我将 DataGrid 绑定到类型 A 的 ObservableCollection 的 CollectionView(例如),但我不想添加列对于该类的每个属性。我正在手动绑定要为其创建列的属性。
    • 这就是上述事件的目的。您可以分配列模板或取消要忽略的列的生成。我之前评论中提供的链接向您展示了如何做到这一点。要添加列,您只需将一个新列添加到您应该用作数据源的 DataTable。
    猜你喜欢
    • 1970-01-01
    • 2011-03-05
    • 2011-08-26
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 2016-05-12
    • 1970-01-01
    • 2014-12-15
    相关资源
    最近更新 更多