简体   繁体   English

如何刷新datagrid wpf中的子组总和?

[英]How to refresh sums of subgroups in datagrid wpf?

I have a datagrid with grouped items created via CollectionViewGroup class. 我有一个DataGrid,其中包含通过CollectionViewGroup类创建的分组项目。 For each group of items I calculate the sum of the items of this group and then the total sum of all groups. 对于每组项目,我计算该组项目的总和,然后计算所有组的总和。 The user can change value of a sum of one of groups. 用户可以更改一组值之和的值。 The total sum has to be changed as well, but it doesn't. 总金额也必须更改,但不必更改。 I suppose that the problem is that the qualifier of the event OnPropertyChanged is protected, but I'm not sure. 我想问题是事件OnPropertyChanged的限定符受到保护,但我不确定。

My code is: 我的代码是:

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;

in XAML: 在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>

The corresponding converter: 对应的转换器:

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();
    }
}

And the LPosition class is: 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; }
}

Any suggestions how to solve this problem and help would be appreciated! 任何建议如何解决这个问题和帮助,将不胜感激!

EDIT 1: 编辑1:
I have tried to solve the problem according to this question . 我已经尝试根据这个问题解决问题 I've used a class TrulyObservableCollection. 我使用了TrulyObservableCollection类。 The problem now is, that collection notices when item in it changes, but the Converter is not called after the item has been changed. 现在的问题是,该集合会在其中的项目更改时发出通知,但在更改项目后不会调用Converter。
EDIT 2: I have added screenshots so it's clear what the problem is: 编辑2:我已经添加了屏幕截图,所以很清楚问题是什么:

The sum is correct 总数是正确的
The sum is not correct 总数不正确

EDIT 3: I have tried the solution of @Estacado7706, but there is still one problem remaining. 编辑3:我尝试了@ Estacado7706的解决方案,但仍然存在一个问题。

It works for all rows, except the ones that are the last of a group. 它适用于所有行,但组中的最后一行除外。 The problem seems to be, that the change of the index is handeled in another way in this case. 问题似乎在于,在这种情况下,索引的更改以另一种方式处理。 The SelectedIndex is set to -1 because there is no next following item to select and therefore the converter is called (works as expected so far). SelectedIndex设置为-1,因为没有下一个要选择的项目,因此调用了转换器(到目前为止,按预期工作)。

The problem is, that the last value of the collection is not passed in this case. 问题是在这种情况下不传递集合的最后一个值。 @Estacado7706 has mentioned this and posted a workaround. @ Estacado7706提到了这一点,并发布了一种解决方法。 But this workaround does not work for me: The index is changed to 0 and so the converter is called again, but the edited item is still not passed. 但是,这种解决方法对我不起作用:索引更改为0,因此再次调用了转换器,但是仍未传递已编辑的项目。

@Estacado7706 has mentioned in a workaround to add a selectedItemchanged event to the DataGrid. @ Estacado7706在解决方法中提到了将selectedItemchanged事件添加到DataGrid的方法。 There is no such event (at least I didn't find one), so I tried SelectionChanged event, as well as SelecedCellsChanged event, but both seem to be called before the commit. 没有这样的事件(至少我没有找到一个事件),因此我尝试了SelectionChanged事件以及SelecedCellsChanged事件,但似乎都在提交之前调用了它们。

I have also tried to avoid this by commiting the item manually by using 我还尝试通过使用手动提交项目来避免这种情况

myGrid.CommitEdit();
myGrid.Items.Refresh();

This works and refreshes the sum, but the disadvantage is, that all expanders are closed (because of the Items.Refresh()) , which is not very user-friendly. 这可以工作并刷新总和,但是缺点是所有扩展器都已关闭(由于Items.Refresh()) ,这不是非常用户友好的。

Your main problem should be, that, by binding to the collection you only update the values, if the itemset is changed. 您的主要问题应该是,如果更改了项集,则通过绑定到集合仅更新值。 Eg: new items are added. 例如:添加了新项目。 That's the reason for correct values on startup and no change at all after that. 这就是启动时正确设置值的原因,此后完全没有更改。 The funny thing: It works, as long as you only have one item within a second level group, because then editing the item is the same as editing the collection. 有趣的是:只要您在第二级组中只有一个项目,它就可以工作,因为然后编辑项目与编辑集合相同。

Here is a little hack that should do the trick. 这是一个应该解决的小技巧。

In your first level group: 在您的第一级小组中:

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

the "new" converter (pretty much the same): “新”转换器(大致相同):

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

After that you will have the situation, that your sum is only correct, when you klick any item, but after editing your sum will represent the real sum minus the recently edited value. 之后,您将遇到一种情况,即当您点选任何项目时,总和才是正确的,但是在编辑后,总和将代表实际总和减去最近编辑的值。 The reason: the collection is passed without the value...(Don't ask me why) A workaround for this: 原因:传递的集合没有值...(不要问我为什么)的解决方法:

Add a "selectedItemChanged" event to your grid: 将“ selectedItemChanged”事件添加到网格中:

if (myGrid.SelectedIndex < 1 && myGrid.Items.Count > 0)
            myGrid.SelectedIndex = 0;

When hitting enter your last cell is not selected anymore (in your case, even the first level group closes, when only one item is present). 按下Enter键时,不再选择最后一个单元格(在您的情况下,即使只有一个项目,第一个级别组也会关闭)。 Therefore this method is called. 因此,称为此方法。 It will change the selectedIndex, thereby causing the sums to be updated and that's it. 它将更改selectedIndex,从而使总和被更新而已。

Not really nice (rather ugly as hell), but working. 不太好(比较丑陋),但是可以工作。

That should be fairly easy to fix. 那应该很容易解决。 After pressing enter you travel to the next row. 按Enter键后,您进入下一行。 If you are at the end of a group you'll select the first element of the next group, if it is expanded. 如果您位于某个组的末尾,则将展开下一个组的第一个元素。 If that is not the case there is nothing to select and you end up with -1. 如果不是这种情况,则没有任何选择可供选择,最终得到-1。

Just create a custom grid, that inherits from DataGrid, override OnKEyDown and OnPreviewKeyDown to work as intended with everything except Key.Return. 只需创建一个继承自DataGrid的自定义网格,覆盖OnKEyDown和OnPreviewKeyDown即可按预期使用除Key.Return之外的所有内容。 If this is presses, just leave the editmode, but don't go any further. 如果按此键,则只需退出编辑模式,就可以了。

Second possibility: change the way the validation template does the work to stay in the same row after validating. 第二种可能性:更改验证模板完成验证后留在同一行中的工作方式。 I've seen a few of those template, but I couldn't find it within the last minute to link it here. 我已经看过其中一些模板,但是在最后一分钟内找不到将其链接到此处的模板。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM