wpf - DataGrid 计算两个数据绑定(bind)单元格中值之间的差异

标签 wpf datagrid binding

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

enter image description here

测量列表绑定(bind)到 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>

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

例如此附加列应计算 2 月 13 日和 2 月 6 日的值之间的差值 => 199.789 - 187.115 = 15.674

实现这一目标的最佳方法是什么?我想避免测量类中的任何计算,它只应保存数据。我更喜欢 DataGrid 来处理计算。 那么有没有办法添加另一列来计算值之间的差异?也许使用某种转换器和极限绑定(bind)? ;D

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

最佳答案

极端绑定(bind)?没问题。

<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>

关于wpf - DataGrid 计算两个数据绑定(bind)单元格中值之间的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4996926/

相关文章:

wpf - 正确设置 DataGridCell 样式

c# - 在 WPF 中重新加载绑定(bind)到 Datagrid 的 DataTable

c# - XAML 标签 : binding text content when using text styles

wpf - 从 DataGrid 中访问 Window 的 DataContext

.net - 如何在嵌套在 HierarchicalDataTemplate 中的 DataTemplate 中设置 RelativeSource?

wpf - NotifyOnValidationError如何最终调用CanExecute(以及为什么它不适用于MVVM Light RelayCommand)

wpf - 滚动 WPF DataGrid 以在顶部显示所选项目

android - 从另一个应用程序绑定(bind)到服务

c# - WPF 中的 "Infinite"面板

窗口上的 WPF 渲染线