在WPF中搜索未知子类型的子项

dkqlctbz  于 2023-10-22  发布在  其他
关注(0)|答案(2)|浏览(133)

我想实现一个包含多个GroupBox的搜索设置窗口,只查看对应的GroupBox。每个GroupBox都包含几个控件,所以当事件searchSettingsTextBox_TextChanged()发生时,我可以搜索每个GroupBox.Header及其查尔兹的文本属性(Text,Content,Header,ToolTip),如果GroupBox有它,则使其成为gb.Visibility = Visibility.Visible;,否则使其成为Visibility.Collapsed,到目前为止我所做的是:

private void searchSettingsTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    foreach (GroupBox gb in wrapPanel1.Children)
    {
        if (!gb.Header.ToString().ToLower().Contains(((TextBox)e.Source).Text))
        {
            gb.Visibility = Visibility.Collapsed;
        }
        else
            gb.Visibility = Visibility.Visible;

        foreach (var child in ((Grid)gb.Content).Children)
        {
            // Stopped here, can't check child.Text property which might be: (Text, Content, Header, ToolTip)
        }
    }
}
m4pnthwp

m4pnthwp1#

每个可能的控件(TextBlockTextBoxGroupBoxLabel等)可能具有相同的属性(例如TextContent),但表示不同的基类,因此您不能将它们作为一组类似控件使用。
这将使您转向手动类型检查和每个特定控件类型的后续操作。
另外,应该提到的是,Content属性(例如在Label中)或Header(在GroupBox中)是一个对象,可能包含的不是纯字符串,而是另一个控件。这意味着一些LabelContent可能是TextBoxTextBlock,具有Text属性,或GroupBox具有Header属性,甚至是另一个子Label具有自己的Content,因此这个链可能非常庞大,代码,即使是递归的,也可能看起来一团糟。
了解了这一点,即使是简单的类型转换检查也不能保证正确检索控件的 text 值(表示其内容):

var controlTextValue = string.Empty;

foreach (var child in grid.Children)
{
    controlTextValue = child switch
        {
            HeaderedContentControl headeredControl => headeredControl.Header?.ToString(),
            ContentControl contentControl => contentControl.Content?.ToString(),
            TextBox textBox => textBox.Text,
            TextBlock textBlock => textBlock.Text,
            _ => null
        };
}

如果你确定,并有严格的一堆控制与内容为 * 文本 *,比这种方式可能会为你做的工作。
也许可以创建一个自定义的UserControl作为任何其他控件的 Package 器(对于Framework/UI元素作为属性/子元素),带有一些SearchName属性,并在搜索时使用它,而不是迭代原始的查尔兹属性。但是我没有遇到任何这样的实现来提供例子,只有纯粹的想法和理论。

t9aqgxwy

t9aqgxwy2#

有许多方法可以遍历视觉树。
下面是我的实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using static System.Windows.Media.VisualTreeHelper;

namespace CommonCore.Helpers
{
    public static class VisualTreeHelper
    {
        public static IEnumerable<DependencyObject> GetChildren(this DependencyObject reference)
        {
            ArgumentNullException.ThrowIfNull(reference);
            Queue<DependencyObject> queue = new Queue<DependencyObject>(16);
            queue.Enqueue(reference);
            while (queue.Count != 0)
            {
                DependencyObject current = queue.Dequeue();
                int count = GetChildrenCount(current);
                for (int i = 0; i < count; i++)
                {
                    queue.Enqueue(GetChild(current, i));
                }
                yield return current;
            }
        }
        public static IEnumerable<T> GetChildren<T>(this DependencyObject reference)
           where T : DependencyObject
            => reference.GetChildren().OfType<T>();

        public static T FindAncestor<T>(this DependencyObject dobj)
            where T : DependencyObject
        {
            while (dobj is not null)
            {
                if (dobj is T t)
                    return t;

                dobj = GetParent(dobj);
            }
            return null;
        }

        public static FrameworkElement FindDataAncestor<TData>(this DependencyObject dobj)
        {
            while (dobj is not null)
            {
                if (dobj is FrameworkElement element and { DataContext: TData })
                    return element;

                dobj = GetParent(dobj);
            }
            return null;
        }

        public static TData FindData<TData>(this DependencyObject dobj)
        {
            FrameworkElement element = dobj.FindDataAncestor<TData>();
            if (element == null)
                return default;
            return (TData)element.DataContext;
        }

    }
}

您可以使用端点仅将文本输出到TextBlock或TextBox的事实。
使用这个帮助器,你可以像这样获取所有的文本值:

string[] texts = someElement
                        .GetChildren()
                        .Where(el => el is TextBox or TextBlock)
                        .Select(el => el is TextBox tBox ? tBox.Text : ((TextBlock)el).Text)
                        .ToArray();

P.S.@Auditive正确地指出,其他一些元素也会输出文本。例如标签、按钮和其他内容控件。但是如果展开它们的可视树,就会发现负责显示文本的最后一个元素是TextBlock。因此,我建议的代码也适用于他们。

相关问题