TreeView with MasterDetails and ToggleButton

I want to create a TreeView using MasterDetails for a selected item.

The problem is that no child appears in my SelectedItem even when the parent expands. Somehow the HierarchicalDataTemplate seems to get lost.

I may be wrong about the HierarchicalDataTemplate in <TreeView.ItemTemplate>

. Should I start with ItemsPanelTemplate or something similar? There is no clue at this time.

Here is my XAML:

<Window x:Class="TreeViewMasterDetails.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TreeViewMasterDetails" 
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <SolidColorBrush x:Key="GlyphBrush" Color="#444" />
            <BooleanToVisibilityConverter x:Key="booltoVisibilityConverter" />

            <PathGeometry x:Key="TreeArrow">
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure IsFilled="True"
                            StartPoint="0 0"
                            IsClosed="True">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <LineSegment Point="0 6"/>
                                    <LineSegment Point="6 0"/>
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>

            <Style x:Key="ExpandCollapseToggleStyle"
           TargetType="{x:Type ToggleButton}">
                <Setter Property="Focusable"
                Value="False"/>
                <Setter Property="Width"
                Value="16"/>
                <Setter Property="Height"
                Value="16"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ToggleButton}">
                            <Border Width="16"
                            Height="16"
                            Background="Transparent"
                            Padding="5,5,5,5">
                                <Path x:Name="ExpandPath"
                              Fill="Transparent"
                              Stroke="#FF989898"
                              Data="{StaticResource TreeArrow}">
                                    <Path.RenderTransform>
                                        <RotateTransform Angle="135"
                                                 CenterX="3"
                                                 CenterY="3"/>
                                    </Path.RenderTransform>
                                </Path>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver"
                                 Value="True">
                                    <Setter TargetName="ExpandPath"
                                    Property="Stroke"
                                    Value="#FF1BBBFA"/>
                                    <Setter TargetName="ExpandPath"
                                    Property="Fill"
                                    Value="Transparent"/>
                                </Trigger>

                                <Trigger Property="IsChecked"
                                 Value="True">
                                    <Setter TargetName="ExpandPath"
                                    Property="RenderTransform">
                                        <Setter.Value>
                                            <RotateTransform Angle="180"
                                                     CenterX="3"
                                                     CenterY="3"/>
                                        </Setter.Value>
                                    </Setter>
                                    <Setter TargetName="ExpandPath"
                                    Property="Fill"
                                    Value="#FF595959"/>
                                    <Setter TargetName="ExpandPath"
                                    Property="Stroke"
                                    Value="#FF262626"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

            <ControlTemplate TargetType="{x:Type TreeViewItem}" x:Key="selectedItemTemplate">
                <Grid Height="Auto" Background="SkyBlue">
                    <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>
                    <ToggleButton x:Name="Expander"
                                      Style="{StaticResource ExpandCollapseToggleStyle}"
                                      IsChecked="{Binding Path=IsExpanded,RelativeSource={RelativeSource TemplatedParent}}"
                                      ClickMode="Press"
                                      Visibility="{Binding Path=HasItems,RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource booltoVisibilityConverter}}"/>

                    <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Id}"></TextBlock>
                    <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Name}"></TextBlock>
                    <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Description}"></TextBlock>
                </Grid>
            </ControlTemplate>
        </Grid.Resources>

        <TreeView Height="Auto" 
                  HorizontalAlignment="Stretch" 
                  Margin="10" 
                  VerticalAlignment="Stretch" 
                  Width="Auto"
                  ItemsSource="{Binding Items}">
            <TreeView.ItemContainerStyle>
                <Style TargetType="TreeViewItem">
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Template" Value="{StaticResource selectedItemTemplate}"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TreeView.ItemContainerStyle>
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="x:Type local:NodeViewModel" ItemsSource="{Binding Children}">
                    <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>

                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

      

I apologize for the massive XAML block, but right now I don't know where the reason lies.

And my ViewModel:

public class NodeViewModel : ViewModelBase
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public bool IsExpanded { get; set; }
    public bool HasChildren  // perhaps this can be replaced by HasItems in TemplatedParent?
    {
        get
        {
            if (Children != null)
            {
                Children.Any();
            }

            return false;
        }
    }

    public ObservableCollection<NodeViewModel> Children { get; set; }
}

      

How do I display children with my HierarchicalDataTemplate? Why is he lost?

Update Added a setter to our TreeViewItem style to set IsExpanded to true on selection and the ToggleButton seems to display that right.

Is there a good tutorial or anything else where I can learn how to deal with the hierarchical data pattern?

Any idea on how I can proceed would be much appreciated!

0


source to share


1 answer


Have you tried emulating a tree item with a toggle button. I'm sure there would be a way to do it, but it's tricky. If you can live with an extension button outside of your template, try the next solution. Changing the visual style of the treeview item is now embedded in the HierarchicalDataTemplate.

enter image description here

You bind the TreeView to a property Items

of the ViewModel that you haven't shared, while the HierarchicalDataTemplate is using the property Children

, I had to change it because I am using the NodeViewModel as the "root" ViewModel.

I think the same can be achieved with 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" },
        };
    }
}

      

+1


source







All Articles