当 ItemsSource = 自定义对象集合时,如何使 TreeView 节点 selected/expanded?
How to make TreeView node selected/expanded when ItemsSource = custom object collection?
我有一个 TreeView
打算在两个层面上运作。对于他,我有两个 HierarchicalDataTemplate
和两个自定义类型。 ItemsSource
链接到 ObservableCollection
,一切正常。我只是不知道如何从代码隐藏中选择或扩展节点。某处提到了将 IsExpanded
和 IsSelected
属性绑定到我的自定义类型中的相应属性的一个非常好的主意。唯一的问题是 HierarchicalDataTemplate
没有直接实现 TreeViewItem
,那么如何在下面的代码中访问这些属性?
<TreeView Name="treeViewNotes" AllowDrop="True" PreviewMouseLeftButtonDown="treeViewNotes_PreviewMouseLeftButtonDown" PreviewMouseMove="treeViewNotes_PreviewMouseMove" Drop="treeViewNotes_Drop" DragEnter="treeViewNotes_DragEnter">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type dataclasses:NoteFolder}" ItemsSource="{Binding Notes}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding TreeViewIcon}" Tag="{Binding Self}"/>
<TextBlock Text="{Binding Title}" Tag="{Binding Self}" Margin="3"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type dataclasses:Note}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding TreeViewIcon}" Tag="{Binding Self}"/>
<TextBlock Text="{Binding Title}" Tag="{Binding Self}" Margin="3"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
我的目标是在创建新的 Note
并将其添加到某些 NoteFolder
时,选择 Note
并扩展 Folder
。进一步改进 UI 对拖放的响应也需要同样的方法。
您可以尝试按以下方式更改 TreeView 的 ItemContainerStyle,以便其 IsExpanded 和 IsSelected 属性绑定到 DataContext 的 IsExpanded 和 IsSelected:
<TreeView x:Name="..." ItemsSource="{Binding RootNode}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- Items in the ItemsSource need to have these properties for the binding to work -->
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<!-- You can also optionally change some style values based on IsSelected and IsExpanded values -->
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="BorderThickness" Value="4 0 0 1"/>
<Setter Property="BorderBrush" Value="DeepSkyBlue"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<Setter Property="BorderThickness" Value="4 0 0 1 "/>
<Setter Property="BorderBrush" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate>
...
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
当然,来自 ItemSource 并存在于层次结构中的每个项目都需要具有这些属性。
我没有发现任何问题。您可以创建一个 StyleSelector
、两个 Style
并绑定到属性。
XAML:
<TreeView Name="treeViewNotes" AllowDrop="True" PreviewMouseLeftButtonDown="treeViewNotes_PreviewMouseLeftButtonDown" PreviewMouseMove="treeViewNotes_PreviewMouseMove" Drop="treeViewNotes_Drop" DragEnter="treeViewNotes_DragEnter">
<TreeView.Resources>
<!-- StyleSelector for containers -->
<notdataclasses:NoteStyleSelector x:Key="NoteStyleSelector" />
<!-- Style for a NoteFolder's container -->
<Style x:Key="NoteFolderStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}" />
<Setter Property="ItemContainerStyleSelector" Value="{StaticResource NoteFolderStyle}" />
</Style>
<!-- Style for a Note's container -->
<Style x:Key="NoteStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
</Style>
<!-- ... -->
</TreeView.Resources>
<TreeView.ItemContainerStyleSelector>
<StaticResource ResourceKey="NoteStyleSelector" />
</TreeView.ItemContainerStyleSelector>
</TreeView>
NoteStyleSelector:
public sealed class NoteStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
FrameworkElement fe = container as FrameworkElement;
if (fe!= null)
{
if (item is Note)
{ return (Style)fe.FindResource("NoteStyle"); }
if (item is NoteFolder)
{ return (Style)fe.FindResource("NoteFolderStyle"); }
}
return base.SelectStyle(item, container);
}
}
在放置处理程序中:
currentFolder.Nodes.Add(pastedNode);
currentFolder.IsExpanded = true;
currentNode.IsSelected = true;
数据案例:
public class Note : INotifyPropertyChanged
{
// Only the IsSelected property because a Note can not be expanded
public bool IsSelected { get { /* ... */ } set { /* ... */ } }
}
public class NoteFolder : INotifyPropertyChanged
{
public bool IsSelected { get { /* ... */ } set { /* ... */ } }
public bool IsExpanded { get { /* ... */ } set { /* ... */ } }
}
The only problem is that the HierarchicalDataTemplate does not
implement a TreeViewItem directly, so how can I access these
properties in the following code?
您可以在 TreeView.ItemContainerStyle 中做到这一点:
<TreeView ItemTemplate="{StaticResource ResourceKey=treeViewDataTemplate}"
ItemsSource="{Binding Data}"
Name="trvTreeView">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}" />
<EventSetter Event="Expanded" Handler="TreeViewItem_Expanded" />
<EventSetter Event="Collapsed" Handler="TreeViewItem_Collapsed" />
<EventSetter Event="PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
在后面的代码中:
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem treeViewItem = e.OriginalSource as TreeViewItem;
if (treeViewItem != null)
{
BaseObjectExplorerNode baseObjectExplorerNode = treeViewItem.Header as BaseObjectExplorerNode;
if (baseObjectExplorerNode != null)
{
baseObjectExplorerNode.IsExpanded = true;
}
}
}
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
{
TreeViewItem treeViewItem = e.OriginalSource as TreeViewItem;
if (treeViewItem != null)
{
BaseObjectExplorerNode baseObjectExplorerNode = treeViewItem.Header as BaseObjectExplorerNode;
if (baseObjectExplorerNode != null)
{
baseObjectExplorerNode.IsExpanded = false;
}
}
}
然后例如:
root.IsExpanded = true;
我有一个 TreeView
打算在两个层面上运作。对于他,我有两个 HierarchicalDataTemplate
和两个自定义类型。 ItemsSource
链接到 ObservableCollection
,一切正常。我只是不知道如何从代码隐藏中选择或扩展节点。某处提到了将 IsExpanded
和 IsSelected
属性绑定到我的自定义类型中的相应属性的一个非常好的主意。唯一的问题是 HierarchicalDataTemplate
没有直接实现 TreeViewItem
,那么如何在下面的代码中访问这些属性?
<TreeView Name="treeViewNotes" AllowDrop="True" PreviewMouseLeftButtonDown="treeViewNotes_PreviewMouseLeftButtonDown" PreviewMouseMove="treeViewNotes_PreviewMouseMove" Drop="treeViewNotes_Drop" DragEnter="treeViewNotes_DragEnter">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type dataclasses:NoteFolder}" ItemsSource="{Binding Notes}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding TreeViewIcon}" Tag="{Binding Self}"/>
<TextBlock Text="{Binding Title}" Tag="{Binding Self}" Margin="3"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type dataclasses:Note}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding TreeViewIcon}" Tag="{Binding Self}"/>
<TextBlock Text="{Binding Title}" Tag="{Binding Self}" Margin="3"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
我的目标是在创建新的 Note
并将其添加到某些 NoteFolder
时,选择 Note
并扩展 Folder
。进一步改进 UI 对拖放的响应也需要同样的方法。
您可以尝试按以下方式更改 TreeView 的 ItemContainerStyle,以便其 IsExpanded 和 IsSelected 属性绑定到 DataContext 的 IsExpanded 和 IsSelected:
<TreeView x:Name="..." ItemsSource="{Binding RootNode}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- Items in the ItemsSource need to have these properties for the binding to work -->
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<!-- You can also optionally change some style values based on IsSelected and IsExpanded values -->
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="BorderThickness" Value="4 0 0 1"/>
<Setter Property="BorderBrush" Value="DeepSkyBlue"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="False">
<Setter Property="BorderThickness" Value="4 0 0 1 "/>
<Setter Property="BorderBrush" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate>
...
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
当然,来自 ItemSource 并存在于层次结构中的每个项目都需要具有这些属性。
我没有发现任何问题。您可以创建一个 StyleSelector
、两个 Style
并绑定到属性。
XAML:
<TreeView Name="treeViewNotes" AllowDrop="True" PreviewMouseLeftButtonDown="treeViewNotes_PreviewMouseLeftButtonDown" PreviewMouseMove="treeViewNotes_PreviewMouseMove" Drop="treeViewNotes_Drop" DragEnter="treeViewNotes_DragEnter">
<TreeView.Resources>
<!-- StyleSelector for containers -->
<notdataclasses:NoteStyleSelector x:Key="NoteStyleSelector" />
<!-- Style for a NoteFolder's container -->
<Style x:Key="NoteFolderStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}" />
<Setter Property="ItemContainerStyleSelector" Value="{StaticResource NoteFolderStyle}" />
</Style>
<!-- Style for a Note's container -->
<Style x:Key="NoteStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
</Style>
<!-- ... -->
</TreeView.Resources>
<TreeView.ItemContainerStyleSelector>
<StaticResource ResourceKey="NoteStyleSelector" />
</TreeView.ItemContainerStyleSelector>
</TreeView>
NoteStyleSelector:
public sealed class NoteStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
FrameworkElement fe = container as FrameworkElement;
if (fe!= null)
{
if (item is Note)
{ return (Style)fe.FindResource("NoteStyle"); }
if (item is NoteFolder)
{ return (Style)fe.FindResource("NoteFolderStyle"); }
}
return base.SelectStyle(item, container);
}
}
在放置处理程序中:
currentFolder.Nodes.Add(pastedNode);
currentFolder.IsExpanded = true;
currentNode.IsSelected = true;
数据案例:
public class Note : INotifyPropertyChanged
{
// Only the IsSelected property because a Note can not be expanded
public bool IsSelected { get { /* ... */ } set { /* ... */ } }
}
public class NoteFolder : INotifyPropertyChanged
{
public bool IsSelected { get { /* ... */ } set { /* ... */ } }
public bool IsExpanded { get { /* ... */ } set { /* ... */ } }
}
The only problem is that the HierarchicalDataTemplate does not implement a TreeViewItem directly, so how can I access these properties in the following code?
您可以在 TreeView.ItemContainerStyle 中做到这一点:
<TreeView ItemTemplate="{StaticResource ResourceKey=treeViewDataTemplate}"
ItemsSource="{Binding Data}"
Name="trvTreeView">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded}" />
<EventSetter Event="Expanded" Handler="TreeViewItem_Expanded" />
<EventSetter Event="Collapsed" Handler="TreeViewItem_Collapsed" />
<EventSetter Event="PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
在后面的代码中:
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem treeViewItem = e.OriginalSource as TreeViewItem;
if (treeViewItem != null)
{
BaseObjectExplorerNode baseObjectExplorerNode = treeViewItem.Header as BaseObjectExplorerNode;
if (baseObjectExplorerNode != null)
{
baseObjectExplorerNode.IsExpanded = true;
}
}
}
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
{
TreeViewItem treeViewItem = e.OriginalSource as TreeViewItem;
if (treeViewItem != null)
{
BaseObjectExplorerNode baseObjectExplorerNode = treeViewItem.Header as BaseObjectExplorerNode;
if (baseObjectExplorerNode != null)
{
baseObjectExplorerNode.IsExpanded = false;
}
}
}
然后例如:
root.IsExpanded = true;