【问题标题】:How to Bind ObservableCollection Class to DataGrin in WPF如何在 WPF 中将 ObservableCollection 类绑定到 DataGrin
【发布时间】:2013-04-18 21:28:23
【问题描述】:

所有,我正在创建一个在运行时“绑定”到DataGrid 的数据集。我提取了一些用于构建继承自ObservableCollection<T> 的类的数据。有问题的课程是

public class ResourceData : ObservableCollection<ResourceRow>
{
   public ResourceData(List<ResourceRow> resourceRowList) : base()
   {
      foreach (ResourceRow row in resourceRowList)
         Add(row);
   }
}

public class ResourceRow
{
   private string keyIndex;
   private string fileName;
   private string resourceName;
   private List<string> resourceStringList;

   public string KeyIndex
   {
      get { return keyIndex; }
      set { keyIndex = value; }
   }

   public string FileName
   {
      get { return fileName; }
      set { fileName = value; }
   }

   public string ResourceName
   {
      get { return resourceName; }
      set { resourceName = value; }
   }

   public List<string> ResourceStringList
   {
      get { return resourceStringList; }
      set { resourceStringList = value; }
   }
}

我从一个名为BuildDataGrid()的方法返回一个ResourceData对象,定义为

private ResourceData BuildDataGrid(Dictionary<string, string> cultureDict)
{
    // ...
}

然后我将DataGridItemSource 设置为此ResourceData 对象通过

dataGrid.ItemSource = BuiltDataGrid(cultureDictionary);

但是,这并没有正确扩展 ResourceStringList 内的 ResourceRow。我得到展示:

我的问题是:如何修改我的 ResourceRow 类以允许 DataGrid 自动读取和显示 ResourceData 对象的内容? 我想显示每个项目ResourceStringList 在单独的列中。

感谢您的宝贵时间。

【问题讨论】:

  • 每个resourceStringList 对象是否包含相同数量的元素?如果是这样,从 ResourceRow 对象的资源字符串列表中枚举要添加的列可能是最简单的。如果集合中的字符串数量因 ResourceRow 对象而异,问题就会变得更加困难,因为您将混合使用设计时列和运行时列,这需要代码隐藏来混合在一起
  • 对于每次加载,是的,resourceStringList 对象将包含相同数量的项目。我在这里使用 List 以避免在使用数组时需要初始化...
  • 所以您想将ResourceStringList 显示为额外的列,还是仅将它们显示为单元格内的可编辑列表?
  • 作为额外的列。我的计划是将标准字符串作为列,但由于某些数据在运行时是未知的,所以我将这些东西放在一个列表中......

标签: c# wpf datagrid itemssource


【解决方案1】:

这是我的解决方案 - 我更改了控件,而不是 ResourceRow,但我认为它可以实现您想要的。

我们只需在 xaml 中创建一个带有设计时列的 DataGrid,然后在控件的构造函数中动态添加运行时列。有几件事要记住——我们假设 ResourceData 的第一行是所有行的好模型,因为这是我们用来确定要添加到构造函数中的数据网格的列的模型。其次,由于 ResourceRow 没有实现 INotifyPropertyChanged,我们不会获得来自 ResourceStringList 的列的更新值 - 但可以轻松更改。

代码隐藏:

namespace WpfApplication
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            ResourceData data = GetData();
            _dataGrid.ItemsSource = data;

            for (int i = 0; i < data[0].ResourceStringList.Count; i++)
            {
                DataGridTextColumn column = new DataGridTextColumn();
                column.Binding = new Binding(string.Format("ResourceStringList[{0}]", i));
                column.Header = string.Format("dynamic column {0}", i);
                _dataGrid.Columns.Add(column);
            }
        }

        public ResourceData GetData()
        {
            List<ResourceRow> rows = new List<ResourceRow>();

            for (int i = 0; i < 5; i++)
            {
                rows.Add(new ResourceRow() { KeyIndex = i.ToString(), FileName = string.Format("File {0}", i), ResourceName = string.Format("Name {0}", i), ResourceStringList = new List<string>() { "first", "second", "third" } });
            }
            return new ResourceData(rows);
        }
    }

    public class ResourceData : ObservableCollection<ResourceRow>
    {
        public ResourceData(List<ResourceRow> resourceRowList)
            : base()
        {
            foreach (ResourceRow row in resourceRowList)
                Add(row);
        }
    }

    public class ResourceRow
    {
        private string keyIndex;
        private string fileName;
        private string resourceName;
        private List<string> resourceStringList;

        public string KeyIndex
        {
            get { return keyIndex; }
            set { keyIndex = value; }
        }

        public string FileName
        {
            get { return fileName; }
            set { fileName = value; }
        }

        public string ResourceName
        {
            get { return resourceName; }
            set { resourceName = value; }
        }

        public List<string> ResourceStringList
        {
            get { return resourceStringList; }
            set { resourceStringList = value; }
        }
    }
}

