I have a TreeView
which uses a HierarchicalDataTemplate
to bind its data.
It looks like this:
I modified William's recursive search to a more compact version:
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;
}
One would call it by providing the TreeView instance's ItemContainerGenerator and the target data object:
TreeViewItem tvi = GetTreeViewItemFromObject(treeView.ItemContainerGenerator, targetDataObject);
Here is solution. rtvEsa is treeview. HierarchicalDataTemplate is treeview template Tag make real consumation of current item. This is not selected item it is current item in tree control that use HierarchicalDataTemplate.
Items.CurrentItem is part of internal tree colection. You cant get many and diferent data. For example Items.ParenItem too.
<HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">
<TextBox Tag="{Binding ElementName=rtvEsa, Path=Items.CurrentItem }" />
If you have to find item in children of children you may have to use recursion like this
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;
}
Do you need the TreeViewItem because you're going to modify what is being displayed? If that is the case, I'd recommend using a Style to change how the item is being displayed instead of using code-behind instead of directly modifying the TreeViewItem. It should hopefully be cleaner.
Inspired by Fëanor’s answer, I’ve attempted to make the TreeViewItem
easily accessible for every data item that a TreeViewItem
has been created for.
The idea is to add a TreeViewItem
-typed field to the view model, also exposed via an interface, and have the TreeView
automatically populate it whenever the TreeViewItem
container is created.
This is done by subclassing TreeView
and attaching an event to the ItemContainerGenerator
, which records the TreeViewItem
whenever it’s created. Gotchas include the fact that TreeViewItem
s are created lazily, so there might genuinely not be one available at certain times.
Since posting this answer, I've developed this further and used it for a long time in one project. No issues so far, other than the fact that this violates MVVM (but also saves you a ton of boilerplate for the simple cases). Source here.
Usage
Select the parent of the selected item and collapse it, ensuring that it’s in the view:
...
var selected = myTreeView.SelectedItem as MyItem;
selected.Parent.TreeViewItem.IsSelected = true;
selected.Parent.TreeViewItem.IsExpanded = false;
selected.Parent.TreeViewItem.BringIntoView();
...
Declarations:
<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; }
}