Pregunta

Tengo un TreeView que utiliza un HierarchicalDataTemplate para enlazar sus datos.

Se parece a esto:

<TreeView x:Name="mainTreeList" ItemsSource="{Binding MyCollection}>
  <TreeView.Resources>
    <HierarchicalDataTemplate 
     DataType="{x:Type local:MyTreeViewItemViewModel}" 
     ItemsSource="{Binding Children}">
      <!-- code code code -->
    </HierarchicalDataTemplate>
  </TreeView.Resources>
</TreeView>

Ahora, desde el código subyacente de, por ejemplo, la ventana principal, quiero obtener el TreeViewItem seleccionado actualmente. Sin embargo, si uso:

this.mainTreeList.SelectedItem;

El elemento seleccionado es de tipo MyTreeViewItemViewModel . Pero quiero obtener el 'padre' o 'enlazado' TreeViewItem . No lo paso a mi objeto TreeViewItemModel (ni siquiera sabría cómo).

¿Cómo puedo hacer esto?

¿Fue útil?

Solución

Desde la entrada del blog de Bea Stollnitz sobre esto, intente

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

Otros consejos

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

NO FUNCIONA (para mí) como mainTreeList.Items.CurrentPosition en una vista de árbol con una HierarchicalDataTemplate siempre será -1.

NI HACE a continuación como mainTreeList.Items.CurrentItem en una vista de árbol con una HierarchicalDataTemplate siempre será nula.

TreeViewItem item = (TreeViewItem)mainTreeList
    .ItemContainerGenerator
    .ContainerFromItem(mainTreeList.Items.CurrentItem);

INSTEAD Tuve que configurar el último TreeViewItem seleccionado en el TreeViewItem enrutado. Evento seleccionado que aparece en la vista de árbol (los TreeViewItem en sí mismos no existen en el momento del diseño, ya que estamos utilizando un plan de datos de datos jerárquicos ).

El evento se puede capturar en XAML así:

<TreeView TreeViewItem.Selected="TreeViewItemSelected" .../> 

Luego, el último TreeViewItem seleccionado se puede configurar en el evento como tal:

    private void TreeViewItemSelected(object sender, RoutedEventArgs e)
    {
        TreeViewItem tvi = e.OriginalSource as TreeViewItem;

        // set the last tree view item selected variable which may be used elsewhere as there is no other way I have found to obtain the TreeViewItem container (may be null)
        this.lastSelectedTreeViewItem = tvi;

        ...
     }

Me encontré con este mismo problema. Necesitaba llegar al TreeViewItem para poder seleccionarlo. Luego me di cuenta de que solo podía agregar una propiedad IsSelected a mi ViewModel, que luego vinculé a TreeViewItems IsSelectedProperty. Esto se puede lograr con el ItemContainerStyle:

<TreeView>
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </TreeView.ItemContainerStyle>
</TreeView>

Ahora, si quiero tener un elemento en la vista de árbol seleccionada, solo llamo a IsSelected en mi clase de ViewModel directamente.

Espero que ayude a alguien.

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition)); gives first item in the TreeView because CurrentPosition is always 0.

¿Qué tal

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromItem(mainTreeList.SelectedItem)));

Esto funciona mejor para mí.

Inspirado en la respuesta de F & # 235; anor & # 8217; s & # 8217; he intentado hacer que TreeViewItem sea fácilmente accesible para cada elemento de datos que tiene un TreeViewItem creado para.

La idea es agregar un campo con código TreeViewItem al modelo de vista, también expuesto a través de una interfaz, y hacer que TreeView lo rellene automáticamente siempre que Se crea el contenedor TreeViewItem .

Esto se realiza mediante la subclase de TreeView y adjuntando un evento al ItemContainerGenerator , que registra el TreeViewItem cada vez que se crea. Los errores incluyen el hecho de que los TreeViewItem se crean de forma perezosa, por lo que puede que realmente no haya uno disponible en ciertos momentos.

Desde que publiqué esta respuesta, lo he desarrollado aún más y lo he usado durante mucho tiempo en un proyecto. No hay problemas hasta ahora, aparte del hecho de que esto viola MVVM (pero también le ahorra una tonelada de repetitivo para los casos simples). Fuente aquí .

Usuario

Seleccione el elemento primario del elemento seleccionado y cámbielo, asegurándose de que esté en la vista:

...
var selected = myTreeView.SelectedItem as MyItem;
selected.Parent.TreeViewItem.IsSelected = true;
selected.Parent.TreeViewItem.IsExpanded = false;
selected.Parent.TreeViewItem.BringIntoView();
...

Declaraciones:

<Window ...
        xmlns:tvi="clr-namespace:TreeViewItems"
        ...>
    ...
    <tvi:TreeViewWithItem x:Name="myTreeView">
        <HierarchicalDataTemplate DataType = "{x:Type src:MyItem}"
                                  ItemsSource = "{Binding Children}">
            <TextBlock Text="{Binding Path=Name}"/>
        </HierarchicalDataTemplate>
    </tvi:TreeViewWithItem>
    ...
