如何创建可重用的、与上下文无关的XAML代码块?

3
在下面的WPF ListView中,我创建了两个ContextMenus:一个用于ListView本身,另一个用于每个特定的列表项。它看起来有点像这样:
<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>

这个方法能够工作,但是你可以看到,第一个上下文菜单的内容会被作为第二个上下文菜单的一部分重复使用。这是因为我希望通用选项(新项目/排序)也可以在用户右键单击项目而不是空白区域时可用。由于我计划多次使用此通用结构,我可以想象这变得相当混乱,特别是考虑到所有必要的命令绑定。
我尝试在<ListView.Resources>中定义DataTemplate,如类似问题的答案所解释的那样,但是这行不通,因为显然独立的MenuItem不能包含在模板中。如果在模板中包含<ContextMenu>标记,则会出现运行时异常,因为ContextMenu不能存在于另一个ContextMenu中。
有没有办法创建可重用的代码片段,这些代码片段通常需要特定的父元素?某种在编译时评估的模板?我只想要更易维护的代码。
1个回答

2
你可以这样做:
<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>

请注意 x:Shared="false" 属性。它的作用是使得对于该资源的每次引用都会创建新的副本。默认情况下,会重用同一个资源实例,但这并不符合我们的场景,因为我们需要为不同的菜单使用不同的实例(否则会出现同一项不能是多个父级的子项的错误)。
您可以像往常一样在这些项目上定义命令绑定和点击事件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接