具有动态可编辑列的DataGrid

我一直在尝试在WPF MVVM项目中使用动态列创建一个可编辑的DataGrid 。 动态列的类型相同,即: decimal

目的是收集部门数量不确定的商店的部门总数。 我试着在下面演示它。

 Day Dept1 Dept2 Dept3... TotalOfDepartments CashTotal CreditTotal ===================================================================== 1 100 200 50 350 50 300 2 75 100 0 175 25 150 

因此,有许多商店有无限期的部门,我的目标是收集月份

我想让Department,CashTotal和CreditTotal Columns可以编辑。 我尝试了几种方法:

  • 使用mVVm填充动态datagrid列和可编辑的绑定
  • 使用MVVM在Silverlight应用程序中使用动态列填充DataGrid
  • 如何将WPF DataGrid绑定到可变数量的列?

这是我最后一次尝试的最后一次尝试。 如下:

模型:

  public class DailyRevenues { public int ShopId { get; set; } public int Day { get; set; } public ObservableCollection DepartmentList { get; set; } public DailyRevenues() { this.DepartmentList = new ObservableCollection(); } } public class Department { public string Name { get; set; } private decimal total; public decimal Total { get { return total; } set { total = value; } } } 

视图模型:

 public class DataItemViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public DataItemViewModel() { this.MonthlyRevenues = new ObservableCollection(); var d1 = new DailyRevenues() { ShopId = 1, Day = 1 }; d1.DepartmentList.Add(new Department() { Name = "Deapartment1", Total = 100 }); d1.DepartmentList.Add(new Department() { Name = "Deapartment2", Total = 200 }); var d2 = new DailyRevenues() { ShopId = 1, Day = 2 }; d2.DepartmentList.Add(new Department() { Name = "Deapartment1", Total = 75 }); d2.DepartmentList.Add(new Department() { Name = "Deapartment2", Total = 150 }); d2.DepartmentList.Add(new Department() { Name = "Deapartment3", Total = 100 }); this.MonthlyRevenues.Add(d1); this.MonthlyRevenues.Add(d2); } private ObservableCollection monthlyRevenues; public ObservableCollection MonthlyRevenues { get { return monthlyRevenues; } set { if (monthlyRevenues != value) { monthlyRevenues = value; OnPropertyChanged(nameof(MonthlyRevenues)); } } } private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } 

和XAML:

           

不幸的是,在最后一次尝试使用XAML上的索引器并没有帮助我动态列,我找不到任何其他方式绑定它们的方法。

更多信息:上面的datagrid(和数据演示)属于shop1,我想在窗口/用户控件上收集它的部门的月收入。 每个商店在整个月都有相同的部门数量,但这并不意味着每个部门每天都应该有收入,它可以为零。 该部门可能会在任何一天关闭,因此当天不会增加任何收入。 Shop2可能在同一个月有完全不同的部门,所以我不会在同一个屏幕上处理所有商店。

编辑1:有关添加的场景的更多信息。

您可以采取多种不同的方法,每种方法都有优点和缺点。 根据您对问题的更完整描述,我选择了自定义类型描述符方法。

在这里,我们为每日收入类添加一个自定义类型描述符…

 public class DailyRevenues : ICustomTypeDescriptor { public int ShopId { get; set; } public int Day { get; set; } public ObservableCollection DepartmentList { get; set; } public DailyRevenues() { this.DepartmentList = new ObservableCollection(); } public decimal TotalOfDepartments { get; } public decimal CashTotal { get; } public decimal CreditTotal { get; } public AttributeCollection GetAttributes() { return new AttributeCollection(); } public string GetClassName() { return "DailyRevenues"; } public string GetComponentName() { return ""; } public TypeConverter GetConverter() { return null; } public EventDescriptor GetDefaultEvent() { return null; } public PropertyDescriptor GetDefaultProperty() { return null; } public object GetEditor(Type editorBaseType) { return null; } public EventDescriptorCollection GetEvents() { return null; } public EventDescriptorCollection GetEvents(Attribute[] attributes) { return null; } public PropertyDescriptorCollection GetProperties() { PropertyDescriptorCollection pdc0 = TypeDescriptor.GetProperties(typeof(DailyRevenues)); List pdList = new List(); pdList.Add(pdc0["Day"]); for (int i = 0; i < DepartmentList.Count; ++i) { pdList.Add(new DailyRevenuesProperty(DepartmentList[i].Name, i)); } pdList.Add(pdc0["TotalOfDepartments"]); pdList.Add(pdc0["CashTotal"]); pdList.Add(pdc0["CreditTotal"]); return new PropertyDescriptorCollection(pdList.ToArray()); } public PropertyDescriptorCollection GetProperties(Attribute[] attributes) { return GetProperties(); } public object GetPropertyOwner(PropertyDescriptor pd) { return this; } } 

自定义类型描述符允许我们“展平”数据结构。 随着部门数量的变化,对象上的属性数量也会发生变化。 这需要每日收入等级的自定义属性描述符...

 public class DailyRevenuesProperty : PropertyDescriptor { int _index; public DailyRevenuesProperty(string name, int index) : base(name, new Attribute[0]) { _index = index; } public override Type ComponentType { get { return typeof(DailyRevenues); } } public override bool IsReadOnly { get { return false; } } public override Type PropertyType { get { return typeof(decimal); } } public override bool CanResetValue(object component) { return false; } public override object GetValue(object component) { DailyRevenues dr = component as DailyRevenues; if(dr != null && _index >= 0 && _index < dr.DepartmentList.Count) { return dr.DepartmentList[_index].Total; } else { return (decimal)0; } } public override void ResetValue(object component) { } public override void SetValue(object component, object value) { DailyRevenues dr = component as DailyRevenues; if (dr != null && _index >= 0 && _index < dr.DepartmentList.Count && value is decimal) { dr.DepartmentList[_index].Total = (decimal)value; } } public override bool ShouldSerializeValue(object component) { return false; } } 

现在我们需要一个打字列表。 这取代了可观察的集合。

 public class MonthlyRevenues : ObservableCollection, ITypedList { public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) { if(Count > 0) { return TypeDescriptor.GetProperties(this[0]); } else { return TypeDescriptor.GetProperties(typeof(DailyRevenues)); } } public string GetListName(PropertyDescriptor[] listAccessors) { return "Monthly Revenues"; } } 

自动生成列时,数据网格会检查项集合是否为类型列表。 如果是,则数据网格查询键入列表上的属性。

最后要总结一下,这里是数据网格......

   

这是由此产生的网格......

在此处输入图像描述

这种方法有许多限制。 首先,我依靠数据网格来自动生成列。 如果我想在标题文本中添加空格之类的东西,我需要做更多的事情。 其次,我指望部门名称是有效的属性名称,并且不会与日常收入类别中的其他属性发生冲突。 如果没有,那么我将需要做更多的事情。 等等。