ContextMenu的WPF子菜单突然消失

时间:2017-12-06 18:30:35

标签: c# wpf contextmenu

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

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

谢谢。

Image showing what happens..

.Net framework 4.6.1和4.7

MainWindow.xaml

sess.run(x_batch)

MainWindow.xaml.cs

<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>

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

MainWindow.xaml

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;
}

IsInstanceOfTypeConverter.cs

<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>

1 个答案:

答案 0 :(得分:0)

你正在做一些奇怪的事情,你依赖于ContextMenu的ItemsSource作为菜单项的顶级列表,但后来依靠hierarchydatatemplate来构建一个树。也许它在某些情况下会起作用 - 我没有尝试过这种策略。但是,将演示代码的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的ItemsSource来创建我的子菜单项。