在显示ContextMenu之前,右键单击选择TreeView节点

ale*_*2k8 94 wpf treeview contextmenu

我想在显示ContextMenu之前右键单击选择一个WPF TreeView节点.

对于WinForms,我可以使用这样的代码在上下文菜单下点击查找节点,WPF的替代方案是什么?

ale*_*2k8 127

根据树的填充方式,发送方和e.Source值可能会有所不同.

其中一个可能的解决方案是使用e.OriginalSource并使用VisualTreeHelper查找TreeViewItem:

private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    TreeViewItem treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);

    if (treeViewItem != null)
    {
        treeViewItem.Focus();
        e.Handled = true;
    }
}

static TreeViewItem VisualUpwardSearch(DependencyObject source)
{
    while (source != null && !(source is TreeViewItem))
        source = VisualTreeHelper.GetParent(source);

    return source as TreeViewItem;
}
Run Code Online (Sandbox Code Playgroud)

  • 回答Louis Rhys的问题:`if(treeViewItem == null)treeView.SelectedIndex = -1`或`treeView.SelectedItem = null`.我相信要么应该工作. (2认同)

Mar*_*age 21

如果您想要一个仅限XAML的解决方案,您可以使用Blend Interactivity.

假设TreeView数据绑定到具有Boolean属性IsSelectedString属性的视图模型的分层集合Name以及命名的子项集合Children.

<TreeView ItemsSource="{Binding Items}">
  <TreeView.ItemContainerStyle>
    <Style TargetType="TreeViewItem">
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
    </Style>
  </TreeView.ItemContainerStyle>
  <TreeView.ItemTemplate>
    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
      <TextBlock Text="{Binding Name}">
        <i:Interaction.Triggers>
          <i:EventTrigger EventName="PreviewMouseRightButtonDown">
            <ei:ChangePropertyAction PropertyName="IsSelected" Value="true" TargetObject="{Binding}"/>
          </i:EventTrigger>
        </i:Interaction.Triggers>
      </TextBlock>
    </HierarchicalDataTemplate>
  </TreeView.ItemTemplate>
</TreeView>
Run Code Online (Sandbox Code Playgroud)

有两个有趣的部分:

  1. TreeViewItem.IsSelected属性IsSelected与视图模型上的属性绑定.将IsSelectedview-model上的属性设置为true将选择树中的相应节点.

  2. PreviewMouseRightButtonDown触发节点的可视部分时(在此示例a中TextBlock)IsSelected,视图模型上的属性设置为true.回到1.您可以看到在树中单击的相应节点成为选定节点.

在项目中获得Blend Interactivity的一种方法是使用NuGet包Unofficial.Blend.Interactivity.

  • 很好的答案,谢谢!显示`i`和`ei`命名空间映射可以解决的问题以及可以找到它们的程序集会很有帮助.我假设:`xmlns:i ="http://schemas.microsoft.com/expression/ 2010/interactivity"`和`xmlns:ei ="http://schemas.microsoft.com/expression/2010/interactions"`,分别在System.Windows.Interactivity和Microsoft.Expression.Interactions程序集中找到. (2认同)

小智 16

使用"item.Focus();" 似乎没有100%工作,使用"item.IsSelected = true;" 确实.


Sea*_*all 12

使用alex2k8的原创想法,正确处理来自Wieser Software Ltd的非可视化,来自Stefan的XAML,来自Erlend的IsSelected,以及我对真正制作静态方法Generic的贡献:

XAML:

<TreeView.ItemContainerStyle> 
    <Style TargetType="{x:Type TreeViewItem}"> 
        <!-- We have to select the item which is right-clicked on --> 
        <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown"
                     Handler="TreeViewItem_PreviewMouseRightButtonDown"/> 
    </Style> 
</TreeView.ItemContainerStyle>
Run Code Online (Sandbox Code Playgroud)

C#代码背后:

void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    TreeViewItem treeViewItem = 
              VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject);

    if(treeViewItem != null)
    {
        treeViewItem.IsSelected = true;
        e.Handled = true;
    }
}

static T VisualUpwardSearch<T>(DependencyObject source) where T : DependencyObject
{
    DependencyObject returnVal = source;

    while(returnVal != null && !(returnVal is T))
    {
        DependencyObject tempReturnVal = null;
        if(returnVal is Visual || returnVal is Visual3D)
        {
            tempReturnVal = VisualTreeHelper.GetParent(returnVal);
        }
        if(tempReturnVal == null)
        {
            returnVal = LogicalTreeHelper.GetParent(returnVal);
        }
        else returnVal = tempReturnVal;
    }

    return returnVal as T;
}
Run Code Online (Sandbox Code Playgroud)

编辑:以前的代码总是适用于这种情况,但在另一种情况下,当LogicalTreeHelper返回一个值时,VisualTreeHelper.GetParent返回null,因此修复了该问题.


Ste*_*fan 11

在XAML中,在XAML中添加PreviewMouseRightButtonDown处理程序:

    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <!-- We have to select the item which is right-clicked on -->
            <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
        </Style>
    </TreeView.ItemContainerStyle>
Run Code Online (Sandbox Code Playgroud)

然后像这样处理事件:

    private void TreeViewItem_PreviewMouseRightButtonDown( object sender, MouseEventArgs e )
    {
        TreeViewItem item = sender as TreeViewItem;
        if ( item != null )
        {
            item.Focus( );
            e.Handled = true;
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 它没有按预期工作,我总是将root元素作为发送者.我找到了一个类似的解决方案http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/25e113a5-6f52-4c25-974f-d58b9b689f62/这样添加的事件处理程序按预期工作.你的代码有什么变化可以接受吗?:-) (2认同)

Ant*_*ser 7

几乎是正确的,但你需要注意树中的非视觉效果(例如Run,像a ).

static DependencyObject VisualUpwardSearch<T>(DependencyObject source) 
{
    while (source != null && source.GetType() != typeof(T))
    {
        if (source is Visual || source is Visual3D)
        {
            source = VisualTreeHelper.GetParent(source);
        }
        else
        {
            source = LogicalTreeHelper.GetParent(source);
        }
    }
    return source; 
}
Run Code Online (Sandbox Code Playgroud)


Nat*_*net 6

我认为注册一个类处理程序应该可以解决问题.只需在app.xaml.cs代码文件中的TreeViewItem的PreviewMouseRightButtonDownEvent上注册一个路由事件处理程序,如下所示:

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.PreviewMouseRightButtonDownEvent, new RoutedEventHandler(TreeViewItem_PreviewMouseRightButtonDownEvent));

        base.OnStartup(e);
    }

    private void TreeViewItem_PreviewMouseRightButtonDownEvent(object sender, RoutedEventArgs e)
    {
        (sender as TreeViewItem).IsSelected = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 内森你好.听起来代码是全局的,会影响每个TreeView.拥有仅限本地的解决方案不是更好吗?它可能会产生副作用? (2认同)