Come selezionare un elemento in un TreeView WPF al livello di programmazione?

Come è ansible selezionare in modo programmatico un elemento in un TreeView WPF? Il modello ItemsControl sembra prevenirlo.

È una vera sofferenza per qualche strana ragione, devi usare ContainerFromItem per ottenere il contenitore, quindi invocare il metodo select.

 // selectedItemObject is not a TreeViewItem, but an item from the collection that // populated the TreeView. var tvi = treeView.ItemContainerGenerator.ContainerFromItem(selectedItemObject) as TreeViewItem; if (tvi != null) { tvi.IsSelected = true; } 

C’era una volta un post sul blog su come farlo qui , ma il link è morto ora.

Per coloro che stanno ancora cercando la giusta soluzione a questo problema, ecco quello sotto. Ho trovato questo nei commenti all’articolo di Project Code “WPF TreeView Selection” http://www.codeproject.com/KB/WPF/TreeView_SelectionWPF.aspx di DaWanderer. È stato pubblicato da Kenrae il 25 novembre 2008. Questo ha funzionato alla grande per me. Grazie Kenrae!

Ecco il suo post:

Invece di camminare sull’albero, il proprio object dati deve avere la proprietà IsSelected (e io consiglio anche la proprietà IsExpanded). Definire uno stile per TreeViewItems dell’albero utilizzando la proprietà ItemContainerStyle in TreeView che associa tali proprietà da TreeViewItem agli oggetti dati. Qualcosa come questo:

   

È necessario ottenere TreeViewItem e quindi impostare IsSelected su true .

Non è così semplice come sembra, il collegamento fornito da Steven ha una soluzione pubblicata nel 2008, che può ancora funzionare ma non si occupa di TreeView virtuali. Inoltre molti altri problemi sono menzionati nei commenti di quell’articolo. Nessun reato, ma sono anche bloccato con lo stesso problema e non riesco a trovare una soluzione perfetta. Ecco i link ad alcuni degli articoli / post che mi hanno aiutato molto-

Come posso espandere gli elementi in un TreeView? – Parte III: http://bea.stollnitz.com/blog/?p=59

Selezione a livello di codice di un object in un TreeView: http://blog.quantumbitdesigns.com/2008/07/22/programmatically-selecting-an-item-in-a-treeview/#respond

TreeView, TreeViewItem e IsSelected: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7e368b93-f509-4cd6-88e7-561e8d3246ae/

