不开虚拟化,可以用下面的方法直接定位
<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>
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个SubChildSelectAndScrollToNode(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; }}
但是如果开了虚拟化,需要默认显示的节点很可能不在可视化树上,所以这套方法就不能用了。
解决办法:计算默认选中项的垂直位置,直接操作滚动条移动到目标节点。
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);}