前言

我们通过 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 的页面切换的事件中去找:

【wpf】视觉树上找元素的注意事项-LMLPHP

 但是如果直接找,还是找不到,需要让主线程先运行一会再找。

其实这个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();
	}                
});

对应的前台代码:

【wpf】视觉树上找元素的注意事项-LMLPHP

 

小结

        给我的感觉就是只有当前界面显示了,再去通过VisualTreeHelper找,才能找到。

结语

        欢迎评论区参与讨论!

05-25 05:19