TreeView with MasterDetails and ToggleButton

本秂侑毒 提交于 2020-03-05 06:49:27

问题


I want to create a TreeView with MasterDetails on selected Item.

The problem is, that no children are displayed in my SelectedItem, even when the parent is expanded. Somehow the HierarchicalDataTemplate seems to get lost.

Maybe I am just wrong with the HierarchicalDataTemplate in the <TreeView.ItemTemplate>. Should I start with ItemsPanelTemplate or something similar? Have no clue at the moment.

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>

Please excuse the massiv block of XAML, but right now I do not know where the cause 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 can I display the children with my HierarchicalDataTemplate? Why it gets lost?

Update Have added a setter in my TreeViewItem Style to set IsExpanded to true on selection, and the ToggleButton seems to display this right.

Is there any good tutorial or anything where I can find out how to deal with the hierarchical data template?

Any idea on how I can go on will be highly appreciated!


回答1:


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


来源:https://stackoverflow.com/questions/15447819/treeview-with-masterdetails-and-togglebutton

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!