除非按下shift,否则隐藏某些XAML定义的上下文菜单项

时间:2012-10-25 14:54:24

标签: c# wpf xaml contextmenu

我有一个定义上下文菜单的DataTemplate:

<DataTemplate>
    <TextBlock>
        <TextBlock.ContextMenu>
            <ContextMenu>
                <MenuItem Command="{Binding SendToRecycleBin}" Header="Delete">
            </ContextMenu>
        </TextBlock.ContextMenu>
    </TextBlock>
</DataTemplate>

我想在上下文菜单中添加另一个菜单项,只有当用户在打开上下文菜单时按住Shift键才会显示,只使用XAML(可能会创建一个新的附加属性App.PowerUserOnly?):

<MenuItem Command="{Binding Delete}" Header="Permanently Delete"
                                     local:App.PowerUserOnly="true">

这可以仅在XAML中完成(如果是这样,怎么做?),还是必须使用代码?

编辑: 在打开上下文菜单时按住Shift时,Windows shell还会显示高级选项。我试图模仿这种行为。例如,应用程序的一个高级选项是以不同的用户身份运行它。

我简化了我的代码以帮助测试人们的建议。该项目在VS2010中使用默认的WPF应用程序创建,名为ShiftContextMenu。 App.xaml和App.xaml.cs文件未经修改。

MainWindow.xaml:

<Window x:Class="ShiftContextMenu.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="DummyItemTemplate">
            <TextBlock Text="{Binding Name}">
                <TextBlock.ContextMenu>
                    <ContextMenu>
                        <MenuItem Command="{Binding SendToRecycleBin}" Header="Delete" />
                        <MenuItem Command="{Binding Delete}" Header="Permanently Delete" />
                    </ContextMenu>
                </TextBlock.ContextMenu>
            </TextBlock>
        </DataTemplate>
    </Window.Resources>
    <TreeView Name="tvMain" ItemTemplate="{StaticResource DummyItemTemplate}" ItemsSource="{Binding DummyItems}" />
</Window>

MainWindow.xaml.cs:

using System.Collections.Generic;
using System.Windows;
using System.Collections.ObjectModel;

namespace ShiftContextMenu
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            DummyItem[] dummyItems = new DummyItem[] {
                new DummyItem("First"),
                new DummyItem("Second"),
                new DummyItem("Third")
            };
            DummyItems = new ReadOnlyCollection<DummyItem>(new List<DummyItem>(dummyItems));
            this.DataContext = this;
            InitializeComponent();
        }

        public ReadOnlyCollection<DummyItem> DummyItems { get; protected set; }
    }
}

ViewModelBase.cs:

using System.ComponentModel;

