将 HierarcicalDataTemplates 与 TreeViewItem 控件模板结合使用

发布于 2024-12-13 04:12:58 字数 2348 浏览 1 评论 0原文

我在弄清楚如何模板化以下 TreeView 项目布局时遇到一些困难:

TreeView Item Layout Mockup

我有几个项目,SearchList ,其中包含 Search 的集合,其中包含 DataSet 的集合(有点像,但这不是重点)。我遇到的困难是按照我想要的方式设置每个节点级别的样式。我正在使用 MVVM,并且 TreeViews ItemsSource 属性设置为 SearchListViewModels 的 ObservableCollection,而该集合又包含我的对象一直到对象树。

我可以成功设置 SearchList HierarchicalDataTemplate 的样式以正确显示它们。我困惑的地方是 SearchTerm 节点样式。我希望数据集在 SearchTerm 内容区域右侧的包装面板或统一网格(我尚未决定)中表示。我已经修改了 TreeViewItem 控件模板以按照我认为的方式运行),但是如果我在搜索 HierarchicalDataTemplate 的 ItemContainerStyle 属性中设置它,它不会执行任何操作。显示的只是搜索的内容。

我的更改后的 TreeViewItem 模板

<Style TargetType="{x:Type TreeViewItem}" x:Key="AlteredTreeViewItem">
    <Setter Property="HorizontalContentAlignment"
        Value="Stretch" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TreeViewItem}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"
                            MinWidth="19" />
                        <ColumnDefinition Width="0.414*" />
                        <ColumnDefinition Width="0.586*"/>
                    </Grid.ColumnDefinitions>
                    <Border x:Name="Bd" HorizontalAlignment="Stretch"
                        Grid.Column="1" Grid.ColumnSpan="1" Background="#7F058956">
                        <ContentPresenter x:Name="PART_Header" Margin="10,0" />
                    </Border>
                    <WrapPanel x:Name="ItemsHost"
                        Grid.Column="2" IsItemsHost="True"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我的搜索分层数据模板

    <HierarchicalDataTemplate DataType="{x:Type local:SearchViewModel}"  ItemsSource="{Binding MySearch.Custodians}" ItemContainerStyle="{StaticResource AlteredTreeViewItem}">
        <TextBlock Text="{Binding MySearch.SearchName}" Foreground="Black" FontFamily="Arial" FontSize="16"/>
    </HierarchicalDataTemplate>

当然可以使用不同的样式并且使子项布局不同吗?如何才能实现这一目标?

I am having some difficulty figuring out how to template the following TreeView item layout:

TreeView Item Layout Mockup

I have several items, SearchList, which contains a collection of Search, which contains a collection of DataSet (sort of, but that is beside the point). What I am having difficulty with is styling each node level the way I want. I am using MVVM, and the TreeViews ItemsSource property is set to an ObservableCollection of SearchListViewModels which in turn contain my objects all the way down the object tree.

I can successfully style the SearchList HierarchicalDataTemplate to display them correctly. Where I get hung up is on SearchTerm nodes styling. I want the DataSets to be represented in a wrap panel or uniform grid (I haven't decided yet) to the right of the SearchTerm content area. I have modified a TreeViewItem control template to behave this way I think), however if I set it in the ItemContainerStyle property of the Search HierarchicalDataTemplate, it does nothing. All that gets displayed is the content for the Search.

My Altered TreeViewItem Template

<Style TargetType="{x:Type TreeViewItem}" x:Key="AlteredTreeViewItem">
    <Setter Property="HorizontalContentAlignment"
        Value="Stretch" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TreeViewItem}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"
                            MinWidth="19" />
                        <ColumnDefinition Width="0.414*" />
                        <ColumnDefinition Width="0.586*"/>
                    </Grid.ColumnDefinitions>
                    <Border x:Name="Bd" HorizontalAlignment="Stretch"
                        Grid.Column="1" Grid.ColumnSpan="1" Background="#7F058956">
                        <ContentPresenter x:Name="PART_Header" Margin="10,0" />
                    </Border>
                    <WrapPanel x:Name="ItemsHost"
                        Grid.Column="2" IsItemsHost="True"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

My Search Hierarchical Data Template

    <HierarchicalDataTemplate DataType="{x:Type local:SearchViewModel}"  ItemsSource="{Binding MySearch.Custodians}" ItemContainerStyle="{StaticResource AlteredTreeViewItem}">
        <TextBlock Text="{Binding MySearch.SearchName}" Foreground="Black" FontFamily="Arial" FontSize="16"/>
    </HierarchicalDataTemplate>

Surely it is possible to both style differently and have child items laid out differently? How can this be achieved?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

