Sélectionnez TreeView Node sur un clic droit avant d’afficher ContextMenu

Je souhaite sélectionner un noeud TreeView WPF en cliquant avec le bouton droit, juste avant le menu contextuel affiché.

Pour WinForms, je pourrais utiliser un code tel que ce nœud de recherche cliqué dans le menu contextuel , quelles sont les alternatives WPF?

Selon la manière dont l’arborescence a été renseignée, les valeurs de l’expéditeur et de la source électronique peuvent varier .

L’une des solutions possibles consiste à utiliser e.OriginalSource et à trouver TreeViewItem à l’aide de VisualTreeHelper:

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

Si vous souhaitez une solution XAML uniquement, vous pouvez utiliser Interactivité de mélange.

Supposons que TreeView soit des données liées à une collection hiérarchique de modèles de vue ayant une propriété Boolean IsSelected et une propriété Ssortingng Name , ainsi qu’une collection d’éléments Children nommés Children .

                 

Il y a deux parties intéressantes:

  1. La propriété TreeViewItem.IsSelected est liée à la propriété IsSelected du modèle de vue. IsSelected propriété IsSelected du modèle de vue sur true, vous sélectionnerez le noeud correspondant dans l’arborescence.

  2. Lorsque PreviewMouseRightButtonDown déclenche sur la partie visuelle du nœud (dans cet exemple, un TextBlock ), la propriété IsSelected du modèle de vue est définie sur true. En revenant à 1. vous pouvez voir que le nœud correspondant sur lequel vous avez cliqué dans l’arborescence devient le nœud sélectionné.

Une façon d’obtenir l’interactivité de Blend dans votre projet consiste à utiliser le package NuGet Unofficial.Blend.Interactivity .

Utiliser “item.Focus ();” ne semble pas fonctionner à 100%, en utilisant “item.IsSelected = true;” Est-ce que.

Dans XAML, ajoutez un gestionnaire PreviewMouseRightButtonDown dans XAML:

     

Ensuite, gérez l’événement comme ceci:

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

En utilisant l’idée originale d’alex2k8, gérer correctement les éléments non visuels de Wieser Software Ltd, le XAML de Stefan, le IsSelected de Erlend et ma consortingbution à la création de la méthode statique Generic:

XAML:

    

Code C # derrière:

 void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { TreeViewItem treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject); if(treeViewItem != null) { treeViewItem.IsSelected = true; e.Handled = true; } } static T VisualUpwardSearch(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; } 

Modifier: le code précédent fonctionnait toujours correctement pour ce scénario, mais dans un autre scénario, VisualTreeHelper.GetParent renvoyait null lorsque LogicalTreeHelper renvoyait une valeur ainsi corrigée.

Presque à droite , mais vous devez faire attention aux éléments non visuels dans l’arborescence (comme une Run , par exemple).

 static DependencyObject VisualUpwardSearch(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; } 

Je pense que l’enregistrement d’un gestionnaire de classe devrait faire l’affaire. Il suffit d’enregistrer un gestionnaire d’événement routé sur le PreviewMouseRightButtonDownEvent de TreeViewItem dans votre fichier de code app.xaml.cs comme ceci:

 ///  /// Interaction logic for App.xaml ///  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; } } 

Une autre façon de résoudre le problème en utilisant MVVM est la commande de liaison pour un clic droit sur votre modèle de vue. Là, vous pouvez spécifier une autre logique ainsi que source.IsSelected = true . Cela utilise uniquement xmlns:i="http://schemas.microsoft.com/expression/2010/intera‌​ctivity" de System.Windows.Interactivity .

XAML pour voir:

                 

Voir le modèle:

  public ICommand TreeViewItemRigthClickCommand { get { if (_treeViewItemRigthClickCommand == null) { _treeViewItemRigthClickCommand = new RelayCommand(TreeViewItemRigthClick); } return _treeViewItemRigthClickCommand; } } private RelayCommand _treeViewItemRigthClickCommand; private void TreeViewItemRigthClick(object sourceItem) { if (sourceItem is Item) { (sourceItem as Item).IsSelected = true; } } 

Vous pouvez le sélectionner avec l’événement down souris. Cela déclenchera la sélection avant le lancement du menu contextuel.

J’avais un problème avec la sélection d’enfants avec une méthode HierarchicalDataTemplate. Si je sélectionnais l’enfant d’un nœud, il sélectionnerait d’une manière ou d’une autre le parent racine de cet enfant. J’ai découvert que l’événement MouseRightButtonDown serait appelé pour chaque niveau de l’enfant. Par exemple, si vous avez un arbre quelque chose comme ceci:

Objet 1
– Enfant 1
– Enfant 2
– Sous-élément 1
– Subitem2

Si je sélectionnais Subitem2, l’événement se déclencherait trois fois et l’élément 1 serait sélectionné. J’ai résolu cela avec un appel booléen et un appel asynchrone.

 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 SetFirstTimeToFalse() { return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return false; }); } 

C’est un peu embrouillé, mais en gros, je mets le booléen à true lors du premier passage et le réinitialise sur un autre thread en quelques secondes (3 dans ce cas). Cela signifie que le prochain passage à travers lequel il essaiera de monter dans l’arborescence sera ignoré, vous laissant avec le bon nœud sélectionné. Il semble fonctionner jusqu’à présent 🙂