当 ItemsSource = 自定义对象集合时,如何使 TreeView 节点 selected/expanded?

How to make TreeView node selected/expanded when ItemsSource = custom object collection?

我有一个 TreeView 打算在两个层面上运作。对于他,我有两个 HierarchicalDataTemplate 和两个自定义类型。 ItemsSource 链接到 ObservableCollection,一切正常。我只是不知道如何从代码隐藏中选择或扩展节点。某处提到了将 IsExpandedIsSelected 属性绑定到我的自定义类型中的相应属性的一个非常好的主意。唯一的问题是 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;