2017-12-06 19 views
1

TreeViewItem과 관련된 메뉴 및 하위 메뉴를 동적으로 표시하려고하는데 이상한 동작이 나타납니다. 처음 노드를 마우스 오른쪽 버튼으로 클릭하면 메뉴와 하위 메뉴가 올바르게 표시되지만, 그런 다음 escape 키를 누른 다음 노드를 마우스 오른쪽 버튼으로 클릭하면 하위 메뉴가 사라집니다. 그 후, 때로는 부 메뉴가 무작위로 나타나거나 사라집니다.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; 
} 
0,123,178,949,223,

참고 : 덕분에 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를 사용하여 해결책을 찾았습니다. –