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);
            }
            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
        {

        }
        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中的控件都可以获取。

时间: 2024-10-25 01:54:34

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

转:iframe加载的子页面里面获取父级元素窗口以及元素的高度

iframe里的js要操作父级窗口的dom,必须搞懂几个对象: parent是父窗口(如果窗口是顶级窗口,那么parent==self==top) top是最顶级父窗口(有的窗口中套了好几层frameset或者iframe) self是当前窗口(等价window) 父级页面:index.html <!doctype html> <html> <head> <meta charset="utf-8"> <title>父窗口<

子级Repeater获取 父级Repeater

第一种方法,子级Repeater中绑定父级的某个字段: <%# DataBinder.Eval((Container.NamingContainer.NamingContainer as RepeaterItem).DataItem, "ModuleName") %> 二层嵌套:<%# DataBinder.Eval((Container.Parent.Parent as RepeaterItem).DataItem, "class2") %>

小程序 - 子级页面返回父级,并把子级参数带回父级

(小的资深尚浅,不足的地方欢迎提bug,勿喷!!!) 说到页面之间的跳转,跳转中顺带些参数,在程序猿的生活中是很常用的,下面就让我们来看看吧! 这里有两种方法来解决: 方法一 就是我们常用的本地储存,在当前子级页面用( wx.setStorage  ||  wx.setStorageSync )储存好,跳转到父级页面的时候取出,采用( wx.getStorage  ||  wx.getStorageSync ),在这里,退出的时候一定要记得清除缓存哦!!!( wx.clearStorage  |

子元素浮动,父级元素为0怎么解决

当子元素浮动时无法撑开父元素,父元素高度为0.这时可以通过在浮动的子元素后清除浮动来使父元素获得正确的高度. 1.  额外标签法 这种方法就是向父容器的末尾再插入一个额外的标签,并令其清除浮动(clear)以撑大父容器.这种方法浏览器兼容性好,没有什么问题,缺点就是需要额外的(而且通常是无语义的)标签. 2. 使用after伪类 这种方法就是对父容器使用after伪类和内容声明在指定的现在内容末尾添加新的内容.经常的做法就是添加一个"点",因为它比较小不太引人注意.然后我们再利用它来清

PHP 根据子ID递归获取父级ID,实现逐级分类导航效果

代码: //当前路径 $cate=M('wangpan_class')->select(); function get_top_parentid($cate,$id){ $arr=array(); foreach($cate as $v){ if($v['id']==$id){ $arr[]=$v;// $arr[$v['id']]=$v['name']; $arr=array_merge(get_top_parentid($cate,$v['fid']),$arr); } }return $a

&lt;转载&gt;如何解决子级用float浮动父级div高度不能自适应的问题

转载:http://www.kwstu.com/ArticleView/divcss_2013101582430202 解决子级对象使用css float浮动 而父级div不能自适应高度,不能被父级内容撑开解决方法,父级div没有高度解决方法. 最外层的父级DIV不能自适应高度-不能随对象撑开没有高度 当在对象内的盒子使用了float后,导致对象本身不能被撑开自适应高度,这个是由于浮动产生原因. 如何解决父div对象自适应高度,方法有三种,接下来DIVCSS5逐一介绍. 1.首先我们先看HTML

解决子级用css float浮动 而父级div没高度不能自适应高度

解决子级对象使用css float浮动 而父级div不能自适应高度,不能被父级内容撑开解决方法,父级div没有高度解决方法. 最外层的父级DIV不能自适应高度-不能随对象撑开没有高度 当在对象内的盒子使用了float后,导致对象本身不能被撑开自适应高度,这个是由于浮动产生原因. 如何解决父div对象自适应高度,方法有三种,接下来DIVCSS5逐一介绍. 1.首先我们先看HTML源代码: <!DOCTYPE html> <html> <head> <meta cha

JS子元素oumouseover触发父元素onmouseout

JavaScript中,父元素包含子元素: 当父级设置onmouseover及onmouseout时,鼠标从父级移入子级,则触发父级的onmouseout后又触发onmouseover:从子级移入父级后再次触发父级的oumouseout后又触发onmouseover.而如果onmouseover内又应用了计时器便会存在较大的问题.下面针对此问题给出解决方案. 首先,在给出解决方案之前,必须先弄清楚几个对象及方法,分别如下: 1.事件对象 2.事件对象相关属性(只针对onmouseover及onm

子级用css float浮动 而父级div没高度不能自适应高度

子级对象使用css float浮动 而父级div不能自适应高度. 对父级div标签闭合</div>前加一个clear清除浮动对象. <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>父div不自适应高度实例</title> <style> .divcss5{width:500px;border:1px solid #0