1. 程式人生 > >WPF利用VisualTreeHelper遍歷尋找物件的子級物件或者父級物件

WPF利用VisualTreeHelper遍歷尋找物件的子級物件或者父級物件

簡介

  本文將完整敘述我利用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程式碼如下:  

<Window x:Class="VisualTreeHelper.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 Name="TopGrid"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid > <Button Content="Button1" Name="btn_One" VerticalAlignment="Center" HorizontalAlignment="Center"> </Button> </Grid> <Grid Grid.Row="1"> <Button Content="Button2" Name="btn_Two" VerticalAlignment="Center" HorizontalAlignment="Center"> </Button> </Grid> </Grid> </Window>

遍歷尋找子級物件

  現在我們來尋找TopGrid所有Button子級物件,並輸出它們的名稱。
  開啟MainWindow.xaml.cs檔案,新增尋找子級物件的程式碼如下:

/// <summary>
/// 利用visualtreehelper尋找物件的子級物件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
List<T> FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
    try
    {
        List<T> TList = new List<T> { };
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
            if (child != null && child is T)
            {
                TList.Add((T)child);
                List<T> childOfChildren = FindVisualChild<T>(child);
                if (childOfChildren != null)
                {
                    TList.AddRange(childOfChildren);
                }
            }
            else
            {
                List<T> childOfChildren = FindVisualChild<T>(child);
                if (childOfChildren != null)
                {
                    TList.AddRange(childOfChildren);
                }
            }
        }
        return TList;
    }
    catch (Exception ee)
    {
        MessageBox.Show(ee.Message);
        return null;
    }
}

  在btn_One_Click事件裡面書寫程式碼如下:

/// <summary>
/// 窗體載入事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_One_Click(object sender, RoutedEventArgs e)
{
    string btnName = "";
    List<Button> btnList = FindVisualChild<Button>(TopGrid);
    foreach (Button item in btnList)
    {
        btnName += string.IsNullOrEmpty(btnName) ? item.Name.ToString() : "," + item.Name.ToString();
    }
    Show(string.Format(TopGrid.Name.ToString()+"共有{0}個Button,名稱分別為{1}", btnList.Count, btnName));
}

  執行程式,點選Button1,結果如下圖:
  
  這裡寫圖片描述
  
  結果表明遍歷成功。
  

遍歷尋找父級物件

  現在我們來遍歷Button2的父級物件,獲得其所有父級物件的資訊,並且展示。
  開啟MainWindow.xaml.cs檔案,新增尋找父級物件的程式碼如下:

/// <summary>
/// 利用VisualTreeHelper尋找指定依賴物件的父級物件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static List<T> FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
{
    try
    {
        List<T> TList = new List<T> { };
        DependencyObject parent = VisualTreeHelper.GetParent(obj);
        if (parent != null && parent is T)
        {
            TList.Add((T)parent);
            List<T> parentOfParent = FindVisualParent<T>(parent);
            if (parentOfParent !=null)
            {
                TList.AddRange(parentOfParent);
            }
        }
        else if ( parent != null )
        {
             List<T> parentOfParent = FindVisualParent<T>(parent);
             if (parentOfParent != null)
             {
                 TList.AddRange(parentOfParent);
             }
        }
        return TList;
    }
    catch (Exception ee)
    {
        MessageBox.Show(ee.Message);
        return null;
    }
}

  在btn_Two_Click中新增程式碼如下:

/// <summary>
/// 遍歷Button2父級物件資訊
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Two_Click(object sender, RoutedEventArgs e)
{
    string parentName = "";
    List<Grid> gridList = FindVisualParent<Grid>(btn_Two);
    foreach (Grid item in gridList)
    {
        parentName += string.IsNullOrEmpty(parentName) ? item.Name.ToString() : "," + item.Name.ToString();
    }
    MessageBox.Show(string.Format(btn_Two.Name.ToString() + "共有{0}個邏輯父級,名稱分別為{1}", gridList.Count, parentName));
}

  執行程式,點選Button2,效果如下:
這裡寫圖片描述
  結果表明遍歷成功。

總結

  通過上述的方法我們就可以隨心所欲地獲取我們想要的控制元件物件,並對其進行操作,包括自定義的DataTemplate中的控制元件都可以獲取。