0

我想在选定的项目上创建一个带有 MasterDetails 的 TreeView。

问题是,即使父项展开,我的 SelectedItem 中也没有显示子项。不知何故, HierarchicalDataTemplate 似乎迷路了。

也许我对<TreeView.ItemTemplate>. 我应该从 ItemsPanelTemplate 还是类似的东西开始?暂时没有头绪。

这是我的 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>

请原谅 XAML 的大规模块,但现在我不知道原因在哪里。

我的视图模型:

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

如何使用 HierarchicalDataTemplate 显示孩子?为什么会丢失?

更新 在我的 TreeViewItem 样式中添加了一个设置器,以便在选择时将 IsExpanded 设置为 true,并且 ToggleButton 似乎正确显示了这一点。

有什么好的教程或任何我可以找到如何处理分层数据模板的东西吗?

任何关于我如何继续的想法将不胜感激!

4

1 回答 1

1

您尝试使用切换按钮来模拟树视图项。我敢肯定会有办法做到这一点,但这很复杂。如果您可以忍受模板之外的扩展器按钮,请尝试以下解决方案。更改树视图项的视觉样式现在已内置到 HierarchicalDataTemplate 中。

在此处输入图像描述

您将 TreeView 绑定到Items您尚未披露的 ViewModel 的属性,而 HierarchicalDataTemplate 使用该属性Children,我不得不更改它,因为我也使用 NodeViewModel 作为“根”ViewModel。

我认为同样可以使用 DataTemplateSelector 来实现。

xml:

<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(添加 IsSelected 属性):

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" },
        };
    }
}
于 2013-03-16T13:41:46.080 回答