捎一片雪花 2024-12-20 04:12:58

看来你已经非常接近你所追求的了。我尝试根据您发布的代码重新创建您的场景,并注意到它的一些问题(这当然是基于我对您发布的代码的解释)

  • 您缺少 ContentSource= ContentPresenter 的“Header” 部分
  • 我认为您在错误的 HierarchicalDataTemplate 级别应用了 ItemContainerStyle。应该在父级上指定它,以便影响子级(在您的情况下为 SearchListViewModel)。
  • TreeViewItem 的默认 TemplateAuto 大小的 ColumnDefinition 布局 ContentPresenter,因此除非您也修改父项的 ItemContainerStyle,否则 WrapPanel 将不会成功换行。 我在下面的示例中将其更改为 UniformGrid

通过上面的更改和其他一些内容,我得到了一个看起来像这样的结果,希望与您的结果非常接近 之后

在此处输入图像描述

,我在此处上传了示例解决方案:https://www.dropbox.com/s/4v2t8imikkagueb/TreeViewAltered.zip? dl=0

这是它的 Xaml 代码(代码太多,无法全部发布..)

<Window.Resources>
    <!-- DataSet-->
    <HierarchicalDataTemplate DataType="{x:Type data:DataSet}">
        <Border BorderThickness="3"
                BorderBrush="Gray"
                Background="Green">
            <TextBlock Text="{Binding Path=Tables[0].TableName}"
                       Margin="5"/>
        </Border>
    </HierarchicalDataTemplate>

    <!-- SearchViewModel -->
    <HierarchicalDataTemplate DataType="{x:Type viewModel:SearchViewModel}"
                              ItemsSource="{Binding DataSets}">
        <TextBlock Text="{Binding DisplayName}"
                   Foreground="Black"
                   FontFamily="Arial"
                   FontSize="16"/>
    </HierarchicalDataTemplate>

    <!-- SearchListViewModel -->
    <HierarchicalDataTemplate DataType="{x:Type viewModel:SearchListViewModel}"
                              ItemsSource="{Binding SearchList}">
        <HierarchicalDataTemplate.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TreeViewItem}">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" MinWidth="19" />
                                    <ColumnDefinition Width="0.414*" />
                                    <ColumnDefinition Width="0.586*"/>
                                </Grid.ColumnDefinitions>
                                <Border x:Name="Bd"
                                        HorizontalAlignment="Stretch" 
                                        Grid.Column="1"
                                        Grid.ColumnSpan="1"
                                        Background="#7F058956">
                                    <ContentPresenter x:Name="PART_Header"
                                                      ContentSource="Header"
                                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                                </Border>
                                <UniformGrid x:Name="ItemsHost"
                                             Grid.Column="2"
                                             Columns="3"
                                             IsItemsHost="True"/>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </HierarchicalDataTemplate.ItemContainerStyle>
        <TextBlock Text="{Binding DisplayName}"
                   FontSize="20"/>
    </HierarchicalDataTemplate>
</Window.Resources>
<Grid>
    <TreeView ItemsSource="{Binding SearchListViewModels}" />
</Grid>

It seems that you are pretty close to what you're after. I tried to recreate your scenario based on the code you posted and I noted some problems with it (which of course are based on my interpretation of the code you posted)

  • You are missing the ContentSource="Header" part of the ContentPresenter
  • I think you are applying the ItemContainerStyle at the wrong HierarchicalDataTemplate level. It should be specified on the parent in order to affect the children (in your case SearchListViewModel).
  • The default Template for TreeViewItem lays out the ContentPresenter in an Auto sized ColumnDefinition so the WrapPanel won't succesfully wrap unless you modify the ItemContainerStyle for the parent as well. I changed it to a UniformGrid in my sample below

With the changes from above and a few other things I got a result that looks like this which hopefully is pretty close to what you're after

enter image description here

I uploaded the sample solution here: https://www.dropbox.com/s/4v2t8imikkagueb/TreeViewAltered.zip?dl=0

And here is the Xaml code for it (too much code to post it all..)

