#wpf #treeview #hierarchicaldatatemplate #itemspanel
#wpf #просмотр дерева #hierarchicaldatatemplate #itemspanel
Вопрос:
У меня возникли некоторые трудности с пониманием того, как создать шаблон следующего макета элемента TreeView:
У меня есть несколько элементов, searchList, который содержит коллекцию Search, которая содержит коллекцию DataSet (вроде, но это не относится к делу). С чем у меня возникают трудности, так это с оформлением каждого уровня узла так, как я хочу. Я использую MVVM, и для свойства TreeViews ItemsSource установлено значение ObservableCollection SearchListViewModels, которые, в свою очередь, содержат мои объекты на всем пути вниз по дереву объектов.
Я могу успешно стилизовать HierarchicalDataTemplate списка поиска для их правильного отображения. Я зацикливаюсь на стилизации узлов searchTerm. Я хочу, чтобы наборы данных были представлены в виде панели переноса или единой сетки (я еще не решил) справа от области содержимого поискового запроса. Я изменил шаблон элемента управления TreeViewItem, чтобы он вел себя так, как я думаю), однако, если я установлю его в свойстве ItemContainerStyle поиска 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>
Конечно, можно по-разному стилизовать и расположить дочерние элементы по-разному? Как этого можно достичь?
Ответ №1:
Кажется, вы довольно близки к тому, что вам нужно. Я попытался воссоздать ваш сценарий на основе опубликованного вами кода и заметил некоторые проблемы с ним (которые, конечно, основаны на моей интерпретации опубликованного вами кода)
- Вам не хватает
ContentSource="Header"
частиContentPresenter
- Я думаю, вы применяете
ItemContainerStyle
на неправильномHierarchicalDataTemplate
уровне. Он должен быть указан для родительского элемента, чтобы повлиять на дочерние элементы (в вашем случаеSearchListViewModel
). - Значение по умолчанию
Template
дляTreeViewItem
определяетContentPresenter
Auto
размерColumnDefinition
, поэтомуWrapPanel
оно не будет успешно перенесено, если выItemContainerStyle
также не измените для родительского элемента. Я изменил его на aUniformGrid
в моем примере ниже
С изменениями, внесенными выше, и некоторыми другими вещами я получил результат, который выглядит следующим образом, который, надеюсь, довольно близок к тому, что вам нужно
Я загрузил пример решения здесь: 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>
Комментарии:
1. Это выглядит великолепно! Я посмотрю на это утром. Спасибо!
2. Спасибо Meleak за отличное решение. Он работал так, как рекламировалось, и показал мне ошибку моих путей. Немного изменив стиль, теперь у меня есть приятный для глаз способ визуализации большой иерархии данных в гораздо более компактном пространстве. Наслаждайтесь дополнительным повторением!
3. @CodeWarrior: Конечно, рад, что я смог 🙂
4. @CodeWarrior: Вы ищете какую-либо дополнительную информацию для присуждения награды? Просто проверяю, поскольку в вашем предыдущем комментарии указано, что вы планировали его присвоить, и оно заканчивается завтра 🙂
5. Я прошу прощения. Я думал, что пометка его как ответа даст награду, но это не так. Все сделано. Спасибо за ответы.
Ответ №2:
Что-то, что я узнал давным-давно, когда пытался создать аналогичный интерфейс, заключалось в том, что вам лучше использовать a ListBox
, чем a TreeView
.
Почему?
-
Если у вас есть только один уровень расширения (как видно из вашего примера), у вас будет намного больше контроля над макетом, поскольку у вас есть единый
DataTemplate
стиль. -
Намного проще настроить a
ListBox
, чем aTreeView
, поскольку вам не нужно беспокоиться оGridViewColumnHeader
иGridViewColumnPresenters
т. Д.
Чтобы получить часть расширения (именно поэтому вы изначально выбрали a TreeView
), просто используйте a Grid
с двумя определенными строками и an Expander
во второй строке, привязанной к IsChecked
свойству a ToggleButton
. Смотрите пример, который я извлек из своего средства просмотра журналов.
<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, не так ли ?!), А Затем создайте a IValueConverter
, Который возвращает поле (т.Е. Thickness
) Для подделки отступа.
Комментарии:
1. Да, я надеялся, что смогу использовать TreeView, поскольку я ожидаю, что скоро появится требование к элементам компоновки в довольно длинной иерархии, и, как мне кажется, listbox больше не будет жизнеспособным. Я знаю, что это отлично работает для элементов на трех уровнях иерархии, но для элементов на 20 уровнях? Я надеюсь разместить предстоящее требуемое представление в истинном дереве (элементы, разветвляющиеся от родителей), и кажется, что TreeView будет лучшим, поскольку он специально разработан для размещения подобных иерархически упорядоченных элементов.
2.Я бы все равно продолжал использовать a,
ListBox
если бы мне действительно не нужны были столбцы. Я создал файловый менеджер (для отображения ресурсов в нашем игровом редакторе / инструментах), используя подход «поддельного» отступа, который должен был отображать более 20 000 элементов (очевидно, с использованием виртуализации), глубина которых может составлять n уровней (где n — макс. вложение папок разрешено на диске NTFS). Он очень хорошо держится.3. В целом я согласен. Я уже использовал ListBox таким образом. Моя единственная реальная проблема с использованием ListBox поверх TreeView в подобных случаях и моим предстоящим требованием заключается в том, что TreeView (каким бы неработоспособным он ни был на данный момент) создан для этой цели, в то время как ListBox можно «обмануть» в него. Это все еще работает и все. Это всего лишь попытка использовать эти элементы, которые были разработаны для этой конкретной цели. Я получил 1 за хорошее предложение и примеры кода.