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

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

  1. 关于iframe里的子页面如何调取父级页面里的事件(子调父)

    在子页面里面的事件里写 self.parent.window.父级函数名('参数名'); 父级里面直接写函数. js中的parent.top.self的含义. js中经常看到window.parent ...

  2. 子级Repeater获取 父级Repeater

    第一种方法,子级Repeater中绑定父级的某个字段: <%# DataBinder.Eval((Container.NamingContainer.NamingContainer as Rep ...

  3. html/css更改子级继承的父级属性

    一个精美的网页需要的样式很多,在父级上设置的字体颜色或者大小,在其子元素中不一定全部相同,这时候要更改其中某一项的样式怎么办呢. 很多新手朋友就不明白,会迷惑为什么我使用class单独命名了,重新设置 ...

  4. Repeater 嵌套,子级Repeater获取 父级Repeater 中的值

    第一种方法,子级Repeater中绑定父级的某个字段: <%# DataBinder.Eval((Container.NamingContainer.NamingContainer as Rep ...

  5. 子iframe 怎么调用 父级的JS函数

    window.parent.父级函数名();

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

    代码: //当前路径 $cate=M('wangpan_class')->select(); function get_top_parentid($cate,$id){ $arr=array() ...

  7. 【idea】idea重新打包依赖了父级项目的子级项目,父级项目代码改变,但是子级项目打包依旧是老的代码 问题解决

    最简单的方法: 就是单独打包父级项目,然后替换本地maven仓库中的父级项目的jar,然后重新打包子级项目,就可以了.

  8. <转载>如何解决子级用float浮动父级div高度不能自适应的问题

    转载:http://www.kwstu.com/ArticleView/divcss_2013101582430202 解决子级对象使用css float浮动 而父级div不能自适应高度,不能被父级内 ...

  9. JS子元素oumouseover触发父元素onmouseout

    原文:JS子元素oumouseover触发父元素onmouseout JavaScript中,父元素包含子元素: 当父级设置onmouseover及onmouseout时,鼠标从父级移入子级,则触发父 ...

随机推荐

  1. xmpp即时通讯协议的特性---长处和缺点!

    xmpp协议的定义? XMPP是一种基于标准通用标记语言的子集XML的协议,它继承了在XML环境中灵活的发展性. 因此.基于XMPP的应用具有超强的可扩展性.经过扩展以后的XMPP能够通过发送扩展的信 ...

  2. [TypeScript] Create random integers in a given range

    Learn how to create random integers using JavaScript / TypeScript. /** * Returns a random int betwee ...

  3. js进阶正则表达式13RegExp对象方法(RegExp对象的方法:compile,test,exec)(子表达式 var reg1=/([a-z]+)\d/)

    js进阶正则表达式13RegExp对象方法(RegExp对象的方法:compile,test,exec)(子表达式 var reg1=/([a-z]+)\d/) 一.总结 1.RegExp对象有三个方 ...

  4. [Typescript] Sorting arrays in TypeScript

    In this lesson we cover all the details of how to sort a list of items using TypeScript. We also pre ...

  5. EM12C 安装及卸载 注意点整理

    版本号:  em12c 12.1.0.4          OS :  redhat 5.7 x86_64bit (CentOS6.2,測试过,当时因glibc*.i686包安装一直报错.所以放弃了) ...

  6. java线程池框架源代码分析

    相关类Executor,Executors.AbstractExecutorService.ExecutorService Executor:整个线程池运行者框架的顶层接口. 定义了一个execute ...

  7. sql server中触发器

    触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程.触发器主要是通过事件进行触发被自动调用执行的.而存储过程可以通过存储过程的名称被调用. Ø 什么是触发器 触发器对表进行插入.更新.删 ...

  8. 基于Linux应用层的6LOWPAN物联网网关及实现方法

    本发明涉及一种基于Linux应用层的6LOWPAN物联网网关及实现方法,所述物联网网关包括开发平台以及无线射频模块,其实现方法是:所述6LOWPAN物联网网关的以太网网口收到访问6LOWPAN无线传感 ...

  9. 2015-07-30Java 错题

    2 推断对错.在java的多态调用中,new的是哪一个类就是调用的哪个类的方法. 正确答案: A 对 错 解析: java多态有两种情况:重载和覆写 在覆写中.运用的是动态单分配.是依据new的类型确 ...

  10. 6LoWPAN - Transmission of IPv6 Packets over IEEE 802.15.4 Networks

    6LoWPAN covered topics include the following:   Frame format for transmission of IPv6 packets Method ...