TreeView с MasterDetails и ToggleButton
Я хочу создать TreeView с MasterDetails на выбранном элементе.
Проблема в том, что в SelectedItem не отображаются дочерние элементы, даже если родительский элемент раскрыт. Каким-то образом HierarchicalDataTemplate, похоже, теряется.
Может быть, я просто не прав с 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, но сейчас я не знаю, в чем причина.
И моя ViewModel:
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, кажется, отображает это право.
Есть ли хороший учебник или что-нибудь, где я могу узнать, как обращаться с шаблоном иерархических данных?
Любая идея о том, как я могу продолжать, будет высоко оценена!
1 ответ
Вы пытались эмулировать элемент дерева с помощью кнопки-переключателя. Я уверен, что был бы способ сделать это, но это сложно. Если вы можете жить с кнопкой расширения, находящейся вне вашего шаблона, попробуйте следующее решение. Изменение визуального стиля элемента древовидной структуры теперь встроено в шаблон HierarchicalDataTemplate.
Вы привязываете TreeView к свойству Items
модели представления, которую вы не раскрыли, в то время как HierarchicalDataTemplate использует свойство Children
Мне пришлось изменить это, потому что я использую NodeViewModel также в качестве "корневой" ViewModel.
Я думаю, что того же можно достичь с помощью DataTemplateSelector.
Xaml:
<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" },
};
}
}