namespace ShiftContextMenu
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        protected PropertyChangedEventHandler _propertyChangedEvent;

        protected void SendPropertyChanged(string propertyName)
        {
            if (_propertyChangedEvent != null)
            {
                _propertyChangedEvent(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged
        {
            add
            {
                _propertyChangedEvent += value;
            }
            remove
            {
                _propertyChangedEvent -= value;
            }
        }
    }
}

DummyItem.cs:

using System;
using System.Windows.Input;
using System.Windows;

namespace ShiftContextMenu
{
    public class DummyItem : ViewModelBase
    {
        public string Name { get; protected set; }

        public DummyItem(string name)
        {
            Name = name;
            _sendToRecycleBinCommand = new SendToRecycleBinCommand();
            _deleteCommand = new DeleteCommand();
        }

        protected SendToRecycleBinCommand _sendToRecycleBinCommand;
        protected DeleteCommand _deleteCommand;

        public ICommand SendToRecycleBin { get { return _sendToRecycleBinCommand; } }
        public ICommand Delete { get { return _deleteCommand; } }

        protected class SendToRecycleBinCommand : ICommand
        {
            public void Execute(object parameter)
            {
                MessageBox.Show("Send To Recycle Bin");
            }

            public bool CanExecute(object parameter)
            {
                return true;
            }

            public event EventHandler CanExecuteChanged { add { } remove { } }
        }

        protected class DeleteCommand : ICommand
        {
            public void Execute(object parameter)
            {
                MessageBox.Show("Permanently Delete");
            }

            public bool CanExecute(object parameter)
            {
                return true;
            }

            public event EventHandler CanExecuteChanged { add { } remove { } }
        }
    }
}

2 个答案:

答案 0 :(得分:2)

Windows shell行为解决方案:

在这个解决方案中,我使用两个程序集:

  1. System.Windows.Interactivity
  2. System.Windows.Forms的
  3. 将以下命名空间添加到窗口:

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    

    DataTemplate应如下所示:

    <DataTemplate x:Key="DummyItemTemplate">
                <TextBlock Text="{Binding Name}">
                    <TextBlock.ContextMenu>
                        <ContextMenu> 
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Opened">
                                    <i:InvokeCommandAction Command="{Binding ShowMoreOptions}" />
                                </i:EventTrigger>
                                <i:EventTrigger EventName="Closed">
                                    <i:InvokeCommandAction Command="{Binding HideMoreOptions}" />
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                            <MenuItem Command="{Binding SendToRecycleBin}" Header="Delete" />
                            <MenuItem Command="{Binding Delete}" Header="Permanently Delete">
                            <MenuItem.Style>
                                <Style TargetType="MenuItem">
                                    <Setter Property="Visibility" Value="Collapsed" />
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding IsVisibleDelete}" Value="True">
                                            <Setter Property="Visibility" Value="Visible" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </MenuItem.Style>    
                        </MenuItem>
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>
            </DataTemplate>
    

    DummyItem类中,您必须添加两个命令和一个属性:

    private bool _isVisibleDelete;
    public bool IsVisibleDelete
    {
        get { return _isVisibleDelete; }
        set { _isVisibleDelete = value; SendPropertyChanged("IsVisibleDelete"); }
    }
    
    public ICommand ShowMoreOptions { get; private set; }
    private void OnShowMoreOptions()
    {
        if (System.Windows.Forms.Control.ModifierKeys == System.Windows.Forms.Keys.Shift)
            IsVisibleDelete = true;
    }
    
    public ICommand HideMoreOptions { get; private set; }
    private void OnHideMoreOptions()
    {
        IsVisibleDelete = false;
    }
    

    在我的示例中,我使用DelegateCommand程序集中的Microsoft.Practices.Prism

    所以在ctor DummyItem我有:

     ShowMoreOptions = new DelegateCommand(this.OnShowMoreOptions);
     HideMoreOptions = new DelegateCommand(this.OnHideMoreOptions);
    

    第二种解决方案允许您动态更改此菜单:

    你可以尝试这样的事情:

    XAML文件:

    <TextBlock>
                <TextBlock.ContextMenu>
                    <ContextMenu>
                        <ContextMenu.InputBindings>
                            <KeyBinding Modifiers="Shift" Key="Shift" Command="{Binding ShowMoreOptions}" />
                        </ContextMenu.InputBindings>
                        <MenuItem Command="{Binding SendToRecycleBin}" Header="Delete" />
                        <MenuItem Command="{Binding Delete}" Header="Permanently Delete">
                            <MenuItem.Style>
                                <Style TargetType="MenuItem">
                                    <Setter Property="Visibility" Value="Collapsed" />
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding IsVisibleDelete}" Value="True">
                                            <Setter Property="Visibility" Value="Visible" />
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </MenuItem.Style>    
                        </MenuItem>
                    </ContextMenu>
                </TextBlock.ContextMenu>
            </TextBlock>
    

    在ViewModel类中,您应该添加属性和命令来更改MenyItem可见性属性:

    private bool _isVisibleDelete;
    public bool IsVisibleDelete
    {
        get { return _isVisibleDelete; }
        set { _isVisibleDelete = value; RaisePropertyChanged(() => IsVisibleDelete); }
    }
    
    public ICommand ShowMoreOptions { get; private set; }
    private void OnShowMoreOptions()
    {
        IsVisibleDelete = !IsVisibleDelete;
    }
    

答案 1 :(得分:2)

kmatyaszek的回答有效,但我不想修改我的ViewModel。所以我最终创建了我在问题中提出的附加依赖属性:

public static readonly DependencyProperty PowerUserOnlyProperty =
    DependencyProperty.RegisterAttached(
        "PowerUserOnly", 
        typeof(bool), 
        typeof(App), 
        new UIPropertyMetadata(false, new PropertyChangedCallback(PUOChanged)));

public static bool GetPowerUserOnly(MenuItem obj)
{
    return (bool)obj.GetValue(PowerUserOnlyProperty);
}

public static void SetPowerUserOnly(MenuItem obj, bool value)
{
    obj.SetValue(PowerUserOnlyProperty, value);
}

public static void PUOChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    MenuItem menuItem = sender as MenuItem;
    if (menuItem == null) return;

    bool value = (bool)e.NewValue;
    if (!value) return;

    new PowerUserOnlyHelper(menuItem);
}

public class PowerUserOnlyHelper
{
    public MenuItem Item { get; protected set; }

    public PowerUserOnlyHelper(MenuItem menuItem)
    {
        Item = menuItem;

        ContextMenu parent = VisualUpwardSearch<ContextMenu>(menuItem);
        if (parent != null)
        {
            parent.Opened += new RoutedEventHandler(OnContextMenuOpened);
        }
    }

    protected void OnContextMenuOpened(object sender, RoutedEventArgs e)
    {
        Visibility v;
        if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
        {
            v = Visibility.Visible;
        }
        else v = Visibility.Collapsed;

        Item.Visibility = v;
    }
}

public static T VisualUpwardSearch<T>(DependencyObject source)
    where T : DependencyObject
{
    DependencyObject returnVal = source;
    DependencyObject tempReturnVal;

    while (returnVal != null && !(returnVal is T))
    {
        tempReturnVal = null;
        if (returnVal is Visual || returnVal is Visual3D)
        {
            tempReturnVal = VisualTreeHelper.GetParent(returnVal);
        }
        if (tempReturnVal == null)
        {
            returnVal = LogicalTreeHelper.GetParent(returnVal);
        }
        else
        {
            returnVal = tempReturnVal;
        }
    }

    return returnVal as T;
}