3

I've setup a ListView with grouping and I would like to retrieve the GroupName when I right click on the group in MVVM. I've placed a ContextMenu on my group style, and I was trying to use the EventToCommand from System.Windows.Interactivity to get the underlying item.

Here the relevant part:

<ListView.GroupStyle>
    <GroupStyle>
        <GroupStyle.ContainerStyle>
            <Style TargetType="{x:Type GroupItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Expander IsExpanded="False" Template="{StaticResource CustomizedExpander}" Background="#FFEBEBEB" BorderThickness="0" ContextMenu="{StaticResource GroupContextMenu}">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="MouseRightButtonDown">
                                        <i:InvokeCommandAction Command="{Binding Path=OnCategorySelected}" CommandParameter="{Binding Name}"/>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>

                                <Expander.Header>
                                    <StackPanel Orientation="Horizontal">
                                        <!--N.B. The "Name" property is part of the CollectionViewSource-->
                                        <TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF767676" VerticalAlignment="Bottom" />
                                        <TextBlock Text="{Binding ItemCount}" Foreground="#FF454545" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
                                        <TextBlock Text=" item(s)" Foreground="#FF767676" FontStyle="Italic" VerticalAlignment="Bottom" />
                                    </StackPanel>
                                </Expander.Header>
                                <ItemsPresenter />
                            </Expander>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </GroupStyle.ContainerStyle>
    </GroupStyle>
</ListView.GroupStyle>

I don't know if it's the right way to do it but it seems the command is not triggered at all.

Any suggestion?

UPDATE:

The whole thing was much simpler than I thought. The interaction part was not required at all. Fixing the binding is enough to get the underlying category when the context menu is shown:

<ListView ItemsSource="{Binding Modifications}" SelectedItem="{Binding SelectedItem}">
    <ListView.Resources>
        <ContextMenu x:Key="ItemContextMenu">
            <MenuItem Header="Execute" Command="{Binding Path=DataContext.OnExecuteScript, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" Visibility="{Binding CanExecute, Converter={StaticResource BooleanToVisibilityConverter}}" />
            <MenuItem Header="Execute up to this" Command="{Binding Path=DataContext.OnExecuteScriptUpToThis, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" Visibility="{Binding CanOnlyBeExecutedSequentially, Converter={StaticResource BooleanToVisibilityConverter}}" />
            <MenuItem Header="Drop" Command="{Binding Path=DataContext.OnExecuteDrop, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Visibility="{Binding Drop, Converter={StaticResource BooleanToVisibilityConverter}}" Background="WhiteSmoke" />
            <MenuItem Header="Dump" Command="{Binding Path=DataContext.OnExecuteDump, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Visibility="{Binding CanDump, Converter={StaticResource BooleanToVisibilityConverter}}" Background="WhiteSmoke" />
        </ContextMenu>
        <ContextMenu x:Key="GroupContextMenu">
            <MenuItem Header="Dump all" Command="{Binding Path=DataContext.OnExecuteDumpAll, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" CommandParameter="{Binding Name}" Background="WhiteSmoke" />
        </ContextMenu>
    </ListView.Resources>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Type" Width="120" DisplayMemberBinding="{Binding Type}" />
            <GridViewColumn Header="Object Name" Width="Auto" DisplayMemberBinding="{Binding DisplayName}" />
            <GridViewColumn Header="" Width="50">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding Deploy}" IsHitTestVisible="False" />
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Object Name" Width="300" DisplayMemberBinding="{Binding ObjectName}" />
        </GridView>
    </ListView.View>
    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}" >
            <Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Drop}" Value="True">
                    <Setter Property="Foreground" Value="Red" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.GroupStyle>
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="ContextMenu" Value="{StaticResource GroupContextMenu}" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate>
                                <Expander IsExpanded="False" Template="{StaticResource CustomizedExpander}" Background="#FFEBEBEB" BorderThickness="0">
                                    <Expander.Header>
                                        <StackPanel Orientation="Horizontal">
                                            <!--N.B. The "Name" property is part of the CollectionViewSource-->
                                            <TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF767676" VerticalAlignment="Bottom" />
                                            <TextBlock Text="{Binding ItemCount}" Foreground="#FF454545" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
                                            <TextBlock Text=" item(s)" Foreground="#FF767676" FontStyle="Italic" VerticalAlignment="Bottom" />
                                        </StackPanel>
                                    </Expander.Header>
                                    <ItemsPresenter />
                                </Expander>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </GroupStyle.ContainerStyle>
        </GroupStyle>
    </ListView.GroupStyle>
</ListView>
Dan
  • 1,060
  • 13
  • 39
  • 1
    As far as i know its not possible to use Blend behaviors in Styles. But maybe you can use this workaround: [How to add a Blend Behavior in a Style Setter](http://stackoverflow.com/questions/1647815/how-to-add-a-blend-behavior-in-a-style-setter) – Gabsch Oct 14 '16 at 15:24
  • I've tried the solution in your link but it doesn't seems to work.. Do you know any alternative way to try to access the selected group? – Dan Oct 18 '16 at 13:56
  • Maybe you could try to `PreviewMouseRightButtonDown` as Event. You bound a `ContextMenu` and a `MouseRightButtonDown` event. If ContextMenu is called first and sets `e.Handled` the other events are never called. Maybe this works. – WPFGermany Oct 19 '16 at 05:41

1 Answers1

1

First of all, i think i've figured out why your Command isnt firing.

Since you are in an Template, the DataContext has Changed. Therefore your CommandBinding should look like this:

<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.OnCategorySelected}" CommandParameter="{Binding}"/>

Also, your CommandParameter should not be Name because you'll only get a string in the end instead the whole Object.

If you use my above code you will get the whole CollectionViewGroup instead. In said CollectionViewGroup you'll find all the Items in the group. If you are fine with getting just the Groupname you can proceed with you implementation of course.

I dont really understand why you are using a ContextMenu and what it does (Since you dont posted that code), but if you are interested in how you can display the grouped Items in such a ContextMenu, you can do it like this:

<Expander IsExpanded="False"  Background="#FFEBEBEB" BorderThickness="0" >
          <Expander.ContextMenu>
                   <ContextMenu ItemsSource="{Binding Items}"/>
          </Expander.ContextMenu>
</Expander>

Since we now know, what we have to deal with (It's still an CollectionViewGroup) we can set the Items of it as ItemsSource of the ContextMenu.

Hope this helps!

lokusking
  • 7,396
  • 13
  • 38
  • 57
  • This works like a charm!! The issue was in the binding. I have a minor issue, when the category is created during the ListView binding, the command is invoked even if I don't right click the category. Do you have any suggestion about it? – Dan Oct 19 '16 at 09:17
  • Just a note on your answer: the Name is of type object and it reflects the same type used in group creation (in my case an enum type) – Dan Oct 19 '16 at 09:31
  • Solved! The issue was in the CommandParameter="{Binding}. Binding just the "Name" made the issue disappear! I'm trying to award you the bounty but SO says I've to wait 5 hours... – Dan Oct 19 '16 at 09:33