DataGrid 组 Header 未更新新记录

DataGrid Group Header not Updating on new Record

我在更新 DataGrid 分组 Header 时遇到问题。当我添加或删除记录时,总数不会更新,除非我求助于它,有时甚至编辑数量也不会更新总数。我根据我读到的一些论坛帖子的建议添加了 INotifyPropertyChanged、IEditableObject 和 IsLiveGroupingRequested,但不幸的是。我在转换器函数上放置了一个断点,我注意到当我 add/remove/edit 一条记录时它不会触发,直到我对 table 进行排序时它才会触发。我不确定我在这里做错了什么。

xaml

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        xmlns:s="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <local:Converter1 x:Key="c1"/>
        <CollectionViewSource x:Key="cvs1" Source="{Binding Path=Lines, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="Type"/>
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <DataGrid Name="_dataGrid1" ItemsSource="{Binding Source={StaticResource cvs1}}">
        <DataGrid.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="GroupItem">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="GroupItem">
                                    <StackPanel>
                                        <Grid>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="*"/>
                                                <ColumnDefinition Width="Auto"/>
                                            </Grid.ColumnDefinitions>
                                            <Label Grid.Column="0" Content="{Binding Path=Name}" Padding="0"/>
                                            <Label Grid.Column="1" Content="{Binding Converter={StaticResource c1}}" Padding="0"/>
                                        </Grid>
                                        <ItemsPresenter/>
                                    </StackPanel>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>
        </DataGrid.GroupStyle>
    </DataGrid>

</Window>

代码隐藏

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;

namespace WpfApp2
{

    public partial class MainWindow : Window
    {

        public MainWindow()
        {

            InitializeComponent();


            Transaction transaction = new Transaction();
            transaction.Lines.Add(new TransactionLine() { Account = "ACCOUNT1", Type = "ACCOUNT", Amount = -41.86 });
            transaction.Lines.Add(new TransactionLine() { Account = "BUDGET1", Type = "BUDGET", Amount = 41.86 });
            transaction.Lines.Add(new TransactionLine() { Account = "CATEGORY1", Type = "CATEGORY", Amount = 41.86 });
            this.DataContext = transaction;

        }

    }

    public class Transaction
    {
        public Transaction()
        {
            this.Lines = new ObservableCollection<TransactionLine>();
        }
        public DateTime Date { get; set; }
        public string Payee { get; set; }
        public ObservableCollection<TransactionLine> Lines { get; set; }
    }
    public class TransactionLine : INotifyPropertyChanged, IEditableObject
    {

        void IEditableObject.BeginEdit()
        {

        }
        void IEditableObject.CancelEdit()
        {

        }
        void IEditableObject.EndEdit()
        {

        }

        public TransactionLine() { }

        private string _account;
        private string _type;
        private double _amount;

        public string Account
        {
            get
            {
                return this._account;
            }
            set
            {
                this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Account"));
                this._account = value;
            }
        }
        public string Type
        {
            get
            {
                return this._type;
            }
            set
            {
                this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Type"));
                this._type = value;
            }
        }
        public double Amount
        {
            get
            {
                return this._amount;
            }
            set
            {
                this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Amount"));
                this._amount = value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

    }

    public class Converter1 : IValueConverter
    {

        object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Math.Round(((CollectionViewGroup)value).Items.Sum(l => ((TransactionLine)l).Amount), 2);
        }
        object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

    }

}

您在将新值分配给支持字段之前调用了 PropertyChanged 事件。确保在将 value 分配给您的字段后调用它。

public string Account
{
    get
    {
        return this._account;
    }
    set
    {
        this._account = value;
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Account"));
    }
}

这里的问题是仅在设置数据绑定 属性 时才调用转换器。由于您绑定到 CollectionViewGroup 本身,因此当您将新项目添加到 ObservableCollection<TransactionLine> 时,不会调用 Convert 方法。

除了绑定到 CollectionViewGroup 本身,您还可以使用 MultiBindingIMultiValueConverter 实现来绑定到 Count 属性源集合的:

public class Converter1 : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return Math.Round(((CollectionViewGroup)values[0]).Items.Sum(l => ((TransactionLine)l).Amount), 2);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

XAML:

<ControlTemplate TargetType="GroupItem">
    <StackPanel>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="0" Content="{Binding Path=Name}" Padding="0"/>
            <Label Grid.Column="1" Padding="0">
                <Label.Content>
                    <MultiBinding Converter="{StaticResource c1}">
                        <Binding Path="."  />
                        <Binding Path="Items.Count" />
                    </MultiBinding>
                </Label.Content>
            </Label>
        </Grid>
        <ItemsPresenter/>
    </StackPanel>
</ControlTemplate>

我在这个问题上花了很长时间,我发现更新组信息(例如组 header)的更简单方法如下:

        Dispatcher.Invoke(() =>
        {
            DataInfosGrid.Items.Refresh();
        });

您也可以更改 DataGrid ItemsSource,但我认为它对您的应用程序来说更重!

再见! ;-D

奥利维尔