不开虚拟化,可以用下面的方法直接定位
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);
}