由于这个问题在搜索时最突出,我想发布一个答案。
当我使用带有 HierarchicalDataTemplate 的数据绑定 TreeView 时,lars 的上述帖子对我不起作用,因为 Items 集合返回实际的数据绑定项,而不是 TreeViewItem。
我最终解决了这个问题,方法是使用 ItemContainerGenerator 处理单个数据项,并使用 VisualTreeHelper 搜索“向上”以查找父节点(如果有)。我将它实现为一个静态帮助器类,以便我可以轻松地重用它(对我来说基本上是每个 TreeView)。
这是我的助手类:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace TreeViewHelpers
{
public static class TreeViewItemTextSearcher
{
private static bool checkIfMatchesText(TreeViewItem node, string searchterm, StringComparison comparison)
{
return node.Header.ToString().StartsWith(searchterm, comparison);
}
//https://stackoverflow.com/questions/26624982/get-parent-treeviewitem-of-a-selected-node-in-wpf
public static TreeViewItem getParentItem(TreeViewItem item)
{
try
{
var parent = VisualTreeHelper.GetParent(item as DependencyObject);
while ((parent as TreeViewItem) == null)
{
parent = VisualTreeHelper.GetParent(parent);
}
return parent as TreeViewItem;
}
catch (Exception e)
{
//could not find a parent of type TreeViewItem
return null;
}
}
private static bool tryFindChild(
int startindex,
TreeViewItem node,
string searchterm,
StringComparison comparison,
out TreeViewItem foundnode
)
{
foundnode = null;
if (!node.IsExpanded) { return false; }
for (int i = startindex; i < node.Items.Count; i++)
{
object item = node.Items[i];
object tviobj = node.ItemContainerGenerator.ContainerFromItem(item);
if (tviobj is null)
{
return false;
}
TreeViewItem tvi = (TreeViewItem)tviobj;
if (checkIfMatchesText(tvi, searchterm, comparison))
{
foundnode = tvi;
return true;
}
//recurse:
if (tryFindChild(tvi, searchterm, comparison, out foundnode))
{
return true;
}
}
return false;
}
private static bool tryFindChild(TreeViewItem node, string searchterm, StringComparison comparison, out TreeViewItem foundnode)
{
return tryFindChild(0, node, searchterm, comparison, out foundnode);
}
public static bool SearchTreeView(TreeViewItem node, string searchterm, StringComparison comparison, out TreeViewItem found)
{
//search children:
if (tryFindChild(node, searchterm, comparison, out found))
{
return true;
}
//search nodes same level as this:
TreeViewItem parent = getParentItem(node);
object boundobj = node.DataContext;
if (!(parent is null || boundobj is null))
{
int startindex = parent.Items.IndexOf(boundobj);
if (tryFindChild(startindex + 1, parent, searchterm, comparison, out found))
{
return true;
}
}
found = null;
return false;
}
}
}
我还保存了最后选择的节点,如this post中所述:
<TreeView ... TreeViewItem.Selected="TreeViewItemSelected" ... />
private TreeViewItem lastSelectedTreeViewItem;
private void TreeViewItemSelected(object sender, RoutedEventArgs e)
{
TreeViewItem tvi = e.OriginalSource as TreeViewItem;
this.lastSelectedTreeViewItem = tvi;
}
这是上面的TextInput,修改为使用这个类:
private void treeView_TextInput(object sender, TextCompositionEventArgs e)
{
if ((DateTime.Now - LastSearch).Seconds > 1) { searchterm = ""; }
LastSearch = DateTime.Now;
searchterm += e.Text;
if (lastSelectedTreeViewItem is null)
{
return;
}
TreeViewItem found;
if (TreeViewHelpers.TreeViewItemTextSearcher.SearchTreeView(
node: lastSelectedTreeViewItem,
searchterm: searchterm,
comparison: StringComparison.CurrentCultureIgnoreCase,
out found
))
{
found.IsSelected = true;
found.BringIntoView();
}
}
注意这个方案和上面的有点不同,我只搜索选中节点的子节点,和选中节点同级的节点。