WPF Treeview控件开虚拟化后定位节点

不开虚拟化,可以用下面的方法直接定位

XML 复制代码
<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TreeView Name="myTreeView">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>
cs 复制代码
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            
            // 示例数据
            var root = new TreeNode { Name = "Root" };
            for (int i = 0; i < 1000; i++)
            {
                var child = new TreeNode { Name = "Child " + i };
                for (int j = 0; j < 10; j++)
                {
                    child.Children.Add(new TreeNode { Name = "SubChild " + j });
                }
                root.Children.Add(child);
            }

            myTreeView.ItemsSource = new ObservableCollection<TreeNode> { root };

            // 等待数据绑定完成后选中并定位到指定节点
            this.Loaded += (s, e) => {
                var nodeToSelect = root.Children[51].Children[5]; // 举例:选中第51个Child的第6个SubChild
                SelectAndScrollToNode(myTreeView, nodeToSelect);
            };
        }

        private void SelectAndScrollToNode(TreeView treeView, TreeNode targetNode)
        {
            // 展开到目标节点
            ExpandAndSelect(treeView, targetNode);
        }

   

        public static IEnumerable<T> FindLogicalChildren<T>(DependencyObject obj) where T : DependencyObject
        {
            if (obj != null)
            {
                if (obj is T)
                    yield return obj as T;
                foreach (DependencyObject child in LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>())
                    foreach (T c in FindLogicalChildren<T>(child))
                        yield return c;
            }
        }



        private void ExpandAndSelect(TreeView treeView, TreeNode targetNode)
        {
            foreach (var node in (ObservableCollection<TreeNode>)treeView.ItemsSource)
            {
                if (ExpandAndSelectInternal(treeView, node, targetNode))
                    break;
            }
        }

        private bool ExpandAndSelectInternal(ItemsControl parent, TreeNode currentNode, TreeNode targetNode)
        {
            if (currentNode == targetNode)
            {
                if (parent.ItemContainerGenerator.ContainerFromItem(currentNode) is TreeViewItem )
                {
                    var currentItem = parent.ItemContainerGenerator.ContainerFromItem(currentNode) as TreeViewItem;
                    currentItem.IsSelected = true;
                    currentItem.BringIntoView();
                    return true;
                }
            }

            if (parent.ItemContainerGenerator.ContainerFromItem(currentNode) is TreeViewItem)
            {
                var item = parent.ItemContainerGenerator.ContainerFromItem(currentNode) as TreeViewItem;
                item.IsExpanded = true;
                item.UpdateLayout();
                
                foreach (var childNode in currentNode.Children)
                {
                    if (ExpandAndSelectInternal(item, childNode, targetNode))
                    {
                        item.IsExpanded = true;
                        item.UpdateLayout();
                        if (item.ItemContainerGenerator.ContainerFromItem(targetNode) is TreeViewItem )
                        {
                            var targetItem = item.ItemContainerGenerator.ContainerFromItem(targetNode) as TreeViewItem;
                            targetItem.IsSelected = true;
                            targetItem.BringIntoView();
                        }
                        return true;
                    }
                }
            }

            return false;
        }
    }

 
    public class TreeNode
    {
        public TreeNode()
        {
            Children = new ObservableCollection<TreeNode>();
        }
        public string Name { get; set; }
        public ObservableCollection<TreeNode> Children { get; set; }
    }

但是如果开了虚拟化,需要默认显示的节点很可能不在可视化树上,所以这套方法就不能用了。

解决办法:计算默认选中项的垂直位置,直接操作滚动条移动到目标节点。

cs 复制代码
 private void SelectAndScrollToNode(TreeView treeView, TreeNode targetNode)
        {
            // 展开到目标节点
            ExpandAndSelect(treeView, targetNode);
            //根据targetNode计算滚动条的位置,这里就直接取中间位置了
            TreeViewAutomationPeer lvap = new TreeViewAutomationPeer(treeView);
            var svap = lvap.GetPattern(PatternInterface.Scroll) as ScrollViewerAutomationPeer;
            var scroll = svap.Owner as ScrollViewer;
            double hight = scroll.ScrollableHeight;
            scroll.ScrollToVerticalOffset(hight / 2);
        }
相关推荐
麻花201312 小时前
WPF的表格控件 FlexGrid设置行的高度自适应
wpf
挖石油的问天14 小时前
WPF(C#)中的组件1:ItemsControl
前端·c#·wpf
就是有点傻1 天前
C#中面试的常见问题004
面试·职场和发展·c#·wpf
martian6652 天前
C# 开发应用篇——C# 基于WPF实现数据记录导出excel详解
c#·wpf·excel
就是有点傻2 天前
C#中面试的常见问题005
开发语言·面试·c#·wpf
gjhaoiaao2 天前
《白帽子讲Web安全》13-14章
安全·web安全·php·wpf
就是有点傻2 天前
WPF中的Button按钮中的PreviewMouseLeftButtonDown事件和MouseLeftButtonDown的区别
c#·wpf
海的那边-2 天前
WPF 加载页面的三种方式(瞬时加载,延迟加载,异步行为)
wpf
lfw20192 天前
WPF ItemsControl控件
wpf
就是有点傻2 天前
C#中面试的常见问题007
面试·c#·wpf