<Window.Resources>
    <!-- DataSet-->
    <HierarchicalDataTemplate DataType="{x:Type data:DataSet}">
        <Border BorderThickness="3"
                BorderBrush="Gray"
                Background="Green">
            <TextBlock Text="{Binding Path=Tables[0].TableName}"
                       Margin="5"/>
        </Border>
    </HierarchicalDataTemplate>

    <!-- SearchViewModel -->
    <HierarchicalDataTemplate DataType="{x:Type viewModel:SearchViewModel}"
                              ItemsSource="{Binding DataSets}">
        <TextBlock Text="{Binding DisplayName}"
                   Foreground="Black"
                   FontFamily="Arial"
                   FontSize="16"/>
    </HierarchicalDataTemplate>

    <!-- SearchListViewModel -->
    <HierarchicalDataTemplate DataType="{x:Type viewModel:SearchListViewModel}"
                              ItemsSource="{Binding SearchList}">
        <HierarchicalDataTemplate.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TreeViewItem}">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" MinWidth="19" />
                                    <ColumnDefinition Width="0.414*" />
                                    <ColumnDefinition Width="0.586*"/>
                                </Grid.ColumnDefinitions>
                                <Border x:Name="Bd"
                                        HorizontalAlignment="Stretch" 
                                        Grid.Column="1"
                                        Grid.ColumnSpan="1"
                                        Background="#7F058956">
                                    <ContentPresenter x:Name="PART_Header"
                                                      ContentSource="Header"
                                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                                </Border>
                                <UniformGrid x:Name="ItemsHost"
                                             Grid.Column="2"
                                             Columns="3"
                                             IsItemsHost="True"/>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </HierarchicalDataTemplate.ItemContainerStyle>
        <TextBlock Text="{Binding DisplayName}"
                   FontSize="20"/>
    </HierarchicalDataTemplate>
</Window.Resources>
<Grid>
    <TreeView ItemsSource="{Binding SearchListViewModels}" />
</Grid>
生生漫 2024-12-20 04:12:58

很久以前,我在尝试创建类似界面时了解到,使用ListBoxTreeView更好 /代码>。

为什么?

  1. 如果您只有一级扩展(如示例中所示),您将对布局有更多控制,因为您有一个 DataTemplate 来设置样式。

  2. 自定义 ListBoxTreeView 容易得多,因为您不必关心 GridViewColumnHeaderGridViewColumnPresenters 等等

要获得扩展部分(这就是您最初选择 TreeView 的原因),只需使用定义了两行的 Grid 和一个 Expander > 在第二行绑定到ToggleButtonIsChecked 属性。请参阅我从日志查看器中提取的示例。

<DataTemplate>
    <Grid Margin="0,0,0,3" Grid.IsSharedSizeScope="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="30" SharedSizeGroup="SSG_TimeIcon"/>
            <ColumnDefinition Width="120" SharedSizeGroup="SSG_Time"/>
            <ColumnDefinition Width="30" SharedSizeGroup="SSG_LevelIcon"/>
            <ColumnDefinition Width="70" SharedSizeGroup="SSG_Level"/>
            <ColumnDefinition Width="*" SharedSizeGroup="SSG_Message"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <!-- ProgramTime -->
        <Rectangle Grid.Column="0" Grid.Row="0" Margin="0,0,0,0" Width="16" Height="16" VerticalAlignme="Top"  HorizoalAlignme="Stretch" Fill="{StaticResource Icon_Timer}"/>
        <TextBlock Grid.Column="1" Grid.Row="0" Margin="5,0,0,0" VerticalAlignme="Top" HorizoalAlignme="Stretch" Text="{Binding Path=TimeStamp, Converter={StaticResource ObjectToStringConverter}}" ToolTip="{Binding Path=ProgramTime}"/>
        <!-- Level -->
        <Rectangle Grid.Column="2" Grid.Row="0" Margin="10,0,0,0" Width="16" Height="16" VerticalAlignme="Top" HorizoalAlignme="Stretch" Fill="{Binding Path=Level, Converter={StaticResource MappingConverterNinjaLogLevelEnumToBrushResource}}"/>
        <TextBlock Grid.Column="3" Grid.Row="0" Margin="5,0,0,0" Text="{Binding Path=LevelFriendlyName}" VerticalAlignme="Top" HorizoalAlignme="Stretch"/>
        <!-- Message -->
        <StackPanel Grid.Column="4" Grid.Row="0" Margin="10,0,0,0" Orieation="Horizoal" >
            <TextBlock Margin="0,0,0,0" Text="{Binding Path=LogMessage}" TextWrapping="Wrap" VerticalAlignme="Top"  HorizoalAlignme="Stretch"/>
            <ToggleButton x:Name="ExpandExceptiooggleButton" VerticalAlignme="Top" Margin="5,0,0,0" IsChecked="False" 
                          Coe="Show Details" Tag="Hide Details" Style="{StaticResource TextButtonStyle}"
                          Foreground="{StaticResource BlueBrush}" Background="{StaticResource RedBrush}"
                          Visibility="{Binding Path=HasException, Converter={StaticResource BoolToVisibilityConverter}}" />
        </StackPanel>
        <Expander IsExpanded="{Binding Path=IsChecked, ElemeName=ExpandExceptiooggleButton}" Style="{StaticResource CoeExpanderStyle}" 
                  Margin="10,0,0,0" Grid.Column="4" Grid.Row="1">
            <Border BorderBrush="{StaticResource DarkGreyBrush}" BorderThickness="1,0,0,0">                                
                <TextBlock Text="{Binding Path=Exception}" Margin="5,0,0,0"/>
            </Border>
        </Expander>
    </Grid>
