wpf的wpf子enus突然消失了



我正在尝试动态显示与TreeViewItem相关的菜单和子菜单,但是我发现了一种奇怪的行为:第一次右键单击节点,菜单和子菜单正确显示,然后如果我按逃生,然后重复右键单击节点上的右键,则子菜单消失。之后,有时会随机出现或消失。

我将错误减少到以下代码(这是重现错误的最小代码)。有人知道我如何纠正代码吗?我一直在寻找几天,但找不到任何解决方案。

谢谢。

图像显示发生了什么..

.NET框架4.6.1和4.7

mainwindow.xaml

<Window x:Class="WpfTester.TestWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfTester"
    mc:Ignorable="d"
    Title="Window" Height="300" Width="300">
<Grid>
    <TreeView HorizontalContentAlignment="Stretch">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type local:MyMenu}" ItemsSource="{Binding Commands}">
                <TextBlock Text="{Binding Name}" Background="Blue" />
            </HierarchicalDataTemplate>
        </TreeView.Resources>
        <TreeViewItem Header="Level 1" IsExpanded="True" ContextMenuOpening="OnContextMenuOpening">
            <TreeViewItem.ContextMenu>
                <ContextMenu>
                    <ContextMenu.ItemContainerStyle>
                        <Style TargetType="{x:Type MenuItem}">
                            <Setter Property="Command" Value="{Binding}" />
                        </Style>
                    </ContextMenu.ItemContainerStyle>
                </ContextMenu>
            </TreeViewItem.ContextMenu>
        </TreeViewItem>
    </TreeView>
</Grid>

mainwindow.xaml.cs

public partial class TestWindow : Window
{
    public TestWindow()
    {
        InitializeComponent();
    }
    private void OnContextMenuOpening(object sender, ContextMenuEventArgs args)
    {
        ((TreeViewItem)sender).ContextMenu.ItemsSource = GetCommands();
    }
    private static IEnumerable<MyMenu> GetCommands()
    {
        return new MyMenu[]
        {
            new MyMenu("Pep", new[]
            {
                new MyMenu("x"),
                new MyMenu("y"),
                new MyMenu("z"),
            }),
            new MyMenu("fuz")
        };
    }
}
public class MyMenu : ICommand
{
    public MyMenu(string name)
    {
        this.Name = name;
    }
    public MyMenu(string name, IEnumerable<MyMenu> items)
    {
        this.Name = name;
        items.ToList().ForEach(x => this.commands.Add(x));
    }
    public string Name { get; }
    public ObservableCollection<MyMenu> Commands
    {
        get { return this.commands; }
    }
    private readonly ObservableCollection<MyMenu> commands = new ObservableCollection<MyMenu>();
    public virtual bool CanExecute(object parameter)
    {
        return true;
    }
    public virtual void Execute(object parameter)
    {
    }
    public event EventHandler CanExecuteChanged;
}

注意:感谢Rowbear我找到了最好的解决方案。

mainwindow.xaml

<Window x:Class="WpfTester.TestWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfTester"
    mc:Ignorable="d"
    Title="Window" Height="300" Width="300">
<Grid>
    <TreeView HorizontalContentAlignment="Stretch">
        <TreeView.Resources>
            <local:IsInstanceOfTypeConverter x:Key="IsInstanceOfTypeConverter" />
        </TreeView.Resources>
        <TreeViewItem Header="Level 1" IsExpanded="True" ContextMenuOpening="OnContextMenuOpening">
            <TreeViewItem.ContextMenu>
                <ContextMenu>
                    <ContextMenu.ItemContainerStyle>
                        <!-- Style for MenuItem -->
                        <Style TargetType="{x:Type MenuItem}">
                            <Setter Property="Header" Value="{Binding Name}" />
                            <Setter Property="ToolTip" Value="{Binding Name}" />
                            <Setter Property="Command" Value="{Binding}" />
                            <Style.Triggers>
                                <!-- Style for MyMenu -->
                                <DataTrigger Binding="{Binding Converter={StaticResource IsInstanceOfTypeConverter},
                                                               ConverterParameter={x:Type local:MyMenu}}"
                                             Value="True">
                                    <Setter Property="ItemsSource" Value="{Binding Commands}"/>
                                    <Setter Property="Background" Value="Blue" />
                                </DataTrigger>
                                <!-- More types of menus and commands with different binding.. -->
                                <DataTrigger Binding="{Binding Converter={StaticResource IsInstanceOfTypeConverter},
                                                               ConverterParameter={x:Type local:MyMenu2}}"
                                             Value="True">
                                    <Setter Property="ItemsSource" Value="{Binding Actions}"/>
                                    <Setter Property="Background" Value="Green" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ContextMenu.ItemContainerStyle>
                </ContextMenu>
            </TreeViewItem.ContextMenu>
        </TreeViewItem>
    </TreeView>
</Grid>

isInstanceoftypeconverter.cs

/// <summary>
/// This class tests if <code>value</code> is instance of the type indicated in <code>parameter</code>.
/// </summary>
public sealed class IsInstanceOfTypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Type type = parameter as Type;
        return (type != null) && type.IsInstanceOfType(value);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException("IsInstanceOfTypeConverter can only be used for one way conversion.");
    }
}

您正在做一些奇怪的事情,而您依靠contextMenu的itemssSource作为菜单项的顶级列表,但随后依靠层次构造dataTateMplate来构建树。也许在某些情况下会起作用 - 我没有尝试过这种策略。但是,将您的演示代码的XAML更改为以下应该使您进入想要的东西:

<Grid>
    <TreeView HorizontalContentAlignment="Stretch">
        <TreeViewItem Header="Level 1" IsExpanded="True" ContextMenuOpening="OnContextMenuOpening">
            <TreeViewItem.ContextMenu>
                <ContextMenu>
                    <ContextMenu.ItemContainerStyle>
                        <Style TargetType="{x:Type MenuItem}">
                            <Setter Property="Header" Value="{Binding Name}"></Setter>
                            <Setter Property="Command" Value="{Binding}" />
                            <Setter Property="Background" Value="Blue"></Setter>
                            <Setter Property="ItemsSource" Value="{Binding Commands}"></Setter>
                        </Style>
                    </ContextMenu.ItemContainerStyle>
                </ContextMenu>
            </TreeViewItem.ContextMenu>
        </TreeViewItem>
    </TreeView>
</Grid>

在这里,我依靠Menuitem的物品来创建我的孩子菜单项。

最新更新