如果不存在任何项目,WPF TreeView 上下文菜单将被禁用
WPF TreeView context menu is disabled if no items exist
我正在尝试在 TreeView 中显示上下文菜单。无论是否选择某个项目,某些条目都必须可用,但在我用至少一个项目填充 TreeView 之前,所有命令都被禁用:
<TreeView Name="myTreeView" Width="200px">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Command="New" IsEnabled="True" />
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
但是,菜单项仍然被禁用:
在菜单栏的 File 菜单中启用了完全相同的命令,但没有 CanExecute
属性。
如何在不存在项目的情况下启用上下文菜单条目?
问题是 ContextMenu 的 DataContext(即它要绑定 New 命令的地方)是 tree-view 节点,而不是树视图本身。如果您有与节点相关的命令 - 编辑、移动、更改设置,那就太好了。
对于少数pan-node喜欢添加和删除的人来说不太好。
当它在节点的 DataContext 中查找时(没有节点退出),它找不到命令(无论如何它都在那里没有意义,因为管理 TreeView 的对象应该正在创建新项目,而不是项目本身)。
解决方案是绑定到一个新命令,该命令不在项目的 DataContext 中,而是在 TreeView 中。使用 ContextMenu 处理 data-binding 令人沮丧...因为它与 window 的其余部分不在同一个可视化树中,因此处理起来常常令人沮丧。
一个解决方案是像这样引用上下文菜单的 PlacementTarget:
<TreeView Name="myTreeView" Width="200px">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit (This command exists in the Node's ViewModel)" Command="{Binding Edit}"/>
<MenuItem Header="New (This command exists in the Window's ViewModel)" Command="{Binding PlacementTarget.DataContext.New, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
更多问题
将命令添加为静态资源的示例(如果您在 UserControl 视图中,请将 Window 更改为 UserControl):
<Window.Resources>
<local:MyCommand x:Key="MyCommand"/>
</Window.Resources>
然后引用:
<MenuItem Header="MyCommand" Command="{StaticResource MyCommand}"/>
绑定到 ViewModel(即 DataContext)中的命令与第一个示例中的一样。以与绑定 Title
相同的方式,您可以绑定到任何 属性,例如 ICommand。
所以为了一个观点:
<MenuItem Header="New" Command="{Binding New}"/>
视图模型有一个名为 New 的 属性 NewCommand:
public NewCommand New { get; private set; }
人们经常使用它,因为他们有一个接受委托的通用 ICommand,因此他们可以配置与该 ViewModel 相关的所有操作。例如:
public class MyCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public Action<object> Action { get; set; }
public MyCommand(Action<object> action)
{
Action = Action;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
Action(parameter);
}
}
然后在 ViewModel 中,我们可以 re-use 并让它做不同的事情,而不是全部实现 ICommand 类 负载:
public MyCommand New { get; private set; }
public MyCommand Delete { get; private set; }
public MyCommand ClearAll { get; private set; }
public MyViewModelConstructor()
{
New = new MyCommand((parameter) =>
{
//Add new object
});
Delete = new MyCommand((parameter) =>
{
//Delete object
});
ClearAll = new MyCommand((parameter) =>
{
//Clear all objects
});
}
我正在尝试在 TreeView 中显示上下文菜单。无论是否选择某个项目,某些条目都必须可用,但在我用至少一个项目填充 TreeView 之前,所有命令都被禁用:
<TreeView Name="myTreeView" Width="200px">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Command="New" IsEnabled="True" />
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
但是,菜单项仍然被禁用:
在菜单栏的 File 菜单中启用了完全相同的命令,但没有 CanExecute
属性。
如何在不存在项目的情况下启用上下文菜单条目?
问题是 ContextMenu 的 DataContext(即它要绑定 New 命令的地方)是 tree-view 节点,而不是树视图本身。如果您有与节点相关的命令 - 编辑、移动、更改设置,那就太好了。
对于少数pan-node喜欢添加和删除的人来说不太好。
当它在节点的 DataContext 中查找时(没有节点退出),它找不到命令(无论如何它都在那里没有意义,因为管理 TreeView 的对象应该正在创建新项目,而不是项目本身)。
解决方案是绑定到一个新命令,该命令不在项目的 DataContext 中,而是在 TreeView 中。使用 ContextMenu 处理 data-binding 令人沮丧...因为它与 window 的其余部分不在同一个可视化树中,因此处理起来常常令人沮丧。
一个解决方案是像这样引用上下文菜单的 PlacementTarget:
<TreeView Name="myTreeView" Width="200px">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit (This command exists in the Node's ViewModel)" Command="{Binding Edit}"/>
<MenuItem Header="New (This command exists in the Window's ViewModel)" Command="{Binding PlacementTarget.DataContext.New, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
更多问题
将命令添加为静态资源的示例(如果您在 UserControl 视图中,请将 Window 更改为 UserControl):
<Window.Resources>
<local:MyCommand x:Key="MyCommand"/>
</Window.Resources>
然后引用:
<MenuItem Header="MyCommand" Command="{StaticResource MyCommand}"/>
绑定到 ViewModel(即 DataContext)中的命令与第一个示例中的一样。以与绑定 Title
相同的方式,您可以绑定到任何 属性,例如 ICommand。
所以为了一个观点:
<MenuItem Header="New" Command="{Binding New}"/>
视图模型有一个名为 New 的 属性 NewCommand:
public NewCommand New { get; private set; }
人们经常使用它,因为他们有一个接受委托的通用 ICommand,因此他们可以配置与该 ViewModel 相关的所有操作。例如:
public class MyCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public Action<object> Action { get; set; }
public MyCommand(Action<object> action)
{
Action = Action;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
Action(parameter);
}
}
然后在 ViewModel 中,我们可以 re-use 并让它做不同的事情,而不是全部实现 ICommand 类 负载:
public MyCommand New { get; private set; }
public MyCommand Delete { get; private set; }
public MyCommand ClearAll { get; private set; }
public MyViewModelConstructor()
{
New = new MyCommand((parameter) =>
{
//Add new object
});
Delete = new MyCommand((parameter) =>
{
//Delete object
});
ClearAll = new MyCommand((parameter) =>
{
//Clear all objects
});
}