构造类以绑定到datagrid的最佳方法

时间:2016-03-22 10:31:17

标签: c# .net oop

我有一个如下所示的课程。

class RegionSale
{
    DateTime DateSale;
    string Region;
    double DollarAmount; 
}

在运行时,代码不知道需要多少个RegionSale对象,这很好,因为我可以创建RegionSale对象列表。

我遇到的问题是我现在要求通过wpf数据网格显示数据,但格式如下所示,

 DateS      UK      US      EUxUK   JAP     Brazil
 2015-12-03 23634   22187   NULL    NULL    NULL
 2015-12-04 56000   22187   NULL    NULL    NULL
 2015-12-14 56000   10025   NULL    NULL    NULL

所以我可以创建一个像下面这样的新类(但感觉这是一个坏主意)

class RegionSaleNew
{
    DateTime DateSale;
    double UK;
    double US;
    double EUxUK;
    double JAP;
    double Brazil;
}

正如我前面提到的,我不会在运行时知道区域的数量,所以上面的类似乎是一个坏主意,但显然很容易绑定到datagrid。

问题是如何最好地构建我的类,同时考虑到datagrid&的格式。直到运行时才知道区域的数量?反思是个好主意吗?

2 个答案:

答案 0 :(得分:0)

我会编写一个派生INotifyPropertyChanged接口的类,并为每个区域创建一个属性,如示例所示。

然后我会使用ObservableCollection<myClass>并绑定到属性。

class RegionSaleNew : INotifyPropertyChanged
{
    DateTime DateSale;
    double _uk;
    .....
    public double UK
    {
        get { return _monitor; }
        set
        {
            if (value == _uk) return;
            _uk = value;
            OnPropertyChanged();
        }
    }
    // Add all properties here

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

在XAML中,您可以像这样绑定

...
<DataGridTextColumn Width="Auto" Header="UK" Binding="{Binding UK, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True"/>
...

然后,您只需要设置DataGrid.ItemsSource = myObservableCollection或XAML <DataGrid ItemsSource="{Binding myObservableCollection} ... </DataGrid>

答案 1 :(得分:0)

其实我对同一个主题感兴趣,所以它是我第一个SO问题Cross tabular data binding in WPF的目标。提供的答案一般涵盖了它,但不适用于DataGrid特定绑定。所以我的结论是解决方案应该基于一些System.ComponentModel概念:

(1)自定义PropertyDescriptor实施提供&#34;虚拟&#34;属性。
(2)项目类实现ICustomTypeDescriptor以暴露&#34;虚拟&#34;每件物品 (3)实现ITypedList的集合类,允许从&#34;虚拟&#34;创建自动数据网格列。属性。

拥有你的模特

class RegionSale
{
    public DateTime DateSale;
    public string Region;
    public double DollarAmount;
}

和数据

IEnumerable<RegionSale> data = ...;

&#34;虚拟&#34;只需获取Region字段的明确列表,即可在运行时确定属性:

var regions = data.Select(sale => sale.Region).Distinct().ToList();

对于每个区域,我们将创建一个区域为Name的属性描述符,稍后将其用作项内部字典的键以检索值。

将通过DateSale对数据进行分组来构建项目。

以下是整个实施:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

class RegionSalePivotViewItem : CustomTypeDescriptor
{
    private RegionSalePivotView container;
    private Dictionary<string, double> amountByRegion;
    internal RegionSalePivotViewItem(RegionSalePivotView container, DateTime date, IEnumerable<RegionSale> sales)
    {
        this.container = container;
        DateSale = date;
        amountByRegion = sales.ToDictionary(sale => sale.Region, sale => sale.DollarAmount);
    }
    public DateTime DateSale { get; private set; }
    public double? GetAmount(string region)
    {
        double value;
        return amountByRegion.TryGetValue(region, out value) ? value : (double?)null;
    }
    public override PropertyDescriptorCollection GetProperties()
    {
        return container.GetItemProperties(null);
    }
}

class RegionSalePivotView : ReadOnlyCollection<RegionSalePivotViewItem>, ITypedList
{
    private PropertyDescriptorCollection properties;
    public RegionSalePivotView(IEnumerable<RegionSale> source) : base(new List<RegionSalePivotViewItem>())
    {
        // Properties
        var propertyList = new List<PropertyDescriptor>();
        propertyList.Add(new Property<DateTime>("DateSale", (item, p) => item.DateSale));
        foreach (var region in source.Select(sale => sale.Region).Distinct().OrderBy(region => region))
            propertyList.Add(new Property<double?>(region, (item, p) => item.GetAmount(p.Name)));
        properties = new PropertyDescriptorCollection(propertyList.ToArray());
        // Items
        ((List<RegionSalePivotViewItem>)Items).AddRange(
            source.GroupBy(sale => sale.DateSale,
                (date, sales) => new RegionSalePivotViewItem(this, date, sales))
                .OrderBy(item => item.DateSale)
        );
    }
    public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) { return properties; }
    public string GetListName(PropertyDescriptor[] listAccessors) { return null; }
    class Property<T> : PropertyDescriptor
    {
        Func<RegionSalePivotViewItem, Property<T>, T> getValue;
        public Property(string name, Func<RegionSalePivotViewItem, Property<T>, T> getValue) : base(name, null) { this.getValue = getValue; }
        public override Type ComponentType { get { return typeof(RegionSalePivotViewItem); } }
        public override Type PropertyType { get { return typeof(T); } }
        public override object GetValue(object component) { return getValue((RegionSalePivotViewItem)component, this); }
        public override bool IsReadOnly { get { return true; } }
        public override bool CanResetValue(object component) { return false; }
        public override void ResetValue(object component) { throw new NotSupportedException(); }
        public override void SetValue(object component, object value) { throw new NotSupportedException(); }
        public override bool ShouldSerializeValue(object component) { return false; }
    }
}

样本测试:

视图模型

class ViewModel
{
    public RegionSalePivotView PivotView { get; set; }
}

XAML

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="dataGrid" HorizontalAlignment="Left" Margin="28,33,0,0" VerticalAlignment="Top" Height="263" Width="463" ItemsSource="{Binding PivotView}"/>
    </Grid>
</Window>

背后的代码

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var data = new[]
        {
            new RegionSale { DateSale = new DateTime(2015, 12, 03), Region = "UK", DollarAmount = 23634 },
            new RegionSale { DateSale = new DateTime(2015, 12, 03), Region = "US", DollarAmount = 22187 },
            new RegionSale { DateSale = new DateTime(2015, 12, 04), Region = "UK", DollarAmount = 56000 },
            new RegionSale { DateSale = new DateTime(2015, 12, 04), Region = "US", DollarAmount = 22187 },
            new RegionSale { DateSale = new DateTime(2015, 12, 14), Region = "UK", DollarAmount = 56000 },
            new RegionSale { DateSale = new DateTime(2015, 12, 14), Region = "US", DollarAmount = 10025 },
        };
        DataContext = new ViewModel { PivotView = new RegionSalePivotView(data) };
    }
}

结果

enter image description here