Comment obtenir TreeViewItem de Hiérarchicaldataemplate item?

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

Il 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, depuis le code-behind de la fenêtre principale, je veux obtenir le courant sélectionné TreeViewItem. Cependant, si j'utilise:

this.mainTreeList.SelectedItem;

le selectedItem est de type MyTreeViewItemViewModel. Mais je veux obtenir le "parent" ou "liées"TreeViewItem. Je ne passe pas à mon TreeViewItemModel objet (ne serait même pas savoir comment).

Comment puis-je faire cela?

31
demandé sur GEOCHET 2009-03-06 01:27:29

10 réponses

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

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));
20
répondu Cameron MacFarland 2009-03-06 08:47:14
TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

NE FONCTIONNE PAS (pour moi) comme mainTreeList.Article.CurrentPosition dans une arborescence à l'aide d'un HierarchicalDataTemplate sera toujours -1.

NOR DOES ci-dessous en tant que liste principale.Article.CurrentItem dans une arboreview en utilisant une Hiérarchicaldataemplate sera toujours null.

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

au lieu de j'ai dû définir le dernier TreeViewItem sélectionné dans le TreeViewItem routé.Sélection de l'événement qui bulles jusqu'à la vue de l'arbre (le TreeViewItem eux-mêmes n'existent pas au moment de la conception en utilisant une HierarchicalDataTemplate).

L'événement peut être capturé dans le code XAML de la manière suivante:

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

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

        ...
     }
31
répondu markmnl 2013-05-24 08:27:16

j'ai rencontré ce même problème. J'avais besoin d'aller au TreeViewItem pour pouvoir le faire sélectionner. Je me suis alors rendu compte que je pouvais simplement ajouter une propriété Isélectionnée à mon viseur, que j'ai ensuite liée à la TreeViewItems Iselected property. Cela peut être réalisé avec le 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 avoir un article dans le treeview sélectionné, J'appelle IsSelected sur ma classe ViewModel directement.

j'Espère que ça aide quelqu'un.

7
répondu santiagoIT 2011-09-21 17:20:45
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.

5
répondu kitndirangu 2013-05-24 08:32:26

inspiré par la réponse de Fëanor, j'ai essayé de faire le TreeViewItem facilement accessible pour chaque élément de données qu'un TreeViewItem a été créé.

L'idée est d'ajouter un TreeViewItem-champ dactylographié sur le modèle de vue, également exposé via une interface, et avoir le TreeView remplir automatiquement chaque fois que le TreeViewItem le conteneur est créé.

Ceci est fait en sous-classant TreeView et attacher un événement à la ItemContainerGenerator, qui enregistre l' TreeViewItem chaque fois que c'est créé. Pièges comprendre le fait que TreeViewItem s sont créés paresseusement, de sorte qu'il pourrait vraiment ne pas y en avoir un disponible à certains moments.

depuis la publication de cette réponse, je l'ai développée davantage 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 économise également une tonne de boilerplate pour les cas simples). Source ici.

Utilisation

sélectionnez le parent du l'élément sélectionné et de l'effondrement, en s'assurant qu'il est 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; }
}
4
répondu Roman Starkov 2016-01-31 20:08:57

Essayez quelque chose comme ceci:

    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;
    }
2
répondu Kevin 2015-02-07 05:04:01

Un seul " ItemContainerGenerator.ContainerFromItem' ou ' ItemContainerGenerator.L'appel d'ItemContainerGenerator ' ne peut pas trouver le TreeViewItem associé par l'objet tree view, depuis la séparation des données du contrôleur d'item tree view et de l'item tree view. Il est nécessaire de créer une fonction récursive pour utiliser " ItemContainerGenerator.ContainerFromItem" et " ItemContainerGenerator.ItemContainerGenerator', pour localiser TreeViewItem dans l'arborescence. Exemple fonction récursive pourrait regarder comme:

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 de récursion ne fonctionne qu'après L'ItemContainerGenerator de tree view.Le statut" est "Conteneursgénérés". Ainsi, pendant la période de vue d'initialisation, 'GetTreeViewItemFromObject' ne fonctionne pas.

1
répondu William 2018-06-08 19:44:58

avez-vous besoin du TreeViewItem parce que vous allez modifier ce qui est affiché? Si c'est le cas, je recommande d'utiliser un Style pour changer la façon dont l'élément est affiché au lieu d'utiliser code-behind au lieu de modifier directement le TreeViewItem. Il devrait être plus propre.

0
répondu viggity 2009-09-22 02:47:08

si vous devez trouver l'article dans les enfants des enfants vous pouvez avoir à utiliser recursion 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;
}
0
répondu purvin 2013-09-06 13:27:59

Voici la solution. rtvEsa est treeview. Hiérarchicaldatemplate est le modèle de treeview Balise de faire de réels consumation de l'élément courant. Ce n'est pas l'article sélectionné il s'agit de l'article courant dans le contrôle de l'arbre qui utilisent le Hiérarchicaldatemplate.

articles.CurrentItem fait partie de la colection interne des arbres. Vous ne pouvez pas obtenir beaucoup de données différentes. Par exemple, les points.ParenItem aussi.

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

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

0
répondu Petar 2014-10-09 07:28:38