2017-12-06 48 views
1

TreeViewItemに関連するメニューとサブメニューを動的に表示しようとしていますが、私は奇妙な動作を見つけました。ノードを右クリックしてメニューとサブメニューが正しく表示され、私がエスケープを押した後、ノードを右クリックすると、サブメニューが消えます。その後、サブメニューがランダムに表示されたり消えたりすることがあります。ContextMenuのWPFサブメニューが突然消えます

エラーを次のコードに減らしました(これはエラーを再現するための最小限のコードです)。コードを修正する方法を知っている人はいますか?私は何日も探していましたが、解決策が見つかりません。

ありがとうございます。

Image showing what happens..

.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."); 
    } 
} 

答えて

0

あなたは、メニューのトップレベルのリストについてのContextMenuののItemsSourceに頼っている奇妙な何かをやっていますツリーを構築するために階層的なデータテンプレートに頼っています。たぶんそれはいくつかのケースでうまくいくでしょう - 私はその戦略を試していません。しかし、以下にごデモコードの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に頼っています。

+0

ありがとうございます。あなたの答えは完璧に働いています。 私がこの構造を使用していたのは、複数のタイプのコマンドがあり、ItemsSourceへのバインディングがそれぞれ異なっているからです。あなたの答えに基づいて、私はDataTriggersを使って解決策を見つけました。 –

関連する問題