Question

J'ai un TreeView qui utilise un HierarchicalDataTemplate pour lier ses données.

Cela ressemble à ceci:

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

Maintenant, à partir du code derrière la fenêtre principale, je veux obtenir le TreeViewItem sélectionné . Cependant, si j'utilise:

this.mainTreeList.SelectedItem;

L'élément sélectionné est de type MyTreeViewItemViewModel . Mais je veux obtenir le TreeViewItem 'parent' ou 'lié'. Je ne transmets pas cela à mon objet TreeViewItemModel (je ne saurais même pas comment).

Comment puis-je faire cela?

Était-ce utile?

La solution

Dans l'entrée de blog de Bea Stollnitz à ce sujet, essayez

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

Autres conseils

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

NE FONCTIONNE PAS (pour moi) en tant que mainTreeList.Items.CurrentPosition dans un arborescence utilisant un HierarchicalDataTemplate sera toujours égal à -1.

Ni l'un ni l'autre ne figure ci-dessous en tant que mainTreeList.Items.CurrentItem dans un arborescence utilisant un HierarchicalDataTemplate sera toujours null.

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

INSTEAD : je devais définir le dernier TreeViewItem sélectionné dans l'événement TreeViewItem.Selected routé, qui déborde dans la vue arborescente (les TreeViewItem n'existent pas à la conception car nous utilisons un HierarchicalDataTemplate ).

L'événement peut être capturé en XAML comme suit:

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

Ensuite, le dernier TreeViewItem sélectionné peut être défini dans l'événement comme suit:

    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;

        ...
     }

J'ai rencontré le même problème. Il me fallait accéder à TreeViewItem pour pouvoir le sélectionner. J'ai alors réalisé que je pouvais simplement ajouter une propriété IsSelected à mon ViewModel, que je liais ensuite à TreeViewItems IsSelectedProperty. Ceci peut être réalisé avec 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>

Maintenant, si je veux qu'un élément de l'arborescence soit sélectionné, j'appelle directement IsSelected sur ma classe ViewModel.

J'espère que cela aidera quelqu'un.

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

Que diriez-vous de

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

Cela fonctionne mieux pour moi.

Inspiré par la réponse de Fëanor, j’ai tenté de rendre le TreeViewItem facilement accessible pour chaque élément de données pour lequel un TreeViewItem a été créé.

L’idée est d’ajouter un champ de type TreeViewItem au modèle de vue, également exposé via une interface, afin que le TreeView le remplisse automatiquement à chaque fois que le TreeViewItem est créé.

Ceci est effectué en sous-classant TreeView et en attachant un événement à ItemContainerGenerator , qui enregistre le TreeViewItem chaque fois qu'il est créé. Parmi les pièges, citons le fait que les TreeViewItem sont créés paresseusement. Par conséquent, il se peut qu’il n’en existe vraiment pas de disponibles à certains moments.

Depuis que j'ai posté cette réponse, je l'ai développée plus avant et je l'ai utilisée pendant longtemps dans un projet. Aucun problème jusqu'à présent, mis à part le fait que cela viole MVVM (mais vous permet également d'économiser une tonne de passe-partout pour les cas simples). Source ici .

Utilisation

Sélectionnez le parent de l'élément sélectionné et réduisez-le en veillant à ce qu'il soit dans la vue:

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

Déclarations:

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

Code

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

Essayez quelque chose comme ça:

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

Un seul appel 'ItemContainerGenerator.ContainerFromItem' ou 'ItemContainerGenerator.ItemContainerGenerator' ne peut pas trouver l'objet TreeViewItem associé à un objet de la vue arborescente, car la séparation des données du contrôleur d'élément de vue arborescente et de celle de l'arborescence. Il est nécessaire de créer une fonction récursive pour utiliser 'ItemContainerGenerator.ContainerFromItem' et 'ItemContainerGenerator.ItemContainerGenerator', afin de localiser TreeViewItem dans l'arborescence. Exemple de fonction récursive pourrait ressembler à:

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

appelé par     var target = GetTreeViewItemFromObject (treeView.ItemContainerGenerator, item);

La fonction récursive ne fonctionne qu'après que 'ItemContainerGenerator.Status' de l'arborescence est 'ContainersGenerated'. Ainsi, pendant la période d'affichage de l'initialisation, 'GetTreeViewItemFromObject' ne fonctionne pas.

J'ai modifié la recherche récursive de William en une version plus compacte:

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

On l'appellerait en fournissant l'élément ItemContainerGenerator de l'instance TreeView et l'objet de données cible:

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

Avez-vous besoin de TreeViewItem parce que vous allez modifier ce qui est affiché? Si tel est le cas, je vous recommande d'utiliser un style pour modifier le mode d'affichage de l'élément au lieu d'utiliser code-behind au lieu de modifier directement TreeViewItem. Espérons que ce soit plus propre.

Si vous devez trouver un objet chez les enfants d’enfants, vous devrez peut-être utiliser la récursivité comme ceci

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

Voici la solution. rtvEsa est une vue. HierarchicalDataTemplate est un modèle d'arborescence Les balises permettent une consommation réelle de l'élément en cours. Cet élément n'est pas sélectionné, il s'agit d'un élément actuel du contrôle d'arborescence qui utilise HierarchicalDataTemplate.

Items.CurrentItem fait partie de la collection d'arborescence interne. Vous ne pouvez pas obtenir de nombreuses et différentes données. Par exemple, Items.ParenItem aussi.

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top