首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用WPF在虚拟化TreeView中选择节点

使用WPF在虚拟化TreeView中选择节点
EN

Stack Overflow用户
提问于 2008-10-08 16:25:27
回答 6查看 13K关注 0票数 19

有没有办法在虚拟化TreeView中手动选择一个节点,然后将其带到视图中?

我在TreeView中使用的数据模型是基于VM-M-V模型实现的。每个TreeViewItem的IsSelected属性都绑定到ViewModel中相应的属性。我还为TreeView的ItemSelected事件创建了一个侦听器,其中我为选定的TreeViewItem调用BringIntoView()。

这种方法的问题似乎是在创建实际的TreeViewItem之前不会引发ItemSelected事件。因此,在启用虚拟化的情况下,节点选择不会做任何事情,直到TreeView滚动足够多,然后当事件最终被引发时,它会“神奇地”跳到所选的节点。

我真的很想使用虚拟化,因为我的树中有数千个节点,而且在启用虚拟化后,我已经看到了令人印象深刻的性能改进。

EN

Stack Overflow用户

发布于 2020-10-30 07:06:28

@splintor's excellent answer的更新,使用了一些现代的C#特性,并且没有任何反射。

代码语言:javascript
运行
复制
public class Node
{
    public Node Parent { get; set; }
}

public class NodeTreeSelectionBehavior : Behavior<TreeView>
{
    public Node SelectedItem
    {
        get { return (Node)GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register(
            "SelectedItem",
            typeof(Node),
            typeof(NodeTreeSelectionBehavior),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));

    private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(e.NewValue is Node newNode)) return;

        var treeView = ((NodeTreeSelectionBehavior)d).AssociatedObject;

        var ancestors = new List<Node> { newNode };
        var parent = newNode;
        while ((parent = parent.Parent) != null)
        {
            ancestors.Insert(0, parent);
        }

        var currentParent = treeView as ItemsControl;
        foreach (var node in ancestors)
        {
            // first try the easy way
            var newParent = currentParent.ItemContainerGenerator.ContainerFromItem(node) as TreeViewItem;
            if (newParent == null)
            {
                // if this failed, it's probably because of virtualization, and we will have to do it the hard way.
                // see also the question at http://stackoverflow.com/q/183636/46635
                var itemsPresenter = (ItemsPresenter)currentParent.Template.FindName("ItemsHost", currentParent);
                var virtualizingPanel = (VirtualizingPanel)VisualTreeHelper.GetChild(itemsPresenter, 0);
                var index = currentParent.Items.IndexOf(node);
                if (index < 0)
                {
                    throw new InvalidOperationException("Node '" + node + "' cannot be fount in container");
                }
                virtualizingPanel.BringIndexIntoViewPublic(index);
                newParent = currentParent.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
            }

            if (newParent == null)
            {
                throw new InvalidOperationException("Tree view item cannot be found or created for node '" + node + "'");
            }

            if (node == newNode)
            {
                newParent.IsSelected = true;
                newParent.BringIntoView();
                break;
            }

            newParent.IsExpanded = true;
            currentParent = newParent;
        }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
    }

    private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        SelectedItem = e.NewValue as Node;
    }
}

以同样的方式使用:

代码语言:javascript
运行
复制
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             xmlns:local="clr-namespace:MyProject">
    <Grid>
        <TreeView ItemsSource="{Binding MyItems}"
                  ScrollViewer.CanContentScroll="True"
                  VirtualizingStackPanel.IsVirtualizing="True"
                  VirtualizingStackPanel.VirtualizationMode="Recycling">
            <i:Interaction.Behaviors>
                <local:NodeTreeSelectionBehavior SelectedItem="{Binding MySelectedItem}" />
            </i:Interaction.Behaviors>
        </TreeView>
    <Grid>
<UserControl>
票数 0
EN
查看全部 6 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/183636

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档