WPF利用VisualTreeHelper遍历寻找对象的子级对象或者父级对象

本文介绍如何使用WPF中的VisualTreeHelper类遍历控件的子级和父级对象,包括自定义DataTemplate中的控件。通过示例代码展示了如何获取控件的所有子级和父级对象。
原文: 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中的控件都可以获取。

posted on 2018-08-14 10:58 NET未来之路 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/9473290.html

<think>我们正在处理一个关于WPF中如何通过代码获取控件的控件的问题。根据提供的引用,有几种方法可以实现这个目标。引用[1]提供了一个通用的方法`FindVisualParent<T>`,它使用`VisualTreeHelper.GetParent`递归地向上遍历视觉树,直到找到指定类型的控件。引用[5]提到了一种通过绑定来获取控件数据的方法,但这是在XAML中,而不是代码中。所以我们的重点在引用[1]的方法。因此,我们可以使用以下方法:1.使用`VisualTreeHelper.GetParent`方法获取直接的控件。2.然后判断这个控件是否是我们需要的类型,如果是则返回,否则继续向上查找。我们可以根据引用[1]中的代码示例来实现。另外,注意引用[3]中也有一个类似的方法,但是用于查找控件的,而我们需要的是控件。因此,我们可以编写一个类似引用[1]的辅助方法。具体步骤:1.获取控件的直接控件。2.如果控件为null,则返回null(表示没有找到)。3.判断控件是否是目标类型T,如果是,则返回它。4.如果不是,则递归地在这个控件上继续查找。注意:这个方法是一个泛型方法,可以指定要查找的控件类型(例如Window,UserControl,Grid等)。代码实现如下(注意:需要引入System.Windows.Media命名空间):</think>### 如何在 WPF 中通过代码获取控件的控件? 在 WPF 中获取控件的控件主要通过**视觉树遍历**实现,以下是三种常用方法: #### 1. 通用控件查找方法(推荐) 使用 `VisualTreeHelper.GetParent()` 递归查找指定类型的控件: ```csharp // 泛型方法查找视觉树 public static T FindVisualParent<T>(DependencyObject child) where T : DependencyObject { DependencyObject parentObject = VisualTreeHelper.GetParent(child); if (parentObject == null) return null; if (parentObject is T parent) return parent; return FindVisualParent<T>(parentObject); } ``` **使用示例:** ```csharp // 获取按钮的Grid Button childButton = ...; Grid parentGrid = FindVisualParent<Grid>(childButton); // 获取任意控件的UserControl UserControl userControl = FindVisualParent<UserControl>(childControl); ``` #### 2. 直接获取逻辑 适用于简单场景(非模板化控件): ```csharp // 获取直接(仅限视觉树第一层) DependencyObject immediateParent = VisualTreeHelper.GetParent(childControl); // 获取逻辑(如ContentControl的内容) FrameworkElement logicalParent = childControl.Parent as FrameworkElement; ``` #### 3. 通过绑定查找(XAML方式) 在XAML中声明式获取(无需代码): ```xml <!-- 在控件绑定中获取UserControl的高度 --> <Button MaxHeight="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" /> ``` ### 关键说明 1. **视觉树 vs 逻辑树** - 视觉树(`VisualTreeHelper`)包含所有渲染元素(包括模板部件) - 逻辑树(`.Parent`属性)仅包含声明式层 - 模板中的控件必须使用视觉树遍历[^1][^3] 2. **递归终止条件** - 当 `VisualTreeHelper.GetParent()` 返回 `null` 时到达根节点 - 需处理控件类型不匹配的情况(通过递归继续查找)[^1] 3. **性能考虑** - 避免在频繁触发的事件(如MouseMove)中深度遍历 - 复杂界面建议缓存查找结果 > 提示:查找 `Window` 可直接使用 `Window.GetWindow(childControl)` 方法[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值