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

前言

我们通过 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的延时可以不用(但是必须保证这种方式的写法,也就是让主线程先执行下)。

如果对异步这个写法有疑问,可以去看看这篇文章:

【C#】async和await_delphi 异步await_code bean的博客-CSDN博客发现这个大神的解释一针见血,深得我心!以最简单的例子,解释了async和await。其实,大多情况下,分开才能体现async和await的价值!查了一个小时的资料:async和await。https://blog.csdn.net/songhuangong123/article/details/127627231#:~:text=%E5%8F%91%E5%B8%83-,%E3%80%90C%23%E3%80%91async%E5%92%8Cawait,-%E5%85%AC%E5%BC%80

那最终的写法如下:

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找,才能找到。

结语

        欢迎评论区参与讨论!

猜你喜欢

转载自blog.csdn.net/songhuangong123/article/details/130855043