【问题标题】:DataGrid calculate difference between values in two databound cellsDataGrid 计算两个数据绑定单元格中的值之间的差异
【发布时间】:2011-02-14 20:32:04
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: wpf datagrid binding


    【解决方案1】:

    极端绑定?没问题。

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

    【讨论】:

    • 非常感谢!这就像一个魅力:) 但正如你所说,有一些缺点。但没问题,这么小的解决方案真是太棒了!仅出于兴趣:在这种情况下,您将如何实现中间 ViewModel?
    • @justMe 我在答案中添加了我的示例。它使用 CollectionView 并观察变化。
    • @vorrtex : 如何在这个场景中实现相同的视图模型?我有一个数据网格,它垂直显示数据库中的表格,如下所示: 75 | 100 .我必须在分数下方添加一个名为差异的新行,它应该显示或返回值 75 和 100 之间的差异。(这只是一个例子)(P.S:没有来自差异的负值)。这真的是有帮助..谢谢
    • @Buba1947 如果您需要一行,而不是一列,您应该在您的收藏中添加一个新项目。喜欢someCollection.Add(new SomeModel { Title = "Difference", Score75 = null, Score100 = (scoresItem.Score100 - scoresItem.Score75) });。想如果你把你想要的表格截图并提出一个单独的问题会更好。
    • @vorrtex :我已经在link 中发布了这个问题。有空的时候请看一下。谢谢你..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多