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