Pregunta

¿Cómo es posible seleccionar mediante programación un elemento en un WPF? TreeView?El ItemsControl El modelo parece impedirlo.

¿Fue útil?

Solución

Es un verdadero dolor por alguna extraña razón, tienes que usar ContainerFromItem para obtener el contenedor, luego invocar el método 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;
}

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

Otros consejos

Para aquellos que todavía están buscando la solución correcta para este problema, aquí está la siguiente. Encontré este en los comentarios al artículo del Proyecto de Código & # 8220; WPF TreeView Selection & # 8221; 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 también recomiendo la propiedad IsExpanded). Defina un estilo para los TreeViewItems del árbol utilizando la propiedad ItemContainerStyle en el TreeView que une esas propiedades del TreeViewItem a sus objetos de datos. Algo como esto:

<Style x:Key="LibraryTreeViewItemStyle"
               TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded"
                        Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected"
                        Value="{Binding IsSelected, Mode=TwoWay}" />
            <Setter Property="FontWeight"
                        Value="Normal" />
            <Style.Triggers>
                  <Trigger Property="IsSelected"
                              Value="True">
                        <Setter Property="FontWeight"
                                    Value="Bold" />
                  </Trigger>
            </Style.Triggers>
      </Style>

<TreeView ItemsSource="{Binding Path=YourCollection}"
               ItemContainerStyle="{StaticResource LibraryTreeViewItemStyle}"
               ItemTemplate={StaticResource YourHierarchicalDataTemplate}/>

Necesitas conseguir el TreeViewItem y luego establecer IsSelected a true.

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;

Esto no es tan simple como parece, el enlace proporcionado por Steven tiene una solución publicada en 2008, que todavía 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? & # 8211; Parte III: http://bea.stollnitz.com/blog/?p=59

Selección programática de un elemento en un TreeView: http: // blog. quantumbitdesigns.com/2008/07/22/programmatic-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/

Escribí un método de extensión:

using System.Windows.Controls;

namespace Extensions
{
    public static class TreeViewEx
    {
        /// <summary>
        /// Select specified item in a TreeView
        /// </summary>
        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]);

Si desea seleccionar un elemento ubicado dentro de los hijos del niño, puede usar la recursividad para hacerlo.

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

Prueba con esto

    /// <summary>
    /// Selects the tree view item.
    /// </summary>
    /// <param name="Collection">The collection.</param>
    /// <param name="Value">The value.</param>
    /// <returns></returns>
    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 intervenir con la solución que elegí, en caso de que esto pueda ayudar a alguien. Tenga en cuenta que la mejor manera de hacer esto es usar una propiedad enlazada como 'IsSelected' según la respuesta de kuninl, pero en mi caso era una aplicación heredada que no seguía MVVM, así que terminé con lo 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 esto hace es seleccionar y expandir el elemento de vista de árbol en la interfaz de usuario que representa el elemento de datos seleccionado en el código detrás. El propósito de esto era hacer que la selección cambiara en la vista de árbol cuando la selección de usuarios cambiara en un control de elementos en la misma ventana.

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

public static class VisualTreeExt
{
  public static IEnumerable<T> GetDescendants<T>(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<T>(child))
         yield return subItem;
    }
  }
}

Cuando solicite VisualTreeHelperExt.GetDescendants<TreeViewItem>(MyAmazingTreeView) obtendrá todos los TreeViewItem hijos. Puede seleccionar un valor particular utilizando el siguiente código:

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

Es una solución un poco sucia (y probablemente no sea la más eficiente) y no funcionará si está utilizando un TreeView virtualizado, porque depende de la existencia de los elementos visuales reales. Pero funciona para mi situación ...

Sí ... sé que hace 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 solicitó el OP.

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

