You tried to emulate the treeview item with a toggle button. I'm sure there would be a way to do that, but it's complex. If you can live with the expander button being outside your template, try the following solution. Changing the visual style of the treeview item is now built into the HierarchicalDataTemplate.
You are binding the TreeView to the property Items
of a ViewModel you have not disclosed, while the HierarchicalDataTemplate uses the property Children
, I had to change that because I use the NodeViewModel also as the 'root' ViewModel.
I think the same could be achieved with a DataTemplateSelector.
Xaml:
<UserControl.DataContext>
<local:NodeViewModel />
</UserControl.DataContext>
<Grid>
<Grid.Resources>
<DataTemplate x:Key="notSelectedItemTemplate" DataType="{x:Type local:NodeViewModel}" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="100*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Id}"></TextBlock>
<TextBlock Grid.Column="1" Text="----"></TextBlock>
<TextBlock Grid.Column="2" Text="{Binding Name}"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="selectedItemTemplate" DataType="{x:Type local:NodeViewModel}">
<Grid Height="Auto" Background="SkyBlue" TextElement.Foreground="Black">
<Grid.RowDefinitions>
<!--<RowDefinition Height="Auto"></RowDefinition>-->
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Id}"></TextBlock>-->
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}"></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Description}"></TextBlock>
</Grid>
</DataTemplate>
</Grid.Resources>
<TreeView Height="Auto" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch" Width="Auto" ItemsSource="{Binding Children}">
<TreeView.Resources>
<!-- remove normal selected item background -->
<SolidColorBrush Color="Transparent" x:Key="{x:Static SystemColors.HighlightBrushKey}"/>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:NodeViewModel}" ItemsSource="{Binding Children}">
<ContentPresenter x:Name="item" ContentTemplate="{StaticResource notSelectedItemTemplate}" />
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="item" Property="ContentTemplate" Value="{StaticResource selectedItemTemplate}" />
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
NodeViewModel (added IsSelected property):
public class NodeViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<NodeViewModel> _children;
public ObservableCollection<NodeViewModel> Children { get { return _children; } set { _children = value; OnPropertyChanged("Children"); } }
private string _id;
public string Id { get { return _id; } set { _id = value; OnPropertyChanged("ID"); } }
private string _name;
public string Name { get { return _name; } set { _name = value; OnPropertyChanged("ID"); } }
private string _description;
public string Description { get { return _description; } set { _description = value; OnPropertyChanged("Description"); } }
private bool _isExpanded;
public bool IsExpanded { get { return _isExpanded; } set { _isExpanded = value; OnPropertyChanged("IsExpanded"); } }
private bool _isSelected;
public bool IsSelected { get { return _isSelected; } set { _isSelected = value; OnPropertyChanged("IsSelected"); } }
public bool HasChildren // perhaps this can be replaced by HasItems in TemplatedParent?
{
get
{
if (Children != null)
{
Children.Any();
}
return false;
}
}
private static bool _setData = true; // hack for example data
public NodeViewModel()
{
if (_setData)
{
_setData = false;
SetExampleData();
}
}
public void SetExampleData()
{
Children = new ObservableCollection<NodeViewModel>()
{
new NodeViewModel() { Name = "1", Description = "One" },
new NodeViewModel() { Name = "2", Description = "Two" },
new NodeViewModel() { Name = "3", Description = "Three" },
new NodeViewModel() { Name = "4", Description = "Four" },
new NodeViewModel() { Name = "5", Description = "Five" },
new NodeViewModel() { Name = "6", Description = "Six" },
new NodeViewModel() { Name = "7", Description = "Seven" },
new NodeViewModel() { Name = "8", Description = "Eight" }
};
Children[0].Children = new ObservableCollection<NodeViewModel>()
{
new NodeViewModel() { Name = "1.1", Description="One.One" },
new NodeViewModel() { Name = "1.2", Description="One.Two" },
new NodeViewModel() { Name = "1.3", Description="One.Three" }
};
Children[0].Children[0].Children = new ObservableCollection<NodeViewModel>()
{
new NodeViewModel() { Name = "1.1.1", Description="One.One.One" },
new NodeViewModel() { Name = "1.1.2", Description="One.One.Two" },
};
Children[1].Children = new ObservableCollection<NodeViewModel>()
{
new NodeViewModel() { Name = "2.1", Description="Two.One" },
new NodeViewModel() { Name = "2.2", Description="Two.Two" },
};
}
}