DataGrid计算两个数据绑定单元格中值之间的差异

时间:2011-02-14 20:32:05

标签: wpf datagrid binding

在我的小应用程序中,我有一个DataGrid(见截图),它绑定到一个Measurement对象列表。 Measurement只是一个具有两个属性的数据容器:Date和CounterGas(float)。 每个Measurement对象代表特定日期的燃气消耗量。

enter image description here

测量列表绑定到DataGrid,如下所示:

    <DataGrid ItemsSource="{Binding Path=Measurements}" AutoGenerateColumns="False">
        <DataGrid.Columns>
             <DataGridTextColumn Header="Date" Binding="{Binding Path=Date, StringFormat={}{0:dd.MM.yyyy}}" />
             <DataGridTextColumn Header="Counter Gas" Binding="{Binding Path=ValueGas, StringFormat={}{0:F3}}" />
        </DataGrid.Columns>
    </DataGrid>

好吧,现在我的问题:) 我想在“Counter Gas”列旁边有另一个列,它显示实际计数器值和最后一个计数器值之间的差异。

E.g。这个额外的列应该计算2月13日和2月6日之间的差值=&gt; 199.789 - 187.115 = 15.674

实现这一目标的最佳方法是什么?我想避免在Measurement类中进行任何只能保存数据的计算。我更喜欢DataGrid来处理计算。 那么有没有办法添加另一个只计算值之间差异的列?也许使用某种转换器和极端绑定? ; d

P.S。:也许名声较好的人可以嵌入屏幕截图。谢谢:))

1 个答案:

答案 0 :(得分:4)

极端绑定?没问题。

<Window.Resources>
    <local:ItemsDifferenceConverter x:Key="ItemsDifferenceConverter"/>
</Window.Resources>
<DataGrid ItemsSource="{Binding Path=Measurements}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Date" Binding="{Binding Path=Date, StringFormat={}{0:dd.MM.yyyy}}" />
        <DataGridTextColumn Header="Counter Gas" Binding="{Binding Path=ValueGas, StringFormat={}{0:F3}}" />
        <DataGridTextColumn Header="Difference">
            <DataGridTextColumn.Binding>
                <MultiBinding Converter="{StaticResource ItemsDifferenceConverter}" Mode="OneWay">
                    <Binding Path="."/>
                    <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}" Path="ItemsSource"/>
                </MultiBinding>
            </DataGridTextColumn.Binding>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

某种转换器

class ItemsDifferenceConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType,
                      object parameter, CultureInfo culture)
    {
        if (values.Length != 2)
            return null;

        var item = values[0] as Measurement;
        var collection = values[1] as IEnumerable<Measurement>;
        if (item == null || collection == null)
            return null;

        var list = collection.OrderBy(v => v.Date).ToList(); //it will be easier to find a previous date
        var itemIndex = list.IndexOf(item);
        if (itemIndex == 0) //First item
            return null;

        var diff = item.ValueGas - list[itemIndex - 1].ValueGas;
        return (diff > 0 ? "+" : "") + diff.ToString();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }
}

但是此示例不适用于删除/更新基础集合的项目。在这种情况下,中间ViewModel是最佳选择。

这是我的方法。它适用于更新,删除和添加项目。

/// <summary>
/// Main ViewModel, contains items for DataGrid
/// </summary>
public class MeasurementListViewModel
{
    public MeasurementListViewModel(IEnumerable<Measurement> measurements)
    {
        this.Items = new ObservableCollection<MeasurementViewModel>(measurements.Select(m=>new MeasurementViewModel(m)));
        this.Measurements = (ListCollectionView)CollectionViewSource.GetDefaultView(this.Items);

        this.Items.CollectionChanged += new NotifyCollectionChangedEventHandler(Items_CollectionChanged);
        foreach(var m in this.Items)
            m.PropertyChanged += new PropertyChangedEventHandler(Item_PropertyChanged);

    }

    //Date or Value were changed
    void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //Update the collection view if refresh isn't possible
        if (this.Measurements.IsEditingItem)
            this.Measurements.CommitEdit();
        if (this.Measurements.IsAddingNew)
            this.Measurements.CommitNew();

        this.Measurements.Refresh();
    }

    //Items were added or removed
    void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        //Attach the observer for the properties
        if (e.NewItems != null)
            foreach (var vm in e.NewItems.OfType<MeasurementViewModel>())
                vm.PropertyChanged += Item_PropertyChanged;

        //Refresh when it is possible
        if(!this.Measurements.IsAddingNew && !this.Measurements.IsEditingItem)
            this.Measurements.Refresh();
    }

    private ObservableCollection<MeasurementViewModel> Items { get; set; }

    public ListCollectionView Measurements { get; set; }
}

/// <summary>
/// Wraps Measurement class and provide notification of changes
/// </summary>
public class MeasurementViewModel
{
    public MeasurementViewModel()
    {
        this.Model = new Measurement();
    }

    public MeasurementViewModel(Measurement m)
    {
        this.Model = m;
    }

    public Measurement Model { get; private set; }

    public DateTime Date
    {
        get { return this.Model.Date; }
        set
        {
            this.Model.Date = value;
            OnPropertyChanged("Date");
        }
    }

    public double ValueGas
    {
        get { return this.Model.ValueGas; }
        set
        {
            this.Model.ValueGas = value;
            OnPropertyChanged("ValueGas");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

转换器有点不同:

class ItemsDifferenceConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType,
                      object parameter, CultureInfo culture)
    {
        var item = values[0] as MeasurementViewModel;
        var view = values[1] as ICollectionView;
        if (item == null || view == null)
            return null;

        var list = view.SourceCollection.OfType<MeasurementViewModel>().OrderBy(v => v.Date).ToList(); //it will be easier to find a previous date
        var itemIndex = list.IndexOf(item);

        if (itemIndex == 0) //First item
            return null;

        var diff = item.ValueGas - list[itemIndex - 1].ValueGas;
        return (diff > 0 ? "+" : "") + diff.ToString();
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }
}

DataGrid

<DataGrid ItemsSource="{Binding Path=Measurements}"  AutoGenerateColumns="False"
          CanUserAddRows="True">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Date" Binding="{Binding Path=Date, StringFormat={}{0:dd.MM.yyyy}}" />
        <DataGridTextColumn Header="Counter Gas" Binding="{Binding Path=ValueGas, StringFormat={}{0:F3}}" />
        <DataGridTextColumn Header="Difference">
            <DataGridTextColumn.Binding>
                <MultiBinding Converter="{StaticResource ItemsDifferenceConverter}" Mode="OneWay">
                    <Binding Path="."/>
                    <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}" Path="ItemsSource"/>
                </MultiBinding>
            </DataGridTextColumn.Binding>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>