How do I create reusable, context-sensitive XAML code blocks?

In the following WPF, ListView

I created two ContextMenus

, one for the ListView itself and one for each specific list item. It looks something like this:

<ListView ItemsSource="{Binding Path=MyListData}">
    <ListView.ContextMenu>
        <ContextMenu>    <!-- menu for the entire list -->
            <MenuItem Header="New Item"/>
            <MenuItem Header="Sort by">
                <MenuItem Header="Name"/>
                <MenuItem Header="Author"/>
                <MenuItem Header="Date"/>
            </MenuItem>
        </ContextMenu>
    </ListView.ContextMenu>
    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>    <!-- menu for a specific item -->
                        <MenuItem Header="Edit"/>
                        <MenuItem Header="Remove"/>
                        <Separator/>    <!-- note how the following is basically the same as the other menu -->
                        <MenuItem Header="New Item"/>
                        <MenuItem Header="Sort by">
                            <MenuItem Header="Name"/>
                            <MenuItem Header="Author"/>
                            <MenuItem Header="Date"/>
                        </MenuItem>
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.View>
        <!-- ListView content -->
    </ListView.View>
</ListView>

      

This works, but as you can see, the first Contextmenu content is reused as part of the second. This is because I want the general options (New Item / Sort) to also be available when the user right-clicks on the item, rather than blank space. Since I plan on using this general framework more than once, I can imagine it getting pretty messy, especially when all the required command bindings are taken into account.

I tried to define DataTemplate

in <ListView.Resources>

as explained in answers to similar questions, but it doesn't work because it is not MenuItem

possible to wrap in a template non-standard . And if I include tags <ContextMenu>

in the template, there is an exception from the runtime because it ContextMenu

cannot live inside another ContextMenu

.

Is there a way to create reusable pieces of code that usually require specific parent elements? Some kind of pattern that was evaluated at compile time? All I want is more usable code.

+3


source to share


1 answer


You can do it like this:

<Window.Resources>
    <MenuItem x:Key="newItem" Header="New Item" Command="{x:Static ApplicationCommands.New}" x:Shared="False" />
    <MenuItem x:Key="sortBy" Header="Sort by" x:Shared="False">
        <MenuItem Header="Name" Click="SortByNameClicked"/>
        <MenuItem Header="Author"/>
        <MenuItem Header="Date"/>
    </MenuItem>
</Window.Resources>
<ListView ItemsSource="{Binding Path=MyListData}">
    <ListView.ContextMenu>
        <ContextMenu>
            <!-- menu for the entire list -->
            <StaticResource ResourceKey="newItem" />
            <StaticResource ResourceKey="sortBy" />
        </ContextMenu>
    </ListView.ContextMenu>
    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>
                        <!-- menu for a specific item -->
                        <MenuItem Header="Edit"/>
                        <MenuItem Header="Remove"/>
                        <Separator/>
                        <StaticResource ResourceKey="newItem" />
                        <StaticResource ResourceKey="sortBy" />
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </ListView.ItemContainerStyle>
</ListView>

      



Note x:Shared="false"

. He makes all references to this resource to create a new copy. By default, the same resource instance is reused, but this does not fit our scenario because we need different instances for different menus (otherwise it will complain that the same item cannot be a child of multiple parents).

You can define command bindings and click events (as you can see) as usual on such items.

+2


source







All Articles