WPF:如何动态生成 ContextMenu
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14783961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF: How to dynamically generate ContextMenu
提问by matori82
I have ListBox (with SelectionMode=Extended) that has multiple items and I want to to add context menu feature. The problem is how to dynamically create context menu based on some conditions. Eg. I'd like to show regular context menu if only one item is selected but to show other context menu (eg. with some new items added) when more than one item is selected. In addition, I'd like to create third kind of context menu if among the selected item is at least one that has some property set. Etc... there can be multiple conditions like these.
我有包含多个项目的 ListBox(带有 SelectionMode=Extended),我想添加上下文菜单功能。问题是如何根据某些条件动态创建上下文菜单。例如。如果仅选择一项,我想显示常规上下文菜单,但在选择多个项目时显示其他上下文菜单(例如,添加了一些新项目)。此外,如果所选项目中至少有一个具有某些属性集,我想创建第三种上下文菜单。等等......可以有多个这样的条件。
Basically I need to dynamically generate context menu right after user has right-click the mouse button but just before the menu is actually shown. Is this possible?
基本上,我需要在用户右键单击鼠标按钮后但在实际显示菜单之前立即动态生成上下文菜单。这可能吗?
采纳答案by matori82
I've found the answer to my question and it's ContextMenuOpening Event. Basically I need to handle this event and do menu adjustments according to the current application state. More details here: https://msdn.microsoft.com/en-us/library/Bb613568(v=vs.100).aspx
我找到了我的问题的答案,它是 ContextMenuOpening 事件。基本上我需要处理这个事件并根据当前的应用程序状态进行菜单调整。更多详细信息:https: //msdn.microsoft.com/en-us/library/Bb613568(v=vs.100).aspx
回答by nepdev
I am aware that this is an old question. It seems that there is a very simple answer which solves the OP's original problem in a MVVM scenario, because the ContextMenu class supports binding via the ItemsSource property.
我知道这是一个老问题。似乎有一个非常简单的答案可以解决 MVVM 场景中 OP 的原始问题,因为 ContextMenu 类支持通过 ItemsSource 属性进行绑定。
Hope it helps someone encountering this.
希望它可以帮助遇到此问题的人。
XAML
XAML
<ContextMenu ItemsSource="{Binding Path=ItemList, UpdateSourceTrigger=PropertyChanged}">
</ContextMenu>
In the ViewModel, you can modify the "ItemList" property dynamically according to the current application state.
在ViewModel中,可以根据当前应用状态动态修改“ItemList”属性。
回答by Peter Karlsson
If you have a set of predefined context menus that you want to use based on specific scenarios you can always create your context menus as resources.
如果您有一组要根据特定场景使用的预定义上下文菜单,您始终可以将上下文菜单创建为资源。
<Window.Resources>
<ContextMenu x:Key="Menu1">
<MenuItem>Item1</MenuItem>
</ContextMenu>
<ContextMenu x:Key="Menu2">
<MenuItem>Item1</MenuItem>
<MenuItem>Item2</MenuItem>
</ContextMenu>
</Window.Resources>
And then create data triggers on your ListBox
to set the ContextMenu
to use, rather than what I have done below I would suggest binding to properties on your view model or code behind for this as it might get very messy in xaml.
The implementation here checks to see if only one item is selected and in that case switches to Menu1
然后在您的数据触发器上创建数据触发器ListBox
以设置ContextMenu
使用,而不是我在下面所做的,我建议绑定到您的视图模型上的属性或背后的代码,因为它可能会在 xaml 中变得非常混乱。这里的实现检查是否只选择了一个项目,在这种情况下切换到 Menu1
<ListBox x:Name="mylist" SelectionMode="Multiple" ContextMenu="{StaticResource Menu2}" >
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedItems.Count, RelativeSource={RelativeSource Self}}" Value="1" >
<Setter Property="ContextMenu" Value="{StaticResource ResourceKey=Menu1}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If the selection for which context menu to show is only of concern to the view I would suggest handling it in code behind.
如果选择显示哪个上下文菜单只与视图有关,我建议在后面的代码中处理它。
public partial class MainWindow : Window
{
public MainWindow()
{
// Hook up any events that might influence which menu to show
mylist.SelectionChanged += listSelectionChanged;
InitializeComponent();
}
private void listSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBox = sender as ListBox;
if (listBox == null)
return; // Or throw something, hard
ContextMenu menuToUse;
// Logic for selecting which menu to use goes here
listBox.ContextMenu = menuToUse;
}
}
While if the ViewModel does have some interest in which menu to show (doesn't sound like it but it's hard to tell without knowing the full context) you could expose some properties that let you decide in the ViewModel which ContextMenu
to show. Although rather than individual Boolean properties you'd most likely want to create a class that makes sure that only one of the Booleans is true at any given time.
如果 ViewModel 确实对显示哪个菜单感兴趣(听起来不像,但在不知道完整上下文的情况下很难判断),您可以公开一些属性,让您决定在 ViewModel 中ContextMenu
显示哪个菜单。尽管您最有可能希望创建一个类来确保在任何给定时间只有一个布尔值为真,而不是单个布尔属性。
public class MyViewModel : INotifyPropertyChanged
{
public MyViewModel()
{
SelectedItems = new ObservableCollection<string>();
SelectedItems.CollectionChanged += SelectedItemsChanged;
}
private void SelectedItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Logic to see which ShowMenuX property to set to true goes here
}
public ObservableCollection<string> SelectedItems { get; set; }
private bool _showMenu1 = false;
public bool ShowMenu1
{
get { return _showMenu1; }
set
{
_showMenu1 = value;
RaisePropertyChanged("ShowMenu1");
}
}
// INotifyPropertyChanged implementation goes here
}