xaml:

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="_dataGrid" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding KeyIndex}" Header="Key Index"/>
                <DataGridTextColumn Binding="{Binding FileName}" Header="File Name"/>
                <DataGridTextColumn Binding="{Binding ResourceName}" Header="Resource Name"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

【讨论】:

  • 太棒了!非常感谢你花时间陪伴。我想在业余时间学习 WPF(下班后和周末,这让我很生气!)。不过我很困惑,XAML 中列的绑定引用了“KeyIndex”,它怎么知道这里的“KeyIndex”是什么?
  • 很高兴我能帮上忙!如果这有效地解决了您的问题,请将其标记为已接受的解决方案! WPF 在运行时使用大量反射来解析与底层属性的绑定。在 xaml 中,您可以将 KeyIndex 视为一个魔术词,在创建控件时将解析为运行时数据上下文中的某些属性。如果我拼错了 KeyIndex,该列仍会创建,但其中不会包含任何数据,因为反射 API 找不到合适的属性检索。
  • 哦,对,太棒了。再次为您的时间欢呼...当然,我会将其标记为答案...我正在尝试用整个方法解决另一个问题,这在很多方面都限制了我。我问了另一个问题hereMSDN 有一个正在进行的主题,如果你能在这里帮助我,我将非常感激......
  • 如果我正在服用这个 pi$$,请告诉我!无论如何,再次感谢,一切顺利。
【解决方案2】:

您的属性需要从 INotifyPropertyChanged 继承,因此它们可以更新 UI,您的 ViewModel 也是如此。让您的视图模型(或背后的代码)从 INotifyPropertyChanged 继承。实现接口,然后您可以为您的属性执行此操作。

    private string _keyIndex= string.Empty;
    public string KeyIndex
    {
        get { return this._keyIndex; }

       set
       {
            this._keyIndex= value;
            this.RaisePropertyChangedEvent("KeyIndex");
        }
   }

【讨论】:

  • 他们为什么这样做? ObservableCollection 的实现细节见here。我的问题是关于如何使DataGrid 自动枚举ResourceRow 类的List&lt;string&gt; - 感谢您的宝贵时间。
【解决方案3】:

@Andrew 的回答是正确的,但我喜欢简单易用,因此我实现了一种方法,可以自动设置列并使用我想要的名称绑定到数据网格,而不必使用数据网格列。

声明一个从属性继承的列名类,如下所示:

public class ColumnNameAttribute : Attribute
{
    public ColumnNameAttribute(string Name) { this.Name = Name; }
    public string Name { get; set; }

}

使用列名称装饰您的模型,如下所示:

public class Data
  {
        [ColumnName("Name")]
        public string Name{ get; set; }
        [ColumnName("Customer")]
        public string Client { get; set; }
        [ColumnName("Activity Type")]
        public string Activity_Type { get; set; }
    }

将 AutoGenerateColumns 设置为 True,然后添加此事件处理程序:

 private void gen_AutoGridColumns(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {           
        var desc = e.PropertyDescriptor as PropertyDescriptor;
        if (desc.Attributes[typeof(ColumnNameAttribute)] is ColumnNameAttribute att) e.Column.Header = att.Name;

        //set the width for each column           
        e.Column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);            
    }

您现在可以跳过将 DataGridColumns 定义添加到 DataGrid 中,并且仍然可以将它们命名为您想要的任何名称。

【讨论】:

    猜你喜欢
    • 2014-08-23
    • 2013-12-31
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 2016-09-01
    相关资源
    最近更新 更多