在 XAML 中的 TreeView 中未选择任何内容时禁用 TreeView 上下文菜单项

Posted

技术标签:

【中文标题】在 XAML 中的 TreeView 中未选择任何内容时禁用 TreeView 上下文菜单项【英文标题】:Disable TreeView context menu items when nothing is selected in the TreeView from XAML 【发布时间】:2014-10-21 08:30:47 【问题描述】:

如果树视图的 SelectedItem 属性为空,我正在尝试禁用树视图上下文菜单中的某些菜单项。我的期望是通过将 TreeView 的 SelectedItem 属性绑定到 MenuItem 的 IsEnabled 属性并在两者之间使用转换器来实现这一点是最简单的。

我在 WPF 方面还不是特别有经验,所以我猜我在绑定方面做错了什么。

我找到了this 解决方案,可以在打开菜单时手动蛮力 IsEnabled 属性,但这对我来说似乎不是一个理想的解决方案。如果在树视图中未选择任何内容,任何添加新菜单项的人都不会立即清楚他们需要修改后面的代码以禁用他们的新菜单项。如果我试图通过绑定(或其他一些 XAML 解决方案)实现的目标是不可能的,我将采用这种蛮力解决方案。

这是我目前所拥有的一个精简示例:

MainWindow.xaml

<Window x:Class="treeViewTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:treeViewTest="clr-namespace:treeViewTest"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <treeViewTest:NullObjectToDisabledConverter
            x:Key="NullObjectToDisabledConverter" />
    </Window.Resources>

    <Grid>
        <TreeView Name="treeView" MouseLeftButtonDown="treeView_MouseLeftButtonDown">
            <TreeViewItem Header="Parent" IsExpanded="True">
                <TreeViewItem Header="Child" />
            </TreeViewItem>
            <TreeViewItem Header="Sibling" />

            <TreeView.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Copy"
                        IsEnabled="Binding ElementName=treeView, Path=SelectedItem,
                        Converter=StaticResource NullObjectToDisabledConverter" />
                    <MenuItem Header="Paste"
                        IsEnabled="Binding ElementName=treeView, Path=SelectedItem,
                        Converter=StaticResource NullObjectToDisabledConverter" />
                </ContextMenu>
            </TreeView.ContextMenu>
        </TreeView>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;

namespace treeViewTest

    public partial class MainWindow : Window
    
        public MainWindow()
        
            InitializeComponent();

        

        private void treeView_MouseLeftButtonDown(object sender,
            MouseButtonEventArgs e)
        
            TreeViewItem node = treeView.SelectedItem as TreeViewItem;
            if (node != null)
            
                node.IsSelected = false;
            
        
    

    public class NullObjectToDisabledConverter : IValueConverter
    
        public object Convert(object value, Type targetType, object parameter,
            CultureInfo culture)
        
            return (value != null);
        

        public object ConvertBack(object value, Type targetType, object parameter,
            CultureInfo culture)
        
            throw new NotImplementedException();
        
    

我尝试在转换器中返回 false 以强制禁用菜单项,但实际上从未调用转换器。

在此先感谢您在理解为什么这不起作用方面提供任何帮助。

【问题讨论】:

使用 x:Reference 而不是 ElementName,因为 TreeView 和 ContextMenu 不在同一个可视化树中,来自 ContextMenu 的 Biniding 无法按名称定位树视图 @eranotzap 我尝试了 x:Reference,但是在运行时产生了关于循环引用的错误,所以我怀疑我在那里做错了什么。在尝试找到如何正确使用 x:Reference 时,我找到了解决方案 here。我的最终结果是将绑定更改为IsEnabled="Binding Path=PlacementTarget.SelectedItem, RelativeSource=RelativeSource AncestorType=ContextMenu, Converter=StaticResource NullObjectToDisabledConverter" 你需要禁用整个contexMenu吗?还是只是特定的菜单项? @user1064519 只是主项目中的特定菜单项,示例代码确实应该更好地代表这一事实。 【参考方案1】:

这是使用DataTrigger的解决方法

<TreeView Name="treeView">
    <TreeViewItem Header="Parent"
                  IsExpanded="True">
        <TreeViewItem Header="Child" />
    </TreeViewItem>
    <TreeViewItem Header="Sibling" />
    <TreeView.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Copy"
                      x:Name="copy">
                <MenuItem.Style>
                    <Style TargetType="MenuItem">
                        <Style.Triggers>
                            <DataTrigger Binding="Binding PlacementTarget.SelectedItem,RelativeSource=RelativeSource FindAncestor, AncestorType=ContextMenu"
                                         Value="x:Null">
                                <Setter Property="IsEnabled"
                                        Value="False" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </MenuItem.Style>
            </MenuItem>
            <MenuItem Header="Paste" />
        </ContextMenu>
    </TreeView.ContextMenu>
</TreeView>

如果没有选择项,此示例将禁用第一个菜单项。同样可以应用于其他人

试一试,看看这是不是你要找的。​​p>

【讨论】:

是的,这行得通,谢谢。我喜欢它还避免了对转换器的需要这一事实。我认为可以在一个地方定义这种样式并为每个需要它的菜单项引用它? 没错@MickWaites,您可以定义样式并在未选择任何项目时应用于要禁用的项目。

以上是关于在 XAML 中的 TreeView 中未选择任何内容时禁用 TreeView 上下文菜单项的主要内容,如果未能解决你的问题,请参考以下文章

Wpf Xaml - TreeView 分层数据模板 - 多个项目源

WPF MVVM TreeView SelectedItem

wpf如何将一个TreeView绑定到另一个TreeView

如何在 Xaml 中使用 SortDescriptions 对 TreeView 项目进行排序?

简单的嵌套 TreeView Xaml 结构?

TreeView ItemSsource 绑定到 ViewModel 不更新 xaml 控件项