</Window>
class MyItem : IHasTreeViewItem
{
    public string Name { get; set; }
    public ObservableCollection<MyItem> Children { get; set; }
    public MyItem Parent;
    public TreeViewItem TreeViewItem { get; set; }
    ...
}

Código

public class TreeViewWithItem : TreeView
{
    public TreeViewWithItem()
    {
        ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
    }

    private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
    {
        var generator = sender as ItemContainerGenerator;
        if (generator.Status == GeneratorStatus.ContainersGenerated)
        {
            int i = 0;
            while (true)
            {
                var container = generator.ContainerFromIndex(i);
                if (container == null)
                    break;

                var tvi = container as TreeViewItem;
                if (tvi != null)
                    tvi.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;

                var item = generator.ItemFromContainer(container) as IHasTreeViewItem;
                if (item != null)
                    item.TreeViewItem = tvi;

                i++;
            }
        }
    }
}

interface IHasTreeViewItem
{
    TreeViewItem TreeViewItem { get; set; }
}

Prueba algo como esto:

    public bool UpdateSelectedTreeViewItem(PropertyNode dateItem, ItemsControl itemsControl)
    {
        if (itemsControl == null || itemsControl.Items == null || itemsControl.Items.Count == 0)
        {
            return false;
        }
        foreach (var item in itemsControl.Items.Cast<PropertyNode>())
        {
            var treeViewItem = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
            if (treeViewItem == null)
            {
                continue;
            }
            if (item == dateItem)
            {
                treeViewItem.IsSelected = true;
                return true;
            }
            if (treeViewItem.Items.Count > 0 && UpdateSelectedTreeViewItem(dateItem, treeViewItem))
            {
                return true;
            }
        }
        return false;
    }

Una sola llamada 'ItemContainerGenerator.ContainerFromItem' o 'ItemContainerGenerator.ItemContainerGenerator' no puede encontrar el TreeViewItem asociado por el objeto de vista de árbol, debido a la separación entre el controlador del elemento de vista de árbol y los datos del elemento de vista de árbol. Es necesario crear una función recursiva para usar 'ItemContainerGenerator.ContainerFromItem' y 'ItemContainerGenerator.ItemContainerGenerator', para ubicar TreeViewItem en la vista de árbol. Ejemplo de función recursiva podría verse como:

private TreeViewItem GetTreeViewItemFromObject(ItemContainerGenerator container, object tvio)
{
    var item = container.ContainerFromItem(tvio) as TreeViewItem;
    if (item != null)
    {
        return item;
    }

    for (int i = 0; i < container.Items.Count; i++)
    {
        var subContainer = (TreeViewItem)container.ContainerFromIndex(i);
        if (subContainer != null)
        {
            item = GetTreeViewItemFromObject(subContainer.ItemContainerGenerator, tvio);
            if (item != null)
            {
                return item;
            }
        }
    }

    return null;
}

llamado por     var target = GetTreeViewItemFromObject (treeView.ItemContainerGenerator, item);

La función recursiva solo funciona después de que 'ItemContainerGenerator.Status' de la vista de árbol es 'ContainersGenerated'. Por lo tanto, durante el período de visualización de inicialización, 'GetTreeViewItemFromObject' no funciona.

He modificado la búsqueda recursiva de William a una versión más compacta:

public TreeViewItem GetTreeViewItemFromObject(ItemContainerGenerator container, object targetObject) {
    if (container.ContainerFromItem(targetObject) is TreeViewItem target) return target;
    for (int i = 0; i < container.Items.Count; i++)
        if ((container.ContainerFromIndex(i) as TreeViewItem)?.ItemContainerGenerator is ItemContainerGenerator childContainer)
            if (GetTreeViewItemFromObject(childContainer, targetObject) is TreeViewItem childTarget) return childTarget;
    return null;
}

Uno lo llamaría proporcionando el ItemContainerGenerator de la instancia de TreeView y el objeto de datos de destino:

TreeViewItem tvi = GetTreeViewItemFromObject(treeView.ItemContainerGenerator, targetDataObject);

¿Necesita el TreeViewItem porque va a modificar lo que se muestra? Si ese es el caso, recomendaría usar un estilo para cambiar la forma en que se muestra el elemento en lugar de usar el código subyacente en lugar de modificar directamente el TreeViewItem. Esperemos que sea más limpio.

Si tiene que encontrar un artículo en niños de niños, es posible que tenga que usar recursión como esta

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

Aquí está la solución. rtvEsa es vista de árbol. HierarchicalDataTemplate es una plantilla de vista en perspectiva Etiqueta hacer el consumo real del elemento actual. Este no es un elemento seleccionado, es un elemento actual en el control de árbol que usa HierarchicalDataTemplate.

Items.CurrentItem es parte de la colección interna del árbol. No se pueden obtener muchos y diferentes datos. Por ejemplo, Items.ParenItem también.

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

  <TextBox Tag="{Binding ElementName=rtvEsa, Path=Items.CurrentItem }" />

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top