WPF 的 TreeView.BringIntoView() 方法經常失效,無法正確捲動至指定項目。本文提供一個可靠的替代方案:透過擴充方法 SelectItem() 遞迴展開並選取目標項目。
public static class TreeViewHelper {
/// 展開 TreeView 所有子節點
public static void ExpandAll(this TreeView treeView) {
ExpandSubContainers(treeView);
}
private static void ExpandSubContainers(ItemsControl parent) {
foreach (object item in parent.Items) {
TreeViewItem container = parent.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (container != null && container.Items.Count > 0) {
container.IsExpanded = true;
if (container.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) {
EventHandler handler = null;
handler = (s, e) => {
if (container.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) {
ExpandSubContainers(container);
container.ItemContainerGenerator.StatusChanged -= handler;
}
};
container.ItemContainerGenerator.StatusChanged += handler;
} else {
ExpandSubContainers(container);
}
}
}
}
/// 搜尋並選取指定項目,自動展開父節點
public static void SelectItem(this TreeView treeView, object item) {
ExpandAndSelectItem(treeView, item);
}
private static bool ExpandAndSelectItem(ItemsControl parent, object target) {
// 1. 檢查目前層級
foreach (object item in parent.Items) {
TreeViewItem container = parent.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (item == target && container != null) {
container.IsSelected = true;
container.BringIntoView();
container.Focus();
return true;
}
}
// 2. 遞迴搜尋子層級
foreach (object item in parent.Items) {
TreeViewItem container = parent.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (container != null && container.Items.Count > 0) {
bool wasExpanded = container.IsExpanded;
container.IsExpanded = true;
if (container.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) {
EventHandler handler = null;
handler = (s, e) => {
if (container.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) {
if (!ExpandAndSelectItem(container, target))
container.IsExpanded = wasExpanded;
container.ItemContainerGenerator.StatusChanged -= handler;
}
};
container.ItemContainerGenerator.StatusChanged += handler;
} else {
if (!ExpandAndSelectItem(container, target))
container.IsExpanded = wasExpanded;
else
return true;
}
}
}
return false;
}
}
// 展開所有節點
myTreeView.ExpandAll();
// 選取特定資料項目(自動展開路徑)
myTreeView.SelectItem(targetDataItem);
ItemContainerGenerator.StatusChanged 事件,等待容器產生後再遞迴展開,解決 UI 虛擬化導致容器尚未建立的問題。IsExpanded 還原為原本狀態,避免不必要的展開。此方案比內建 BringIntoView 更可靠,適用於資料繫結與虛擬化的 TreeView 場景。