Cómo seleccionar mediante progtwigción un elemento en WPF TreeView?

¿Cómo es posible seleccionar programáticamente un elemento en WPF TreeView ? El modelo de ItemsControl parece prevenirlo.

Es un verdadero dolor por alguna extraña razón, tienes que usar ContainerFromItem para obtener el contenedor, luego invocar el método de selección.

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

Hubo una vez una entrada de blog sobre cómo hacerlo aquí , pero el enlace está muerto ahora.

Para aquellos que todavía están buscando la solución adecuada para este problema, aquí está el siguiente. Encontré este en los comentarios al artículo de Code Project “WPF TreeView Selection” http://www.codeproject.com/KB/WPF/TreeView_SelectionWPF.aspx por DaWanderer. Fue publicado por Kenrae el 25 de noviembre de 2008. Esto funcionó muy bien para mí. Gracias Kenrae!

Aquí está su publicación:

En lugar de recorrer el árbol, haga que su propio objeto de datos tenga la propiedad IsSelected (y recomiendo también la propiedad IsExpanded). Defina un estilo para TreeViewItems del árbol utilizando la propiedad ItemContainerStyle en el TreeView que vincula esas propiedades desde TreeViewItem a sus objetos de datos. Algo como esto:

   

Necesita obtener TreeViewItem y luego establecer IsSelected en true .

Esto no es tan simple como parece, el enlace provisto por Steven tiene una solución publicada en 2008, que aún puede funcionar pero no se ocupa de Virtualized TreeViews. Además, se mencionan muchos otros problemas en los comentarios de ese artículo. Sin ofensas, pero también estoy atrapado con el mismo problema y no puedo encontrar una solución perfecta. Aquí están los enlaces a algunos de los artículos / publicaciones que me ayudaron mucho

¿Cómo puedo expandir elementos en un TreeView? – Parte III: http://bea.stollnitz.com/blog/?p=59

Seleccionar mediante progtwigción un elemento en TreeView: http://blog.quantumbitdesigns.com/2008/07/22/programmatically-selecting-an-item-in-a-treeview/#respond

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

He tenido éxito con este código:

 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; 

Escribí un método de extensión:

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

Que puedo usar así:

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

Prueba con esto

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

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

Solo pensé en responder con la solución que fui, en caso de que esto pueda ayudar a cualquiera. Tenga en cuenta que la mejor manera de hacerlo es usar una propiedad enlazada como ‘IsSelected’ según la respuesta de kuninl, pero en mi caso era una aplicación heredada que no siguió a MVVM, así que terminé con la siguiente.

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

Lo que hace es seleccionar y expandir el elemento treeview en la interfaz de usuario que representa el elemento de datos seleccionado en el código subyacente. El objective de esto era hacer que la selección cambiara en la vista de árbol cuando la selección de usuarios cambiaba en un control de elementos en la misma ventana.

Si desea seleccionar el elemento ubicado dentro de hijos del niño, puede recurrir al usuario para hacer eso.

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

VisualTreeExt.GetDescendants un método VisualTreeExt.GetDescendants que devuelve una colección enumerable de elementos que coinciden con el tipo especificado:

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

Cuando solicite VisualTreeHelperExt.GetDescendants(MyAmazingTreeView) obtendrá todos los elementos VisualTreeHelperExt.GetDescendants(MyAmazingTreeView) TreeViewItem . Puede seleccionar un valor particular usando el siguiente fragmento de código:

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

Es una solución un poco sucia (y probablemente no la más eficiente) y no funcionará si está utilizando una vista de árbol virtualizada, ya que depende de la existencia de los elementos visuales reales. Pero funciona para mi situación …

Sí … Sé que pasaron muchos años desde que se hizo la pregunta, pero … todavía no hay una solución rápida a este problema … y entonces:

Lo siguiente hará lo que el OP solicite.

Lo que básicamente hice fue leer todas las respuestas en esta página y seguir todos los enlaces relevantes para crear una solución definitiva a este irritante problema.

Beneficios:

  • Es compatible con Virtualizing TreeView también.
  • Se usa la técnica del comportamiento, por lo que XAML es muy fácil.
  • Agrega una propiedad dependiente para permitir el enlace al elemento TreeView seleccionado.

Esta parte es el único código que necesita copiar, las otras partes son solo para ayudar a completar un ejemplo.

 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 } 

Y este es un ejemplo de cómo se ve la línea TreeView en XAML:

  

Lo único que debe preocuparse es asegurarse de que su propiedad de modelo de vista que está a punto de vincular con SelectedItemEx no sea nula. Pero ese no es un caso especial … Solo lo mencioné en caso de que las personas se confundan.

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

Y lo último … ejemplo de selección programática: creé un botón en mi MainWindow.xaml y en su controlador.

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

Espero que esto ayude a alguien 🙂

Puedes hacerlo a través de código detrás de

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