前言
我们通过 VisualTreeHelper类 可以在视觉树上找元素,下面提供几个封装好的方法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows;
namespace VisionCore.Tools
{
public class FindVisualTree
{
/// <summary>
/// 根据类型查找子元素
/// 调用形式: List<StackPanel> initToolBarWeChatUserSp = GetChildObjects<StackPanel>(name, typeof(StackPanel));
/// </summary>
/// <typeparam name="T">查找类型</typeparam>
/// <param name="obj">查询对象</param>
/// <returns></returns>
static public List<T> GetChildObjects<T>(DependencyObject obj) where T : FrameworkElement
{
DependencyObject child = null;
List<T> childList = new List<T>();
Type typename = typeof(T);
for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
{
child = VisualTreeHelper.GetChild(obj, i);
if (child is T && (((T)child).GetType() == typename))
{
childList.Add((T)child);
}
childList.AddRange(GetChildObjects<T>(child));
}
return childList;
}
static public T GetChildObjectFirst<T>(DependencyObject obj) where T : FrameworkElement
{
List<T> childList = new List<T>();
childList = GetChildObjects<T>(obj);
if (childList.Count > 0)
{
return childList[0];
}
else
{
return null;
}
}
/// <summary>
/// 获取父可视对象中第一个指定类型的子可视对象
/// </summary>
/// <typeparam name="T">可视对象类型</typeparam>
/// <param name="parent">父可视对象</param>
/// <returns>第一个指定类型的子可视对象</returns>
public static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
static public T GetChildObjectWithName<T>(DependencyObject obj, string name) where T : FrameworkElement
{
List<T> childList = new List<T>();
childList = GetChildObjects<T>(obj);
foreach (var item in childList)
{
if (item.Name == name)
{
return item;
}
}
return null;
}
}
}
以上函数,使用了递归的方式,可以一层层的查找。
问题出现
但是,我发现有时候,这种方式会失效!
我使用 VisualTreeHelper 类,在 TabControl 控件中 想在 TabItem 寻找一个子控件。但是我发现只能找到当前显示的TabItem中的控件,没显示的找不到。
我尝试了各种办法,比如 TabControl 加载事件中获取,或者 TabItem 的加载事件中获取,都不行。给我的感觉就是只有当前界面显示了,再去通过VisualTreeHelper找,才能找到。
于是,我在 TabControl 的页面切换的事件中去找:
但是如果直接找,还是找不到,需要让主线程先运行一会再找。
这里的 Application.Current.Dispatcher.BeginInvoke 是为了解决跨线程的问题。
其实这个500ms的延时可以不用(但是必须保证这种方式的写法,也就是让主线程先执行下)。
那最终的写法如下:
SelectionChangedCmd = new DelegateCommand<RoutedEventArgs>((e) => {
DependencyObject? obj = e.OriginalSource as DependencyObject;
if (hSmartTemp == null)
{
var t = Task.Factory.StartNew(() =>
{
Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
{
Console.WriteLine("Start");
await Task.Delay(500);//异步等一等,让主线程先执行一会!
hSmartTemp = FindVisualTree.GetChildObjectFirst<HSmartWindowControlWPF>(obj);
}));
});
t.Wait();
}
});
对应的前台代码:
小结
给我的感觉就是只有当前界面显示了,再去通过VisualTreeHelper找,才能找到。
结语
欢迎评论区参与讨论!