如果不存在任何项,则禁用WPF TreeView上下文菜单

时间:2017-01-14 15:36:04

标签: c# wpf treeview contextmenu

我正在尝试在TreeView中显示ContextMenu。无论是否选择了某个项目,某些条目都必须可用,但在我使用至少一个项目填充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>

但是,菜单项仍处于禁用状态:

Disabled context menu

在菜单栏的 File 菜单中启用了相同的命令,并且没有CanExecute属性。

即使没有项目,我如何启用上下文菜单条目?

1 个答案:

答案 0 :(得分:1)

问题是ContextMenu的DataContext(即它希望绑定New命令的位置)是树视图节点,而不是树视图本身。如果您有与节点相关的命令 - 编辑,移动,更改设置,那就太好了。

对于像添加和删除这样的泛节点的少数人来说不太好。

当它查看节点的DataContext(并且没有节点退出)时,它无法找到命令(并且它无论如何都没有意义,因为管理TreeView的对象应该是创建新项目,而不是项目本身。

解决方案是绑定一个New命令,该命令不在项目的DataContext中,而是TreeView。处理与ContextMenu的数据绑定是令人沮丧的...因为它与窗口的其余部分不在同一个视觉树中,它经常令人沮丧。

解决方案是引用上下文菜单的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.Resources>
    <local:MyCommand x:Key="MyCommand"/>
</Window.Resources>

然后引用:

<MenuItem Header="MyCommand" Command="{StaticResource MyCommand}"/>

绑定到ViewModel中的命令(即DataContext)就像在第一个示例中一样。与绑定Title的方式相同,您可以绑定到任何属性,例如ICommand。

所以对于一个观点:

<MenuItem Header="New" Command="{Binding New}"/>

View Model有一个名为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中,我们可以重新使用它并让它做不同的事情,而不是加载所有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
    });
}