VisualTreeHelper使用——使用BFS实现高效率的视觉对象搜索

BFS,即广度优先搜索,是一种典型的图论算法。BFS算法与DFS(深度优先搜索)算法相对应,都是寻找图论中寻路的常用算法,两者的实现各有优点。

其中DFS算法常用递归实现,也就是常见的一条路找到黑再找另一条。如果我们要找的数据存储在一棵树最靠左侧的一边时,DFS的好处就显现出来了。无论数据在树的多深,DFS都能在线性的时间内找出这个数据。

而BFS算法常用队列实现,在查找树内对象时,会由树的根节点,一层一层向下找到目标。BFS的好处在于不管数据存在树的哪个节点,BFS都能以一个比较恒定的速度找到对象,这个速度几乎只与没寻找的深度和每层的节点数量相关。

所以在遍历视觉树时使用BFS相较于DFS有什么好处呢?

一个很明显的视觉树遍历特点就是,一般要找到的视觉对象都不会存储在离根节点特别远的地方。此时BFS相比DFS更不容易出现一条分支摸到黑啥都没找的情况。

话不多少了,上代码!

public static class VisualTreeExtension
{

    /// <summary>
    /// Find all children T in dpObj using BFS.
    /// </summary>
    /// <param name="maxDepth">max finding depth of visual tree.</param>
    /// <param name="maxChildCount">maximum number of child nodes. Whose number of child nodes exceeds this value will be excluded</param>
    /// <returns></returns>
    public static IEnumerable<T> GetChild<T>(this DependencyObject dependencyObject, uint maxDepth = uint.MaxValue, uint maxChildCount = 20)
        where T : DependencyObject
    {
        int depth = 1;
        int count = VisualTreeHelper.GetChildrenCount(dependencyObject);
        Queue<DependencyObject> qObjs = new Queue<DependencyObject>();
        for (int i = 0; i < count; i++)
        {
            qObjs.Enqueue(VisualTreeHelper.GetChild(dependencyObject, i));
        }

        while (qObjs.Count != 0)
        {
            if (depth > maxDepth)
                yield break;
            depth++;
            count = qObjs.Count;
            for (int i = 0; i < count; i++)
            {
                var obj = qObjs.Dequeue();
                if (obj is T result)
                    yield return result;
                int child_count = VisualTreeHelper.GetChildrenCount(obj);
                if (child_count > maxChildCount)
                    continue;
                for (int j = 0; j < child_count; j++)
                {
                    qObjs.Enqueue(VisualTreeHelper.GetChild(obj, j));
                }
            }
        }
    }

    /// <summary>
    /// Find all children T in dpObj using BFS.
    /// </summary>
    /// <param name="maxDepth">max finding depth of visual tree.</param>
    /// <param name="maxChildCount">maximum number of child nodes. Whose number of child nodes exceeds this value will be excluded</param>
    /// <returns></returns>
    public static IEnumerable<T> GetChild<T>(this DependencyObject dependencyObject, string name, uint maxDepth = uint.MaxValue, uint maxChildCount = 20)
        where T : FrameworkElement
    {
        int depth = 1;
        int count = VisualTreeHelper.GetChildrenCount(dependencyObject);
        Queue<DependencyObject> qObjs = new Queue<DependencyObject>(count);
        for (int i = 0; i < count; i++)
        {
            qObjs.Enqueue(VisualTreeHelper.GetChild(dependencyObject, i));
        }

        while (qObjs.Count != 0)
        {
            if (depth > maxDepth)
                yield break;
            depth++;
            count = qObjs.Count;
            for (int i = 0; i < count; i++)
            {
                var obj = qObjs.Dequeue();
                if (obj is T result && result.Name == name)
                    yield return result;
                int child_count = VisualTreeHelper.GetChildrenCount(obj);
                if (child_count > maxChildCount)
                    continue;
                for (int j = 0; j < child_count; j++)
                {
                    qObjs.Enqueue(VisualTreeHelper.GetChild(obj, j));
                }
            }
        }
    }
}

使用方法:

var scrollViewer = listView.GetChild<ScrollViewer>().First();

也可以手动指定BFS的最大深度和最大子节点数量,避免遍历浪费太多时间

posted @ 2019-10-12 15:25  LoyieKing  阅读(400)  评论(0编辑  收藏  举报