</DataTemplate>

您能看到定义标头和可扩展主体是多么容易吗?如果您确实需要嵌套数据,请在您的视图模型中添加 Level 属性(您正在使用 MVVM,不是吗?!),然后创建一个返回 Margin 的 IValueConverter (即 厚度)来伪造缩进。

Something I learnt a long time ago when trying to create a similar interface was that you are better using a ListBox than a TreeView.

Why?

  1. If you only have one level of expansion (as it appears from your sample) you will a lot more control of the layout as you have a single DataTemplate to style.

  2. It is lot easier to customize a ListBox than a TreeView as you do not have be concerned with the GridViewColumnHeader and GridViewColumnPresenters etc.

To get the expansion part (which is why you initially selected a TreeView), simply use a Grid with two rows defined and an Expander in the second row bound to the IsChecked property of a ToggleButton. See the example that I pulled from my Log Viewer.

<DataTemplate>
    <Grid Margin="0,0,0,3" Grid.IsSharedSizeScope="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="30" SharedSizeGroup="SSG_TimeIcon"/>
            <ColumnDefinition Width="120" SharedSizeGroup="SSG_Time"/>
            <ColumnDefinition Width="30" SharedSizeGroup="SSG_LevelIcon"/>
            <ColumnDefinition Width="70" SharedSizeGroup="SSG_Level"/>
            <ColumnDefinition Width="*" SharedSizeGroup="SSG_Message"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <!-- ProgramTime -->
        <Rectangle Grid.Column="0" Grid.Row="0" Margin="0,0,0,0" Width="16" Height="16" VerticalAlignme="Top"  HorizoalAlignme="Stretch" Fill="{StaticResource Icon_Timer}"/>
        <TextBlock Grid.Column="1" Grid.Row="0" Margin="5,0,0,0" VerticalAlignme="Top" HorizoalAlignme="Stretch" Text="{Binding Path=TimeStamp, Converter={StaticResource ObjectToStringConverter}}" ToolTip="{Binding Path=ProgramTime}"/>
        <!-- Level -->
        <Rectangle Grid.Column="2" Grid.Row="0" Margin="10,0,0,0" Width="16" Height="16" VerticalAlignme="Top" HorizoalAlignme="Stretch" Fill="{Binding Path=Level, Converter={StaticResource MappingConverterNinjaLogLevelEnumToBrushResource}}"/>
        <TextBlock Grid.Column="3" Grid.Row="0" Margin="5,0,0,0" Text="{Binding Path=LevelFriendlyName}" VerticalAlignme="Top" HorizoalAlignme="Stretch"/>
        <!-- Message -->
        <StackPanel Grid.Column="4" Grid.Row="0" Margin="10,0,0,0" Orieation="Horizoal" >
            <TextBlock Margin="0,0,0,0" Text="{Binding Path=LogMessage}" TextWrapping="Wrap" VerticalAlignme="Top"  HorizoalAlignme="Stretch"/>
            <ToggleButton x:Name="ExpandExceptiooggleButton" VerticalAlignme="Top" Margin="5,0,0,0" IsChecked="False" 
                          Coe="Show Details" Tag="Hide Details" Style="{StaticResource TextButtonStyle}"
                          Foreground="{StaticResource BlueBrush}" Background="{StaticResource RedBrush}"
                          Visibility="{Binding Path=HasException, Converter={StaticResource BoolToVisibilityConverter}}" />
        </StackPanel>
        <Expander IsExpanded="{Binding Path=IsChecked, ElemeName=ExpandExceptiooggleButton}" Style="{StaticResource CoeExpanderStyle}" 
                  Margin="10,0,0,0" Grid.Column="4" Grid.Row="1">
            <Border BorderBrush="{StaticResource DarkGreyBrush}" BorderThickness="1,0,0,0">                                
                <TextBlock Text="{Binding Path=Exception}" Margin="5,0,0,0"/>
            </Border>
        </Expander>
    </Grid>
</DataTemplate>

Can you see how much easier it is to define a header and expandable body. If you do have a need for nested data, add a Level property your view model (you are using MVVM aren't you?!) and then create a IValueConverter that returns a Margin (i.e. Thickness) to fake the indent.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文