Sono riuscito con questo codice:

 public static TreeViewItem FindTviFromObjectRecursive(ItemsControl ic, object o) { //Search for the object model in first level children (recursively) TreeViewItem tvi = ic.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem; if (tvi != null) return tvi; //Loop through user object models foreach (object i in ic.Items) { //Get the TreeViewItem associated with the iterated object model TreeViewItem tvi2 = ic.ItemContainerGenerator.ContainerFromItem(i) as TreeViewItem; tvi = FindTviFromObjectRecursive(tvi2, o); if (tvi != null) return tvi; } return null; } 

Uso:

 var tvi = FindTviFromObjectRecursive(TheTreeView, TheModel); if (tvi != null) tvi.IsSelected = true; 

Ho scritto un metodo di estensione:

 using System.Windows.Controls; namespace Extensions { public static class TreeViewEx { ///  /// Select specified item in a TreeView ///  public static void SelectItem(this TreeView treeView, object item) { var tvItem = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem; if (tvItem != null) { tvItem.IsSelected = true; } } } } 

Che posso usare in questo modo:

 if (_items.Count > 0) _treeView.SelectItem(_items[0]); 

Prova con questo

  ///  /// Selects the tree view item. ///  /// The collection. /// The value. ///  private TreeViewItem SelectTreeViewItem(ItemCollection Collection, String Value) { if (Collection == null) return null; foreach(TreeViewItem Item in Collection) { /// Find in current if (Item.Header.Equals(Value)) { Item.IsSelected = true; return Item; } /// Find in Childs if (Item.Items != null) { TreeViewItem childItem = this.SelectTreeViewItem(Item.Items, Value); if (childItem != null) { Item.IsExpanded = true; return childItem; } } } return null; } 

Riferimento: http://amastaneh.blogspot.com/2011/06/wpf-selectedvalue-for-treeview.html

Ho solo pensato di inserire la soluzione con cui sono andato, nel caso questo possa aiutare qualcuno. Nota che il modo migliore per farlo è usare una proprietà legata come “IsSelected” come per la risposta di kuninl, ma nel mio caso si trattava di un’applicazione legacy che non seguiva MVVM, così ho finito con il sotto.

 private void ChangeSessionSelection() { foreach (SessionContainer item in this.treeActiveSessions.Items) { var treeviewItem = this.treeActiveSessions.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem; if (item.Session == this.selectedSession.Session) { treeviewItem.IsSelected = true; treeviewItem.IsExpanded = true; } else { treeviewItem.IsSelected = false; treeviewItem.IsExpanded = false; } } } 

Ciò che fa è selezionare ed espandere la voce treeview nell’interfaccia utente che rappresenta il dataitem selezionato nel codice sottostante. Lo scopo di questo era quello di avere il cambio di selezione nella vista ad albero quando la selezione degli utenti cambiava in un controllo di oggetti nella stessa finestra.

Se si desidera selezionare l’elemento che si trova con figli di figlio, è ansible ricorsare l’utente per farlo.

 public bool Select(TreeViewItem item, object select) // recursive function to set item selection in treeview { if (item == null) return false; TreeViewItem child = item.ItemContainerGenerator.ContainerFromItem(select) as TreeViewItem; if (child != null) { child.IsSelected = true; return true; } foreach (object c in item.Items) { bool result = Select(item.ItemContainerGenerator.ContainerFromItem(c) as TreeViewItem, select); if (result == true) return true; } return false; } 

Ho creato un metodo VisualTreeExt.GetDescendants che restituisce una raccolta enumerabile di elementi che corrispondono al tipo specificato:

 public static class VisualTreeExt { public static IEnumerable GetDescendants(DependencyObject parent) where T : DependencyObject { var count = VisualTreeHelper.GetChildrenCount(parent); for (var i = 0; i < count; ++i) { // Obtain the child var child = VisualTreeHelper.GetChild(parent, i); if (child is T) yield return (T)child; // Return all the descendant children foreach (var subItem in GetDescendants(child)) yield return subItem; } } } 

Quando chiedi VisualTreeHelperExt.GetDescendants(MyAmazingTreeView) otterrai tutti i childViewView. Puoi selezionare un valore particolare usando la seguente parte di codice:

 var treeViewItem = VisualTreeExt.GetDescendants(MyTreeView).FirstOrDefault(tvi => tvi.DataContext == newValue); if (treeViewItem != null) treeViewItem.IsSelected = true; 

È una soluzione un po ‘sporca (e probabilmente non è la più efficiente) e non funzionerà se stai usando un TreeView virtualizzato, perché dipende dall’esistenza degli elementi visivi reali. Ma funziona per la mia situazione …

Sì … lo so da molti anni da quando la domanda è stata posta ma … ancora nessuna soluzione rapida a questo problema .. e quindi:

Quanto segue farà ciò che l’OP ha chiesto.

Quello che ho praticamente fatto è leggere tutte le risposte in questa pagina e seguire tutti i link rilevanti per creare una soluzione una volta per tutte a questo problema irritante.

Benefici:

  • Supporta anche Virtualizzazione TreeView.
  • Utilizza la tecnica del comportamento, quindi XAML è molto semplice.
  • Aggiunge una proprietà dependance per consentire il binding all’elemento TreeView selezionato.

Questa parte è l’unico codice che devi copiare, le altre parti servono solo per completare un esempio.

 public static class TreeViewSelectedItemExBehavior { private static List isRegisteredToSelectionChanged = new List(); public static readonly DependencyProperty SelectedItemExProperty = DependencyProperty.RegisterAttached("SelectedItemEx", typeof(object), typeof(TreeViewSelectedItemExBehavior), new FrameworkPropertyMetadata(new object(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemExChanged, null)); #region SelectedItemEx public static object GetSelectedItemEx(TreeView target) { return target.GetValue(SelectedItemExProperty); } public static void SetSelectedItemEx(TreeView target, object value) { target.SetValue(SelectedItemExProperty, value); var treeViewItemToSelect = GetTreeViewItem(target, value); if (treeViewItemToSelect == null) { if (target.SelectedItem == null) return; var treeViewItemToUnSelect = GetTreeViewItem(target, target.SelectedItem); treeViewItemToUnSelect.IsSelected = false; } else treeViewItemToSelect.IsSelected = true; } public static void OnSelectedItemExChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { var treeView = depObj as TreeView; if (treeView == null) return; if (!isRegisteredToSelectionChanged.Contains(treeView)) { treeView.SelectedItemChanged += TreeView_SelectedItemChanged; isRegisteredToSelectionChanged.Add(treeView); } } #endregion private static void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { var treeView = (TreeView)sender; SetSelectedItemEx(treeView, e.NewValue); } #region Helper Structures & Methods public class MyVirtualizingStackPanel : VirtualizingStackPanel { ///  /// Publically expose BringIndexIntoView. ///  public void BringIntoView(int index) { BringIndexIntoView(index); } } /// Recursively search for an item in this subtree. /// The parent ItemsControl. This can be a TreeView or a TreeViewItem. /// The item to search for. /// The TreeViewItem that contains the specified item. private static TreeViewItem GetTreeViewItem(ItemsControl container, object item) { if (container != null) { if (container.DataContext == item) { return container as TreeViewItem; } // Expand the current container if (container is TreeViewItem && !((TreeViewItem)container).IsExpanded) { container.SetValue(TreeViewItem.IsExpandedProperty, true); } // Try to generate the ItemsPresenter and the ItemsPanel. // by calling ApplyTemplate. Note that in the // virtualizing case even if the item is marked // expanded we still need to do this step in order to // regenerate the visuals because they may have been virtualized away. container.ApplyTemplate(); ItemsPresenter itemsPresenter = (ItemsPresenter)container.Template.FindName("ItemsHost", container); if (itemsPresenter != null) { itemsPresenter.ApplyTemplate(); } else { // The Tree template has not named the ItemsPresenter, // so walk the descendents and find the child. itemsPresenter = FindVisualChild(container); if (itemsPresenter == null) { container.UpdateLayout(); itemsPresenter = FindVisualChild(container); } } Panel itemsHostPanel = (Panel)VisualTreeHelper.GetChild(itemsPresenter, 0); // Ensure that the generator for this panel has been created. UIElementCollection children = itemsHostPanel.Children; MyVirtualizingStackPanel virtualizingPanel = itemsHostPanel as MyVirtualizingStackPanel; for (int i = 0, count = container.Items.Count; i < count; i++) { TreeViewItem subContainer; if (virtualizingPanel != null) { // Bring the item into view so // that the container will be generated. virtualizingPanel.BringIntoView(i); subContainer = (TreeViewItem)container.ItemContainerGenerator. ContainerFromIndex(i); } else { subContainer = (TreeViewItem)container.ItemContainerGenerator. ContainerFromIndex(i); // Bring the item into view to maintain the // same behavior as with a virtualizing panel. subContainer.BringIntoView(); } if (subContainer != null) { // Search the next level for the object. TreeViewItem resultContainer = GetTreeViewItem(subContainer, item); if (resultContainer != null) { return resultContainer; } else { // The object is not under this TreeViewItem // so collapse it. subContainer.IsExpanded = false; } } } } return null; } /// Search for an element of a certain type in the visual tree. /// The type of element to find. /// The parent element. ///  private static T FindVisualChild(Visual visual) where T : Visual { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++) { Visual child = (Visual)VisualTreeHelper.GetChild(visual, i); if (child != null) { T correctlyTyped = child as T; if (correctlyTyped != null) { return correctlyTyped; } T descendent = FindVisualChild(child); if (descendent != null) { return descendent; } } } return null; } #endregion } 

E questo è un esempio di come appare la linea TreeView in XAML:

  

L’unica cosa di cui preoccuparsi è assicurarsi che la proprietà del modello di visualizzazione che si sta per associare a SelectedItemEx non sia nullo. Ma questo non è un caso speciale. Ne ho appena parlato nel caso in cui le persone si confondessero.

 public class VmMainContainer : INotifyPropertyChanged { private object selectedItemTreeViewSs = new object(); private ObservableCollection selectedItemsTreeViewSs = new ObservableCollection(); private ObservableCollection itemsTreeViewSs = new ObservableCollection(); public object SelectedItemTreeViewSs { get { return selectedItemTreeViewSs; } set { selectedItemTreeViewSs = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemTreeViewSs))); } } public ObservableCollection SelectedItemsTreeViewSs { get { return selectedItemsTreeViewSs; } set { selectedItemsTreeViewSs = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemsTreeViewSs))); } } public ObservableCollection ItemsTreeViewSs { get { return itemsTreeViewSs; } set { itemsTreeViewSs = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemsTreeViewSs))); } } } 

E ultima cosa .. esempio di selezione programmatica: ho creato un pulsante sul mio MainWindow.xaml e dal suo gestore ..

 private void Button_Click(object sender, RoutedEventArgs e) { TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, trvwSs.Items[3]); //TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, null); } 

Spero che questo aiuti qualcuno 🙂

Puoi farlo via codice come

 if (TreeView1.Items.Count > 0) (TreeView1.Items[0] as TreeViewItem).IsSelected = true;