I have a TreeView
intended to operate on two levels. For his I have two HierarchicalDataTemplate
and two custom types. The ItemsSource
is linked to a ObservableCollection
and everything works fine. I just can't figure how to make a node selected or expanded from codebehind. Somewhere was mentioned a very good idea of binding IsExpanded
and IsSelected
properties to the corresponding properties in my custom types. The only problem is that the HierarchicalDataTemplate
does not implement a TreeViewItem
directly, so how can I access these properties in the following code?
<TreeView Name="treeViewNotes" AllowDrop="True" PreviewMouseLeftButtonDown="treeViewNotes_PreviewMouseLeftButtonDown" PreviewMouseMove="treeViewNotes_PreviewMouseMove" Drop="treeViewNotes_Drop" DragEnter="treeViewNotes_DragEnter">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type dataclasses:NoteFolder}" ItemsSource="{Binding Notes}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding TreeViewIcon}" Tag="{Binding Self}"/>
<TextBlock Text="{Binding Title}" Tag="{Binding Self}" Margin="3"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type dataclasses:Note}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding TreeViewIcon}" Tag="{Binding Self}"/>
<TextBlock Text="{Binding Title}" Tag="{Binding Self}" Margin="3"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
My goal is when creating and adding a new Note
to some NoteFolder
, to make this Note
selected and the Folder
expanded. The same would be needed to further improve the UI response on drag&drop.
You could try changing the TreeView's ItemContainerStyle in the following way, so that its IsExpanded and IsSelected properties would bind to the DataContext's IsExpanded and IsSelected:
<TreeView x:Name="..." ItemsSource="{Binding RootNode}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- Items in the ItemsSource need to have these properties for the binding to work -->
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<!-- You can also optionally change some style values based on IsSelected and IsExpanded values -->
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="BorderThickness" Value="4 0 0 1"/>
<Setter Property="BorderBrush" Value="DeepSkyBlue"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<Setter Property="BorderThickness" Value="4 0 0 1 "/>
<Setter Property="BorderBrush" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate>
...
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Of course, then each item coming from the ItemSource and present in the hierarchy needs to have these properties.
The only problem is that the HierarchicalDataTemplate does not implement a TreeViewItem directly, so how can I access these properties in the following code?
You can do that in TreeView.ItemContainerStyle:
<TreeView ItemTemplate="{StaticResource ResourceKey=treeViewDataTemplate}"
ItemsSource="{Binding Data}"
Name="trvTreeView">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}" />
<EventSetter Event="Expanded" Handler="TreeViewItem_Expanded" />
<EventSetter Event="Collapsed" Handler="TreeViewItem_Collapsed" />
<EventSetter Event="PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
In the code behind:
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem treeViewItem = e.OriginalSource as TreeViewItem;
if (treeViewItem != null)
{
BaseObjectExplorerNode baseObjectExplorerNode = treeViewItem.Header as BaseObjectExplorerNode;
if (baseObjectExplorerNode != null)
{
baseObjectExplorerNode.IsExpanded = true;
}
}
}
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
{
TreeViewItem treeViewItem = e.OriginalSource as TreeViewItem;
if (treeViewItem != null)
{
BaseObjectExplorerNode baseObjectExplorerNode = treeViewItem.Header as BaseObjectExplorerNode;
if (baseObjectExplorerNode != null)
{
baseObjectExplorerNode.IsExpanded = false;
}
}
}
And then for example:
root.IsExpanded = true;
I don't see any problem. You can make a StyleSelector
, two Style
's and bind to properties.
XAML:
<TreeView Name="treeViewNotes" AllowDrop="True" PreviewMouseLeftButtonDown="treeViewNotes_PreviewMouseLeftButtonDown" PreviewMouseMove="treeViewNotes_PreviewMouseMove" Drop="treeViewNotes_Drop" DragEnter="treeViewNotes_DragEnter">
<TreeView.Resources>
<!-- StyleSelector for containers -->
<notdataclasses:NoteStyleSelector x:Key="NoteStyleSelector" />
<!-- Style for a NoteFolder's container -->
<Style x:Key="NoteFolderStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}" />
<Setter Property="ItemContainerStyleSelector" Value="{StaticResource NoteFolderStyle}" />
</Style>
<!-- Style for a Note's container -->
<Style x:Key="NoteStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
</Style>
<!-- ... -->
</TreeView.Resources>
<TreeView.ItemContainerStyleSelector>
<StaticResource ResourceKey="NoteStyleSelector" />
</TreeView.ItemContainerStyleSelector>
</TreeView>
NoteStyleSelector:
public sealed class NoteStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
FrameworkElement fe = container as FrameworkElement;
if (fe!= null)
{
if (item is Note)
{ return (Style)fe.FindResource("NoteStyle"); }
if (item is NoteFolder)
{ return (Style)fe.FindResource("NoteFolderStyle"); }
}
return base.SelectStyle(item, container);
}
}
In a drop handler:
currentFolder.Nodes.Add(pastedNode);
currentFolder.IsExpanded = true;
currentNode.IsSelected = true;
Data casses:
public class Note : INotifyPropertyChanged
{
// Only the IsSelected property because a Note can not be expanded
public bool IsSelected { get { /* ... */ } set { /* ... */ } }
}
public class NoteFolder : INotifyPropertyChanged
{
public bool IsSelected { get { /* ... */ } set { /* ... */ } }
public bool IsExpanded { get { /* ... */ } set { /* ... */ } }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With