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?
10 réponses
Bea Stollnitz'entrée de blog à ce sujet, essayez
TreeViewItem item = (TreeViewItem)(mainTreeList
.ItemContainerGenerator
.ContainerFromIndex(mainTreeList.Items.CurrentPosition));
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;
...
}
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.
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 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; }
}
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;
}
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.
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.
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;
}
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 }" />