Использование HierarcicalDataTemplates в сочетании с шаблонами элементов управления TreeViewItem
Добрый день,
У меня возникли некоторые затруднения с поиском шаблона следующего макета элемента TreeView:
У меня есть несколько элементов, SearchList, который содержит коллекцию Search, которая содержит коллекцию DataSet (вроде, но это не относится к делу). То, с чем я сталкиваюсь, это стилизация каждого уровня узла так, как я хочу. Я использую MVVM, и свойство TreeViews ItemsSource установлено в ObservableCollection SearchListViewModels, которые, в свою очередь, содержат мои объекты вплоть до дерева объектов.
Я могу успешно стилизовать SearchList HierarchicalDataTemplate, чтобы отобразить их правильно. Я зациклен на стилизации узлов SearchTerm. Я хочу, чтобы наборы данных были представлены в виде панели обертки или единой сетки (я еще не решил) справа от области содержимого SearchTerm. Я изменил шаблон элемента управления TreeView Item, чтобы он вел себя так, как я думаю), однако, если я установил его в свойстве ItemContainerStyle Search HierarchicalDataTemplate, он ничего не делает. Все, что отображается, это контент для поиска.
Мой измененный шаблон TreeView Item
<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>
Конечно, у других были предметы, которые по-разному оформлены, и дочерние предметы были по-разному выложены. У кого-нибудь есть опыт в этом, кто мог бы помочь мне?
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>
Когда я пытался создать подобный интерфейс, я давно понял, что лучше использовать ListBox
чем TreeView
,
Зачем?
Если у вас есть только один уровень расширения (как видно из вашего примера), вы будете гораздо больше контролировать макет, поскольку у вас есть один
DataTemplate
стиль.Намного проще настроить
ListBox
чемTreeView
как вы не должны быть связаны сGridViewColumnHeader
а такжеGridViewColumnPresenters
и т.п.
Чтобы получить часть расширения (именно поэтому вы изначально выбрали TreeView
), просто используйте Grid
с двумя определенными рядами и Expander
во втором ряду привязаны к IsChecked
свойство 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, не так ли?!), а затем создайте IValueConverter
который возвращает маржу (т.е. Thickness
Подделать отступ.
Надеюсь, это поможет, если у вас есть дополнительные вопросы, не стесняйтесь задавать.