WPF:如何动态生成上下文菜单

20

我有一个ListBox(SelectionMode=Extended),其中有多个项目,我想添加上下文菜单功能。问题是如何根据某些条件动态创建上下文菜单。例如,如果只选中了一个项目,我想显示常规上下文菜单,但如果选中了多个项目,则显示其他上下文菜单(例如,添加了一些新项目)。此外,如果所选项目中至少有一个设置了某个属性,则我想创建第三种上下文菜单等等......可能会有多个这样的条件。

基本上,我需要在用户右键单击鼠标按钮后但实际显示菜单之前动态生成上下文菜单。这是否可能?

3个回答

14

我知道这是一个老问题。看起来有一个非常简单的答案可以解决在MVVM场景下OP最初的问题,因为ContextMenu类支持通过ItemsSource属性进行绑定。

希望对那些遇到这个问题的人有所帮助。

XAML

      <ContextMenu ItemsSource="{Binding Path=ItemList, UpdateSourceTrigger=PropertyChanged}">
      </ContextMenu>

在ViewModel中,您可以根据当前应用程序状态动态修改“ItemList”属性。


3
@Julien,看看这个答案:链接 - Sinatr

9

1
链接现在无效 - aaron
3
以防文档再次被删除,标题为“如何处理ContextMenuOpening事件”,因此您可以搜索它。 - Rolfi

5

如果您有一组预定义的上下文菜单,想要根据特定情境使用它们,您可以将上下文菜单创建为资源。

<Window.Resources>
    <ContextMenu x:Key="Menu1">
        <MenuItem>Item1</MenuItem>
    </ContextMenu>
    <ContextMenu x:Key="Menu2">
        <MenuItem>Item1</MenuItem>
        <MenuItem>Item2</MenuItem>
    </ContextMenu>
</Window.Resources>

然后在您的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>

如果只关心要显示哪个上下文菜单的选择,则建议在代码后台处理。
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;
    }
}

如果 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
}

非常有帮助,谢谢。但是并没有完全解决我的问题。我需要在代码中动态创建上下文菜单。这是因为某些影响上下文菜单的条件只能从代码中进行检查/计算。 - matori82

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