Beneficios:

  • También es compatible con Virtualizing TreeView.
  • Usa la técnica de comportamiento, por lo que XAML es muy fácil.
  • Agrega una propiedad de dependencia 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<TreeView> isRegisteredToSelectionChanged = new List<TreeView>();

    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<object> e)
    {
        var treeView = (TreeView)sender;
        SetSelectedItemEx(treeView, e.NewValue);
    }

    #region Helper Structures & Methods

    public class MyVirtualizingStackPanel : VirtualizingStackPanel
    {
        /// <summary>
        /// Publically expose BringIndexIntoView.
        /// </summary>
        public void BringIntoView(int index)
        {
            BringIndexIntoView(index);
        }
    }

    /// <summary>Recursively search for an item in this subtree.</summary>
    /// <param name="container">The parent ItemsControl. This can be a TreeView or a TreeViewItem.</param>
    /// <param name="item">The item to search for.</param>
    /// <returns>The TreeViewItem that contains the specified item.</returns>
    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<ItemsPresenter>(container);
                if (itemsPresenter == null)
                {
                    container.UpdateLayout();

                    itemsPresenter = FindVisualChild<ItemsPresenter>(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;
    }

    /// <summary>Search for an element of a certain type in the visual tree.</summary>
    /// <typeparam name="T">The type of element to find.</typeparam>
    /// <param name="visual">The parent element.</param>
    /// <returns></returns>
    private static T FindVisualChild<T>(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<T>(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:

<TreeView x:Name="trvwSs"
          Grid.Column="2" Grid.Row="1" Margin="4" ItemsSource="{Binding ItemsTreeViewSs}"
          behaviors:TreeViewSelectedItemExBehavior.SelectedItemEx="{Binding SelectedItemTreeViewSs}" />

De lo único que debe preocuparse es de asegurarse de que su propiedad de modelo de vista que va a vincular a SelectedItemEx no sea nula. Pero ese no es un caso especial. Solo lo mencioné en caso de que la gente se confunda.

public class VmMainContainer : INotifyPropertyChanged
{
    private object selectedItemTreeViewSs = new object();
    private ObservableCollection<object> selectedItemsTreeViewSs = new ObservableCollection<object>();
    private ObservableCollection<VmItem> itemsTreeViewSs = new ObservableCollection<VmItem>();

 public object SelectedItemTreeViewSs
        {
            get
            {
                return selectedItemTreeViewSs;
            }
            set
            {
                selectedItemTreeViewSs = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemTreeViewSs)));
            }
        }

public ObservableCollection<object> SelectedItemsTreeViewSs
        {
            get
            {
                return selectedItemsTreeViewSs;
            }
            set
            {
                selectedItemsTreeViewSs = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemsTreeViewSs)));
            }
        }

 public ObservableCollection<VmItem> ItemsTreeViewSs
        {
            get { return itemsTreeViewSs; }
            set
            {
                itemsTreeViewSs = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemsTreeViewSs)));
            }
        }
    }

Y lo último ... ejemplo de selección mediante programación: Creé un botón en mi MainWindow.xaml y desde 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 :)

Puede hacerlo a través del código detrás, como

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

Creo que esta es la solución más simple:

private void MouseDownEventProcessing(TreeNodeMouseClickEventArgs e)
{
    tvEmployeeDirectory.SelectedNode = e.Node;
}

La respuesta propuesta no funciona. La respuesta de @ fandisusanto probablemente funciona, pero se puede simplificar. Esta es la respuesta más simple que se me ocurre:

    private static void DeselectTreeViewItem(IEnumerable<TreeViewItem> treeViewItems)
    {
        foreach (var treeViewItem in treeViewItems)
        {
            if (treeViewItem.IsSelected)
            {
                treeViewItem.IsSelected = false;
                return;
            }

            DeselectTreeViewItem(treeViewItem.Items.Cast<TreeViewItem>());
        }
    }

Uso:

    private void ClearSelectedItem()
    {
        if (AssetTreeView.SelectedItem != null)
        {
            DeselectTreeViewItem(AssetTreeView.Items.Cast<TreeViewItem>());
        }
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top