1. 程式人生 > >WPF TreeView 虛擬化-設定滾動到選中項

WPF TreeView 虛擬化-設定滾動到選中項

前言

列表滾動到具體的資料項?

ListBox提供了簡易快捷的滾動定位函式ScrollIntoView。

TreeView樹狀結構列表,則沒有此類方法,無法與ListBox一樣,直接設定滾動到具體的資料項。

同時,SelectedItem也是隻讀的,無法設定SelectedItem來間接的設定滾動項。

TreeView滾動定位

1. 對TreeViewItem新增一個附加屬性IsScrolledToViewWhenSelected,在屬性變更事件中,新增對Loaded事件的訂閱和登出

 1     static void OnIsScrolledToViewWhenSelectedChanged(
 2         DependencyObject depObj, DependencyPropertyChangedEventArgs e)
 3     {
 4         if (depObj is TreeViewItem treeViewItem && e.NewValue is bool isIntoViewWhenSelected)
 5         {
 6             treeViewItem.Loaded -= TreeViewItem_Loaded;
 7             if (isIntoViewWhenSelected)
 8             {
 9                 treeViewItem.Loaded += TreeViewItem_Loaded;
10             }
11         }
12     }

2. 在Loaded事件中,根據當前TreeViewItem是否選中,呼叫是否滾動到檢視區域的邏輯

 1     static void TreeViewItem_Loaded(object sender, RoutedEventArgs e)
 2     {
 3         var treeViewItem = e.OriginalSource as TreeViewItem;
 4         if (treeViewItem != null)
 5         {
 6             treeViewItem.Loaded -= TreeViewItem_Loaded;
 7             if (treeViewItem.IsSelected)
 8             {
 9                 treeViewItem.BringIntoView();
10             }
11         }
12     }

此處,對TreeView新增附加屬性處理,也是可以的

虛擬化後的TreeView滾動定位

因開啟了虛擬化,介面上不在當前視覺內的資料項,沒有生成相應的視覺樹,即無法找到TreeViewItem。

所以虛擬化後,使用TreeViewItem新增附加屬性,而不是TreeView。因為TreeView無法對視覺樹外的項,查詢並定位;而TreeViewItem...emmm~可以使用黑科技處理,詳情如下

1. 在上面邏輯的基礎上,新增對虛擬化的處理。

此處添加了對虛擬化的判斷,虛擬化時如果已經完成了滾動定位,則對後續的邏輯直接跳過,避免選中項的定位被幹擾。

 1     static void OnIsScrolledToViewWhenSelectedChanged(
 2         DependencyObject depObj, DependencyPropertyChangedEventArgs e)
 3     {
 4         if (depObj is TreeViewItem treeViewItem && e.NewValue is bool isIntoViewWhenSelected)
 5         {
 6             treeViewItem.Loaded -= TreeViewItem_Loaded;
 7             //獲取父控制元件TreeView
 8             var treeView = treeViewItem.FindVisualParent<TreeView>();
 9             if (isIntoViewWhenSelected)
10             {
11                 //開啟了虛擬化且未完成滾動,直接返回
12                 var isOpeningVitualizing = ScrollViewer.GetCanContentScroll(treeView) && VirtualizingPanel.GetIsVirtualizing(treeView);
13                 if (isOpeningVitualizing && GetHasSetSelectedItemVisible(treeView))
14                 {
15                     return;
16                 }
17                 treeViewItem.Loaded += TreeViewItem_Loaded;
18             }
19         }
20     }

2. 在之前邏輯的基礎上,新增虛擬化的判斷

如果開啟了虛擬化,

  • 列表項未選中時,設定滾動到檢視中
  • 列表項選中時,設定已完成,並滾動到檢視中

黑科技:

列表資料載入時,每項都滾動到檢視中。

而虛擬化列表實際上初始化的項個數,在預設設定下,比視覺化區域的項個數要多很一部分。

所以在單個數據載入時,設定滾動檢視,會帶動更多原本不在檢視內的資料項,生成視覺樹。

 1     static void TreeViewItem_Loaded(object sender, RoutedEventArgs e)
 2     {
 3         var treeViewItem = e.OriginalSource as TreeViewItem;
 4         if (treeViewItem != null)
 5         {
 6             treeViewItem.Loaded -= TreeViewItem_Loaded;
 7             //獲取父控制元件TreeView
 8             var treeView = treeViewItem.FindVisualParent<TreeView>();
 9             //是否開啟了虛擬化
10             var isOpeningVirtualizing = ScrollViewer.GetCanContentScroll(treeView) && VirtualizingPanel.GetIsVirtualizing(treeView);
11             if (isOpeningVirtualizing)
12             {
13                 if (treeViewItem.IsSelected)
14                 {
15                     //設定已完成滾動,減少剩餘項的載入判斷
16                     SetHasSetSelectedItemVisible(treeView, true);
17                     treeViewItem.BringIntoView();
18                 }
19                 else
20                 {
21                     treeViewItem.BringIntoView();
22                 }
23             }
24             else if(treeViewItem.IsSelected)
25             {
26                 treeViewItem.BringIntoView();
27             }
28         }
29     }

 WPF 列表開啟虛擬化的方式

關鍵字:TreeView虛擬化、滾動到選