我有一个数据网格,其中包含通过CollectionViewGroup类创建的分组项目。对于每组项目,我计算该组项目的总和,然后计算所有组的总和。用户可以更改其中一个组的值的值。总和也必须改变,但事实并非如此。我想问题是OnPropertyChanged事件的限定符是受保护的,但我不确定。
我的代码是:
public MainWindow()
{
InitializeComponent();
var lv = new ObservableCollection<LPosition>();
lv.positions.Add(new LPosition() { GB = 5, OZ1 = "1", OZ2 = "1" });
lv.positions.Add(new LPosition() { GB = 5, OZ1 = "1", OZ2 = "2" });
lv.positions.Add(new LPosition() { GB = 5, OZ1 = "1", OZ2 = "2" });
lv.positions.Add(new LPosition() { GB = 5, OZ1 = "2", OZ2 = "1" });
lv.positions.Add(new LPosition() { GB = 5, OZ1 = "2", OZ2 = "1" });
lv.positions.Add(new LPosition() { GB = 5, OZ1 = "2", OZ2 = "2" });
myGrid.DataContext = lv;
在XAML中:
<Window.Resources>
<local:GroupSumConverter x:Key="groupSumConverter"/>
<CollectionViewSource x:Key="groupedItems" Source="{Binding ElementName=myGrid, Path=DataContext.positions}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="OZ1"/>
<PropertyGroupDescription PropertyName="OZ2"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<Style x:Key="FirstLevelGroupStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderThickness="2" BorderBrush="DarkGray" Margin="0,1">
<StackPanel>
<Expander>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" x:Name="TextBlock_SecondLvlSum">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} - {1}">
<Binding Path="Name"/>
<Binding Path="myGrid.DataContext"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Expander.Header>
<ItemsPresenter/>
</Expander>
<TextBlock TextAlignment="Right" FontWeight="Bold">
<TextBlock.Text>
<MultiBinding x:Name="totalSumConverter" Converter="{StaticResource groupSumConverter}" StringFormat="Summe: {0:N2}" ConverterCulture='de-DE'>
<Binding Path="Items"/>
<Binding />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SecondLevelGroupStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<StackPanel>
<ItemsPresenter>
<ItemsPresenter.Style>
<Style TargetType="ItemsPresenter">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Name}" Value="">
<Setter Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsPresenter.Style>
</ItemsPresenter>
<Border BorderThickness="2" BorderBrush="DarkGray" Margin="0,1" Background="Aquamarine">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding Name}" Value="">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<Expander>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} - {1}">
<Binding Path="Name"/>
<Binding Path="Items[0].kurztext"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Expander.Header>
<ItemsPresenter/>
</Expander>
<TextBlock TextAlignment="Right" FontWeight="Bold">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource groupSumConverter}" StringFormat="Summe: {0:N2}" ConverterCulture='de-DE'>
<Binding Path="Items"/>
<Binding Path="Items[0].GB"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<DataGrid AutoGenerateColumns="False"
x:Name="myGrid"
ItemsSource="{Binding Source={StaticResource groupedItems}}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding OZ1}" Header="OZ1"/>
<DataGridTextColumn Binding="{Binding OZ2}" Header="OZ2"/>
<DataGridTextColumn Binding="{Binding GB}" Header="GB"/>
</DataGrid.Columns>
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource FirstLevelGroupStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
<GroupStyle ContainerStyle="{StaticResource SecondLevelGroupStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
相应的转换器:
class GroupSumConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var lPosContainer = (ReadOnlyObservableCollection<Object>)values[0];
double sum = 0.0;
if (lPosContainer[0] != null && lPosContainer[0] is CollectionViewGroup)
{
foreach (CollectionViewGroup group in lPosContainer)
{
sum += sumPositions((ReadOnlyObservableCollection<Object>)group.Items);
}
}
else
{
sum += sumPositions(lPosContainer);
}
return sum;
}
private double sumPositions(ReadOnlyObservableCollection<Object> items)
{
double sum = 0.0;
foreach (LPosition lPos in items)
{
sum += lPos.GB;
}
return sum;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
LPosition类是:
class LPosition:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
private double _GB;
public double GB
{
get { return _GB; }
set { _GB = value;
OnPropertyChanged("GB");
}
}
public string OZ1 { get; set; }
public string OZ2 { get; set; }
}
任何建议如何解决这个问题和帮助将不胜感激!
编辑1:
我试图根据this question解决问题。我使用了一个类TrulyObservableCollection。现在的问题是,当其中的项目发生更改时,该集合会发出通知,但在项目更改后不会调用Converter
编辑2:我添加了截图,因此很清楚问题是什么:
The sum is correct
The sum is not correct
编辑3:我尝试过@ Estacado7706的解决方案,但仍然存在一个问题。
它适用于所有行,但最后一行除外。该 问题似乎是,指数的变化被另一个人所诟病 在这种情况下的方式。 SelectedIndex设置为-1,因为没有 下一个要选择的项目,因此调用转换器 (到目前为止按预期工作)。
问题是,没有传入集合的最后一个值 这个案例。 @ Estacado7706已经提到了这个并发布了一个解决方法。 但是这种解决方法对我不起作用:索引更改为0和 所以转换器再次被调用,但编辑的项目仍未通过。
@ Estacado7706在解决方法中提到将selectedItemchanged事件添加到DataGrid。没有这样的事件(至少我没有找到一个),所以我尝试了SelectionChanged事件,以及SelecedCellsChanged事件,但是 两者似乎都在提交之前被调用。
我还试图通过使用
手动提交项目来避免这种情况myGrid.CommitEdit();
myGrid.Items.Refresh();
这有效并刷新了总和,但缺点是,所有
扩展器是关闭的(因为Items.Refresh())
,这不是很好
用户友好。
答案 0 :(得分:1)
您的主要问题应该是,通过绑定到集合,您只需更新值,如果项目集已更改。例如:添加了新项目。 这是启动时正确值的原因,之后没有任何变化。 有趣的是:只要您在第二级组中只有一个项目,它就可以工作,因为编辑项目与编辑集合相同。
这是一个应该做的诀窍。
在您的第一级别小组中:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource mySumConverter}" StringFormat="Summe: {0:N2}" ConverterCulture='de-DE'>
<Binding ElementName="myGrid" Path="SelectedItem"/>
<Binding Path="Items"/>
<!--The second value is only used that the Text is updated (as Items itself has no INotifyPropertyChanged).
TODO: This is no really a nice way of doing this, but it works. Has to be improved if someone finds a better solution-->
</MultiBinding>
</TextBlock.Text>
</TextBlock>
&#34; new&#34;转换器(几乎相同):
class MySumConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double sum = 0.0;
var lPosContainer = (ReadOnlyObservableCollection<Object>)values[1];
if (lPosContainer[0] != null && lPosContainer[0] is CollectionViewGroup)
{
foreach (CollectionViewGroup group in lPosContainer)
{
sum += sumPositions((ReadOnlyObservableCollection<Object>)group.Items);
}
}
else
{
sum += sumPositions(lPosContainer);
}
return sum;
}
private double sumPositions(ReadOnlyObservableCollection<Object> items)
{
double sum = 0.0;
foreach (LPosition lPos in items)
{
sum += lPos.GB;
}
return sum;
}
之后你会得到这样的情况,即当你点击任何项目时你的总和是正确的,但是在编辑之后你的总和将代表真实的总和减去最近编辑的值。原因是:收集没有价值......(不要问我为什么) 解决方法:
添加&#34; selectedItemChanged&#34;事件到你的网格:
if (myGrid.SelectedIndex < 1 && myGrid.Items.Count > 0)
myGrid.SelectedIndex = 0;
当击中输入时,您的最后一个单元格不再被选中(在您的情况下,即使第一级别组关闭,只有一个项目存在时)。因此调用此方法。它将更改selectedIndex,从而导致总和更新并且即可。
不是很好(像地狱一样丑陋),但是工作。
答案 1 :(得分:0)
这应该很容易修复。 按下输入后,您将前往下一行。如果您位于组的末尾,则会选择下一组的第一个元素(如果它已展开)。如果不是这种情况,则无需选择,最终得-1。
只需创建一个继承自DataGrid的自定义网格,重写OnKEyDown和OnPreviewKeyDown即可按照预期的方式使用除Key.Return之外的所有内容。如果这是按下,只需离开编辑模式,但不要再继续了。
第二种可能性:更改验证模板在验证后使工作保持在同一行的方式。我已经看过其中的一些模板,但我在最后一刻内找不到它来链接它。