Search code examples
wpfvb.netwpfdatagrid

Getting a group total in DataGrid


I'll try to make this as simple as possible, I have been working on this for 3 days and have tried many different options but for some reason I still cannot get it to work. I'm fairly new to wpf and for that matter vb.net.

So I have a DataGrid that has grouping, multiple levels. The code for this is below.

This is the UserControl.Resources section:

<CollectionViewSource x:Key="HoursViewSource">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="Phase"/>
            <PropertyGroupDescription PropertyName="Employee"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
    <local:GroupTotalConverter x:Key="GroupTotal"/>

And The DataGrid: I have a second groupstyle below this one on the same datagrid, but it is not really important to the question.

<DataGrid  Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Mode=OneWay}" IsReadOnly="True" AutoGenerateColumns="False">
                                   <DataGrid.Columns>
                    <DataGridTextColumn Width="80" Header="Date" Binding="{Binding TransDate, StringFormat=d}"/>
                    <DataGridTextColumn Width="80" Header="Period" Binding="{Binding Period}"/>
                    <DataGridTextColumn Width="80" Header="Hours" Binding="{Binding Hours, StringFormat=n}"/>
                    <DataGridTextColumn Width="200" Header="Comment" Binding="{Binding TransComment, StringFormat=n}"/>
                </DataGrid.Columns>
                <DataGrid.GroupStyle>
                    <GroupStyle>
                        <GroupStyle.ContainerStyle>
                            <Style TargetType="{x:Type GroupItem}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate>
                                            <Expander Foreground="White" Margin="0,0,0,3">
                                               <Expander.Header>
                                                    <DockPanel>
                                                        <TextBlock Text="{Binding Path=Name}"/>
                                                        <TextBlock Text="{Binding Path=Items, Converter={StaticResource GroupTotal}"/>
                                                    </DockPanel>
                                                </Expander.Header>
                                                <Expander.Content>
                                                    <ItemsPresenter>
                                                        <ItemsPresenter.Effect>
                                                            <DropShadowEffect/>
                                                        </ItemsPresenter.Effect>
                                                    </ItemsPresenter>
                                                </Expander.Content>
                                            </Expander>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </GroupStyle.ContainerStyle>
                    </GroupStyle>

                </DataGrid.GroupStyle>

            </DataGrid>

As you can see I have a converter that gets passed the Items from the group, I have been checking the items that get passed and this is working correctly. The problem is in the converter, or at least that's the assumption that I have been going on. When I look at the collection of items that it gets passed there are all the properties that are being sent to the datagrid. However I cannot do anything with the data that is sent to the converter.

Converter:

 Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
    If TypeOf value Is ReadOnlyObservableCollection(Of Object) Then
        Dim items = CType(value, ReadOnlyObservableCollection(Of Object))
        Dim total As Decimal = 0
        For Each gi As GroupItem In items
            total += gi.Hours
        Next gi
        Return total.ToString()
    End If
    Return ""

So this converter was a conversion from c#, that I found in another question. I cannot cast it to the same type of object as the original, although this probably could be done with some major modifications to the underlying datagrid source. I have also tried casting it to a simple class that has the properties of the collection that is passed to the converter it just errors and says that the item "Hours" in this case is not part of the CollectionGroupViewInternal When stepping through the code and looking at the collection there is a property Hours = .

I hope I made my issue clear, something tells me this is a super easy fix, but my stubborn nature required that I spend way to many hours on this.

What I'm hoping to do is create a converter that I can use over and over to get the total of specific columns and place it in a TextBlock of the header of that group. The current project that I'm working on will have dozens of these DataGrids with groupings.

Thank you for your help in advance.


Solution

  • Isn't that how it goes, as soon as you ask for help you figure it out.

    Well I will post the solution that I found and how I came about it. I'm not sure if this is the best solution, but it does work.

    So as I was working with the debugger, I realized that it was sending the converter a collection of collections. Well sort of. It was sending a ReadOnlyObservableCollection, that collection had 1 thing, a collection of CollectionViewGroupInternal which derives from CollectionViewGroup, Then that collection had the items that I wanted to be included in the total amount.

    So here is the converter: I hope it will help someone.

     Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
        If TypeOf value Is ReadOnlyObservableCollection(Of Object) Then
            For Each r In value
                Dim items As CollectionViewGroup = CType(r, CollectionViewGroup)
                Dim total As Decimal = 0
                For Each item In items.Items
                    total += item.Hours
                Next
                Return total
            Next
        End If
        Return ""
    End Function
    

    If you see a way to improve this I would be grateful.