簡介
本文將完整敘述我利用VisualTreeHelper實現題述功能的全部過程,想直接看函數實現的朋友可以跳到函數實現部分。
或者直接在GitHub上下載源碼。
在WPF中我們經常會遇到這種情況:當我們嘗試着去尋找窗體或者頁面中某個控件的子控件或者父控件的時候,我們只能尋找到它的第一級的邏輯子級對象或者父級對象。當我們想更深入的時候,就沒有辦法了。
甚至在我們自定義的DataTemplate中的控件,我們都沒辦法對其訪問。比如在ListView中自定義的控件,我們就沒辦法獲取指定位置的控件了。相關例子可以參見我的博文: WPF中自定義的DataTemplate中的控件,在Window_Loaded事件中加載機制初探 。
本文將探討解決方案。
VisualTreeHelper
微軟在VisualTreeHelper類中,提供了一些實用工具方法,用於執行涉及可視化樹中的節點的常規任務,VisualTreeHelper 類中的一些方法可以接受表示任意一種可視對象類型的 DependencyObject 值。
這里我們將要用到兩個方法分別是:VisualTreeHelper.GetChild()和VisualTreeHelper.GetParent()。
使用VisualTreeHelper
模擬場景搭建
新建一個WPF工程,命名為VisualTreeHelperDemo。
假設我們有如下如所示的一個主窗體,窗體的內容容器為一個name=”TopGrid”的Grid控件,它包含了上下兩個子級Grid,每個子級Grid中各自包含了一個Button。
MainWindow.xaml代碼如下:
1 <Window x:Class="VisualTreeHelper.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="350" Width="525"> 5 <Grid Name="TopGrid"> 6 <Grid.RowDefinitions> 7 <RowDefinition></RowDefinition> 8 <RowDefinition></RowDefinition> 9 </Grid.RowDefinitions> 10 <Grid > 11 <Button Content="Button1" Name="btn_One" VerticalAlignment="Center" HorizontalAlignment="Center"> 12 </Button> 13 </Grid> 14 <Grid Grid.Row="1"> 15 <Button Content="Button2" Name="btn_Two" VerticalAlignment="Center" HorizontalAlignment="Center"> 16 </Button> 17 </Grid> 18 </Grid> 19 </Window>
遍歷尋找子級對象
現在我們來尋找TopGrid所有Button子級對象,並輸出它們的名稱。
打開MainWindow.xaml.cs文件,添加尋找子級對象的代碼如下:
1 /// <summary> 2 /// 利用visualtreehelper尋找對象的子級對象 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 /// <param name="obj"></param> 6 /// <returns></returns> 7 List<T> FindVisualChild<T>(DependencyObject obj) where T : DependencyObject 8 { 9 try 10 { 11 List<T> TList = new List<T> { }; 12 for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) 13 { 14 DependencyObject child = VisualTreeHelper.GetChild(obj, i); 15 if (child != null && child is T) 16 { 17 TList.Add((T)child); 18 List<T> childOfChildren = FindVisualChild<T>(child); 19 if (childOfChildren != null) 20 { 21 TList.AddRange(childOfChildren); 22 } 23 } 24 else 25 { 26 List<T> childOfChildren = FindVisualChild<T>(child); 27 if (childOfChildren != null) 28 { 29 TList.AddRange(childOfChildren); 30 } 31 } 32 } 33 return TList; 34 } 35 catch (Exception ee) 36 { 37 MessageBox.Show(ee.Message); 38 return null; 39 } 40 }
在btn_One_Click事件里面書寫代碼如下:
1 /// <summary> 2 /// 窗體加載事件 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 private void btn_One_Click(object sender, RoutedEventArgs e) 7 { 8 string btnName = ""; 9 List<Button> btnList = FindVisualChild<Button>(TopGrid); 10 foreach (Button item in btnList) 11 { 12 btnName += string.IsNullOrEmpty(btnName) ? item.Name.ToString() : "," + item.Name.ToString(); 13 } 14 Show(string.Format(TopGrid.Name.ToString()+"共有{0}個Button,名稱分別為{1}", btnList.Count, btnName)); 15 }
運行程序,點擊Button1,結果如下圖:
結果表明遍歷成功。
遍歷尋找父級對象
現在我們來遍歷Button2的父級對象,獲得其所有父級對象的信息,並且展示。
打開MainWindow.xaml.cs文件,添加尋找父級對象的代碼如下:
1 /// <summary> 2 /// 利用VisualTreeHelper尋找指定依賴對象的父級對象 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 /// <param name="obj"></param> 6 /// <returns></returns> 7 public static List<T> FindVisualParent<T>(DependencyObject obj) where T : DependencyObject 8 { 9 try 10 { 11 List<T> TList = new List<T> { }; 12 DependencyObject parent = VisualTreeHelper.GetParent(obj); 13 if (parent != null && parent is T) 14 { 15 TList.Add((T)parent); 16 List<T> parentOfParent = FindVisualParent<T>(parent); 17 if (parentOfParent !=null) 18 { 19 TList.AddRange(parentOfParent); 20 } 21 } 22 else if ( parent != null ) 23 { 24 List<T> parentOfParent = FindVisualParent<T>(parent); 25 if (parentOfParent != null) 26 { 27 TList.AddRange(parentOfParent); 28 } 29 } 30 return TList; 31 } 32 catch (Exception ee) 33 { 34 MessageBox.Show(ee.Message); 35 return null; 36 } 37 }
在btn_Two_Click中添加代碼如下:
1 /// <summary> 2 /// 遍歷Button2父級對象信息 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 private void btn_Two_Click(object sender, RoutedEventArgs e) 7 { 8 string parentName = ""; 9 List<Grid> gridList = FindVisualParent<Grid>(btn_Two); 10 foreach (Grid item in gridList) 11 { 12 parentName += string.IsNullOrEmpty(parentName) ? item.Name.ToString() : "," + item.Name.ToString(); 13 } 14 MessageBox.Show(string.Format(btn_Two.Name.ToString() + "共有{0}個邏輯父級,名稱分別為{1}", gridList.Count, parentName)); 15 }
運行程序,點擊Button2,效果如下:
結果表明遍歷成功。
總結
通過上述的方法我們就可以隨心所欲地獲取我們想要的控件對象,並對其進行操作,包括自定義的DataTemplate中的控件都可以獲取。