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
本身,您还可以使用 MultiBinding
和 IMultiValueConverter
实现来绑定到 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
奥利维尔
我在更新 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
本身,您还可以使用 MultiBinding
和 IMultiValueConverter
实现来绑定到 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
奥利维尔