我有一个定义上下文菜单的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 { } }
}
}
}
答案 0 :(得分:2)
Windows shell行为解决方案:
在这个解决方案中,我使用两个程序集:
将以下命名空间添加到窗口:
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;
}