2009-02-26 146 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; 
     } 
    }