2
votes

I'm trying to compose a data grid (in an MVVM 4.5 project) that will show my data grouped by a specific property (by use of expanders). This all works but I'd like to add a context menu with the options "Expand All" and "Collapse All" which will collapse/expand all the groups. The event handlers for this menu item click events are being handled in the window's code-behind.

The problem is my context menu is applied to the expander and thus inherited by all its children which includes the <ItemsPresenter/> and this all rows/cells.

I want to only apply the context menu to the grouped header itself. This is achievable if it's applied to the innards (like the StackPanel in the example below) but in that case, the context menu isn't accessible for the entire Header line, only on the StackPanel contents/text.

I'm planning on using a different context menu for the items themselves (add/edit etc) and have a Collapse/Expand context menu only apply to the group header. Is this achievable?

        <DataGrid Name="dgData" ItemsSource="{Binding MyItems}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False">

            <DataGrid.Resources>

                <!--GroupHeader Text-->
                <Style x:Key="gridGroupTextStyle" TargetType="TextBlock">
                    <Setter Property="FontSize" Value="12"/>
                    <Setter Property="FontWeight" Value="Bold"/>
                </Style>

                <!--GroupHeader ContextMenu-->
                <ContextMenu x:Key="cm_columnHeaderMenu">
                    <MenuItem Name="mi_ExpandAll" Header="Expand groups"/>
                    <MenuItem Name="mi_CollapseAll" Header="Collapse groups"/>
                </ContextMenu>

                <!--Grouping style-->
                <Style x:Key="filesGroupHeaderStyle" TargetType="{x:Type GroupItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type GroupItem}">
                                <Expander x:Name="exp" IsExpanded="true">
                                    <Expander.Style>
                                        <Style TargetType="{x:Type Expander}">
                                            <Setter Property="ContextMenu" Value="{StaticResource cm_columnHeaderMenu}"/>
                                        </Style>
                                    </Expander.Style>
                                    <Expander.Header>
                                        <StackPanel Orientation="Horizontal">
                                            <TextBlock Text="{Binding Name}" Style="{StaticResource gridGroupTextStyle}"/>
                                            <TextBlock Text=" (" Style="{StaticResource gridGroupTextStyle}"/>
                                            <TextBlock Text="{Binding ItemCount}" Style="{StaticResource gridGroupTextStyle}"/>
                                            <TextBlock Text=")" Style="{StaticResource gridGroupTextStyle}"/>
                                        </StackPanel>
                                    </Expander.Header>
                                    <ItemsPresenter/>
                                </Expander>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </DataGrid.Resources>

            <DataGrid.GroupStyle>
                <GroupStyle ContainerStyle="{StaticResource filesGroupHeaderStyle}">
                    <GroupStyle.Panel>
                        <ItemsPanelTemplate>
                            <DataGridRowsPresenter/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                </GroupStyle>
            </DataGrid.GroupStyle>

            <DataGrid.Columns>
                <DataGridTextColumn Header="Column 1" Binding="{Binding Prop1}" IsReadOnly="True"/>
                <DataGridTextColumn Header="Column 2" Binding="{Binding Prop2}" IsReadOnly="True"/>
                <DataGridTextColumn Header="Column 3" Binding="{Binding Prop3}" IsReadOnly="True"/>
            </DataGrid.Columns>                

        </DataGrid>
1

1 Answers

2
votes

If i understand you right, you want to click anywhere in your StackPanel and get the ContextMenu to work like this:

ContextMenu

If yes, here we go in code:

 <Expander Grid.Row="1">
            <Expander.Style>
                <Style TargetType="{x:Type Expander}">
                    <Setter Property="ContextMenu" Value="{x:Null}"/>
                </Style>
            </Expander.Style>
            <Expander.Header>
                <StackPanel Orientation="Horizontal" Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}}" Background="Pink" HorizontalAlignment="Stretch">
                    <StackPanel.Style>
                        <Style TargetType="StackPanel" >
                            <Setter Property="ContextMenu"  Value="{StaticResource cm_columnHeaderMenu}"></Setter>
                        </Style>
                    </StackPanel.Style>
                    <TextBlock Text="Hello" />
                    <TextBlock Text=" (" />
                    <TextBlock Text="world" />
                    <TextBlock Text=")" />
                    <TextBlock Text="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}}" />
                </StackPanel>
            </Expander.Header>
            <TextBlock Text="Dummy"></TextBlock>
        </Expander>

What did i do?

  • First, bind your Stackpanel-Width to actual width of Expander
  • Second, make the Stackpanel stretch.

Hope this helps