2009-02-26 148 views

回答

123

根据树稀少,the sender and the e.Source values may vary的方式。

一个可能的解决方案是使用VisualTreeHelper使用e.OriginalSource找到树型视图:

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

感谢堆解决方案Alex2k8 – 2011-03-09 05:19:40

+0

是TreeView或TreeViewItem的这个事件吗? – 2012-11-20 10:24:57

+1

一个任何想法如何取消选择一切,如果右键点击是在一个空的位置? – 2012-11-21 02:42:11

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> 

然后处理这样的事件:

private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseEventArgs e) 
    { 
     TreeViewItem item = sender as TreeViewItem; 
     if (item != null) 
     { 
      item.Focus(); 
      e.Handled = true; 
     } 
    } 
+2

预期它不工作,我总是得到根元素作为发件人。我找到了一个类似的解决方案http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/25e113a5-6f52-4c25-974f-d58b9b689f62/添加此方法的事件处理程序按预期工作。你的代码接受它的任何改变? :-) – alex2k8 2009-02-26 22:23:49

+0

它显然取决于你如何填充树视图。我发布的代码的作品,因为这是我在我的一个工具中使用的确切代码。 – Stefan 2009-02-27 07:27:47

+0

注意,如果您在此处设置调试点,则可以查看发件人的类型,根据设置树的方式不同,这当然会有所不同。 – Mark 2013-04-11 17:18:43

16

使用 “item.Focus();”似乎不工作100%,使用“item.IsSelected = true;”确实。

7

Almost Right,但您需要注意树中的非视觉效果(例如Run)。

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

我认为注册一个类处理程序应该做的伎俩。 只需注册就TreeViewItem的PreviewMouseRightButtonDownEvent路由事件处理程序在你的app.xaml.cs代码文件是这样的:

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

您可以在鼠标按下事件与选择它。这会在上下文菜单启动之前触发选择。

10

使用来自alex2k8的原创想法,正确处理来自Wieser Software Ltd的非视觉效果,来自Stefan的XAML,来自Erlend的IsSelected,以及我真正做出的贡献静态方法一般:

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> 

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

编辑:上面的代码总是工作对于这种情况罚款,但在另一种情况下,当LogicalTreeHelper返回一个值时,VisualTreeHelper.GetParent返回null,所以修复了这个问题。

16

如果您只想使用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> 

有两个有趣的部分:

  1. TreeViewItem.IsSelected属性绑定到该视图模型的IsSelected财产。将view-model上的IsSelected属性设置为true将选择树中的相应节点。

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

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

0

我在使用HierarchicalDataTemplate方法选择孩子时遇到问题。如果我选择了节点的子节点,它会以某种方式选择该子节点的父节点。我发现MouseRightButtonDown事件会在孩子每个级别被调用。例如,如果你有一棵树是这样的:

项目1
      - 儿童1
      - 儿童2
            - 的SubItem1
            - Subitem2

如果我选择了Subitem2,事件将会启动三次,并选择项目1。我用布尔和异步调用解决了这个问题。

private bool isFirstTime = false; 
    protected void TaskTreeView_MouseRightButtonDown(object sender, MouseButtonEventArgs e) 
    { 
     var item = sender as TreeViewItem; 
     if (item != null && isFirstTime == false) 
     { 
      item.Focus(); 
      isFirstTime = true; 
      ResetRightClickAsync(); 
     } 
    } 

    private async void ResetRightClickAsync() 
    { 
     isFirstTime = await SetFirstTimeToFalse(); 
    } 

    private async Task<bool> SetFirstTimeToFalse() 
    { 
     return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return false; }); 
    } 

感觉有点cludgy但基本上我通过布尔设置为true,在第一轮和具有它(在这种情况下,3)在几秒钟内另一个线程重置。这意味着下一个通过它将尝试向上移动树的地方会跳过,从而让您选择正确的节点。它似乎工作到目前为止:-)

1

使用MVVM解决它的另一种方法是绑定命令右键单击您的视图模型。您可以在其中指定其他逻辑以及source.IsSelected = true。 这仅使用xmlns:i="http://schemas.microsoft.com/expression/2010/intera‌​ctivity"System.Windows.Interactivity

XAML的观点:

<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"> 
      <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.TreeViewItemRigthClickCommand}" CommandParameter="{Binding}" /> 
      </i:EventTrigger> 
     </i:Interaction.Triggers> 
     </TextBlock> 
    </HierarchicalDataTemplate> 
    </TreeView.ItemTemplate> 
</TreeView> 

视图模型:

public ICommand TreeViewItemRigthClickCommand 
    { 
     get 
     { 
      if (_treeViewItemRigthClickCommand == null) 
      { 
       _treeViewItemRigthClickCommand = new RelayCommand<object>(TreeViewItemRigthClick); 
      } 
      return _treeViewItemRigthClickCommand; 
     } 
    } 
    private RelayCommand<object> _treeViewItemRigthClickCommand; 

    private void TreeViewItemRigthClick(object sourceItem) 
    { 
     if (sourceItem is Item) 
     { 
      (sourceItem as Item).IsSelected = true; 
     } 
    }