HierarchicalDataTemplate中的ContextMenu的Command绑定
<ContextMenu x:Key="ModeMenu">
<MenuItem Header="添加" Command="{Binding AddCommand}"
CommandParameter="{Binding ElementName=ModesTree, Path=SelectedItem}">
<MenuItem.Icon>
<Image Source="../Images/Add.png" Width="16" Height="16" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
<HierarchicalDataTemplate x:Key="ModeTemplate" DataType="{x:Type Model:ModeInfo}" ItemsSource="{Binding Path=PlanInfoList}" >
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource ModeMenu}"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<Image x:Name="imgStatus" Source="../Images/Add.png" Width="16" Height="16" />
<TextBlock Margin="2,0,0,0" VerticalAlignment="Center" FontWeight="Bold" Text="{Binding ModeName}" />
</StackPanel>
</HierarchicalDataTemplate>
如代码所示,这个MenuItem的Command并不起作用。网上有人给出的解释是:ContentMenu是作为你一个popup的窗口出现在UI上的,因此,这个Popup和原来的窗口并不是同一个VisualTree,既然不在同一个VisualTree中,则无法找到DataContext。
解决方法如下:
定义一个类CommandReference:
public class CommandReference : Freezable, ICommand, ICommandSource
{
public CommandReference()
{
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(CommandReference),
new PropertyMetadata((object)null));
public object CommandParameter
{
get
{
return (object)GetValue(CommandParameterProperty);
}
set
{
SetValue(CommandParameterProperty, value);
}
}
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register(
"CommandTarget",
typeof(IInputElement),
typeof(CommandReference),
new PropertyMetadata((IInputElement)null));
public IInputElement CommandTarget
{
get
{
return (IInputElement)GetValue(CommandTargetProperty);
}
set
{
SetValue(CommandTargetProperty, value);
}
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (Command != null)
return Command.CanExecute(CommandParameter);
return false;
}
public void Execute(object parameter)
{
Command.Execute(CommandParameter);
}
public event EventHandler CanExecuteChanged;
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
}
if (newCommand != null)
{
newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
}
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
#endregion
}
然后修改View中的代码,先插入一个CommandReference资源,将其绑定到命令,再将菜单的命令绑定到资源,如下所示:
<UserControl.Resources>
<c:CommandReference x:Key="AddModeCommandRef" Command="{Binding Path=AddModeCommand}"/>
<ContextMenu x:Key="RootMenu" >
<MenuItem Header="添加" Command="{StaticResource AddModeCommandRef}" >
<MenuItem.Icon>
<Image Source="../Images/Add.png" Width="16" Height="16" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
<HierarchicalDataTemplate x:Key="ModeTemplate" DataType="{x:Type Model:ModeInfo}" ItemsSource="{Binding Path=PlanInfoList}" >
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource ModeMenu}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"> <Image x:Name="imgStatus" Source="../Images/Add.png" Width="16" Height="16" />
<TextBlock Margin="2,0,0,0" VerticalAlignment="Center" FontWeight="Bold" Text="{Binding ModeName}" />
</StackPanel>
</HierarchicalDataTemplate>
最后在ViewModel中定义命令即可:
public ICommand AddModeCommand { get; set; }
参考文章:
http://social.msdn.microsoft.com/Forums/zh-SG/wpfzhchs/thread/13bcc6e4-d3f7-40c8-a9e8-ee35918edc14
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/36ce07fc-64de-4dc9-8012-3c4f03605c30/