Использование HierarcicalDataTemplates в сочетании с шаблонами элементов управления TreeViewItem

Мне трудно понять, как создать шаблон для следующего макета элемента TreeView:

Макет макета элемента TreeView

У меня есть несколько элементов SearchList, который содержит коллекцию Search, которая содержит коллекцию DataSet (вроде как, но это не относится к делу). У меня возникают трудности с оформлением каждого уровня узла так, как я хочу. Я использую MVVM, а для свойства TreeViews ItemsSource установлено значение ObservableCollection из SearchListViewModels, которое, в свою очередь, содержит мои объекты на всем протяжении дерева объектов.

Я могу успешно стилизовать SearchList HierarchicalDataTemplate, чтобы отображать их правильно. Я зацикливаюсь на стилях узлов SearchTerm. Я хочу, чтобы наборы данных были представлены в виде панели обтекания или равномерной сетки (я еще не решил) справа от области содержимого SearchTerm. Я изменил шаблон элемента управления TreeViewItem, чтобы он вел себя именно так, как я думаю), однако, если я установил его в свойстве ItemContainerStyle в Search HierarchicalDataTemplate, он ничего не сделает. Все, что отображается, - это контент для поиска.

Мой измененный шаблон 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>

Конечно, можно по-разному стилизовать и по-разному расположить дочерние элементы? Как этого добиться?


person CodeWarrior    schedule 02.11.2011    source источник


Ответы (2)


Кажется, вы довольно близки к тому, что вам нужно. Я попытался воссоздать ваш сценарий на основе опубликованного вами кода и заметил некоторые проблемы с ним (которые, конечно же, основаны на моей интерпретации опубликованного вами кода)

  • Вам не хватает ContentSource="Header" части ContentPresenter
  • Я думаю, вы применяете ItemContainerStyle не на том HierarchicalDataTemplate уровне. Он должен быть указан на родителе, чтобы повлиять на потомков (в вашем случае SearchListViewModel).
  • По умолчанию Template для TreeViewItem размещает ContentPresenter в Auto размера ColumnDefinition, поэтому WrapPanel не будет успешно завершен, если вы также не измените ItemContainerStyle для родителя. Я изменил его на 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>
person Fredrik Hedblad    schedule 07.11.2011
comment
Выглядит отлично! Я посмотрю на это утром. Спасибо! - person CodeWarrior; 08.11.2011
comment
Спасибо Meleak за отличное решение. Это сработало, как рекламировалось, и показало мне ошибку моего пути. Приложив немного больше стиля, у меня теперь есть приятный для глаз способ визуализировать большую иерархию данных в гораздо более компактном пространстве. Наслаждайтесь дополнительным повторением! - person CodeWarrior; 09.11.2011
comment
@CodeWarrior: Конечно, я рад, что смог :) - person Fredrik Hedblad; 10.11.2011
comment
@CodeWarrior: Вам нужна дополнительная информация для присуждения награды? Просто проверяю, так как ваш предыдущий комментарий показал, что вы планировали наградить его, и он заканчивается завтра :) - person Fredrik Hedblad; 13.11.2011
comment
Я прошу прощения. Я думал, что отметив это как ответ, вы получите награду, но это не так. Все сделано. Спасибо за ответы. - person CodeWarrior; 13.11.2011
comment
Отличное решение! Одна вещь, которую я бы сделал, - это новый UserControl в качестве содержимого элемента <ControlTemplate .../>. - person IAbstract; 19.08.2016

Давным-давно, пытаясь создать похожий интерфейс, я понял, что вам лучше использовать ListBox, чем TreeView.

Почему?

  1. Если у вас есть только один уровень расширения (как видно из вашего примера), вы получите гораздо больший контроль над макетом, поскольку у вас есть один DataTemplate для стиля.

  2. Намного легче настроить ListBox, чем TreeView, поскольку вам не нужно беспокоиться о GridViewColumnHeader, GridViewColumnPresenters и т. Д.

Чтобы получить часть расширения (именно поэтому вы изначально выбрали TreeView), просто используйте Grid с двумя определенными строками и Expander во второй строке, привязанной к свойству IsChecked ToggleButton. См. Пример, который я извлек из моего 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>

Вы видите, насколько проще определить заголовок и расширяемое тело. Если вам действительно нужны вложенные данные, добавьте свойство Level для вашей модели представления (вы используете MVVM, не так ли ?!), а затем создайте IValueConverter, который возвращает Margin (т.е. Thickness) для имитации отступа.

person Dennis    schedule 08.11.2011
comment
Да, я надеялся, что смогу использовать TreeView, так как скоро я ожидаю, что у меня появятся требования к размещению элементов в довольно длинной иерархии, и мне кажется, что список больше не будет жизнеспособным. Я знаю, что он отлично работает для элементов на трех уровнях иерархии, но для элементов на 20 уровнях? Я надеюсь разместить предстоящее необходимое представление в истинном дереве (элементы, ответвляющиеся от родителей), и кажется, что TreeView будет лучше всего, поскольку он специально разработан для размещения подобных иерархически упорядоченных элементов. - person CodeWarrior; 09.11.2011
comment
Я бы по-прежнему продолжал использовать ListBox , если мне действительно не понадобились столбцы. Я создал файловый менеджер (для отображения ресурсов в нашем игровом редакторе / инструментах), используя подход «поддельного» отступа, который должен был отображать более 20000 элементов (очевидно, с использованием виртуализации), которые могли быть n уровней (где n - максимальное разрешенное вложение папок на NTFS-диске). Он действительно держится очень хорошо. - person Dennis; 09.11.2011
comment
Я в целом согласен. Я раньше использовал ListBox таким образом. Моя единственная реальная проблема с использованием ListBox поверх TreeView в подобных случаях, и мое предстоящее требование состоит в том, чтобы TreeView (каким бы он ни был на данный момент неработоспособен) был создан для этой цели, в то время как ListBox можно обмануть. Еще работает и все. Это всего лишь попытка использовать эти предметы, которые были разработаны для этой конкретной цели. Я поставил +1 за хорошее предложение и образцы кода. - person CodeWarrior; 09.11.2011