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

时间:2015-07-01 15:59:24

标签: c# wpf datagrid

我有一个数据网格,其中包含通过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()),这不是很好 用户友好。

2 个答案:

答案 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之外的所有内容。如果这是按下,只需离开编辑模式,但不要再继续了。

第二种可能性:更改验证模板在验证后使工作保持在同一行的方式。我已经看过其中的一些模板,但我在最后一刻内找不到它来链接它。