web-dev-qa-db-ja.com

タイプごとにWPFコンテナの子を取得する方法は?

WPFのComboBoxMyContainerGrid型の子コントロールを取得するにはどうすればよいですか?

<Grid x:Name="MyContainer">                    
    <Label Content="Name"  Name="label1"  />
    <Label Content="State" Name="label2"  />
    <ComboBox Height="23" HorizontalAlignment="Left" Name="comboBox1"/>
    <ComboBox Height="23" HorizontalAlignment="Left" Name="comboBox3" />
    <ComboBox Height="23" HorizontalAlignment="Left" Name="comboBox4" />
</Grid>

この行は私にエラーを与えます:

var myCombobox = this.MyContainer.Children.GetType(ComboBox);
40
ArchieTiger

この拡張メソッドは、目的のタイプの子要素を再帰的に検索します。

_public static T GetChildOfType<T>(this DependencyObject depObj) 
    where T : DependencyObject
{
    if (depObj == null) return null;

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    {
        var child = VisualTreeHelper.GetChild(depObj, i);

        var result = (child as T) ?? GetChildOfType<T>(child);
        if (result != null) return result;
    }
    return null;
}
_

そのため、MyContainer.GetChildOfType<ComboBox>()を要求できます。

75
Matt Hamilton

ChildrenはUIElementsのコレクションです。そのため、アイテムを反復処理し、各アイテムについて必要なタイプかどうかを判断する必要があります。幸いなことに、まさにこれのためのLinqメソッド、つまり _Enumerable.OfType<T>_ が既にあります。これは Extension Method 構文を使用して簡単に呼び出すことができます。

_var comboBoxes = this.MyContainer.Children.OfType<ComboBox>();
_

このメソッドは、タイプに基づいてコレクションをフィルタリングし、あなたの場合、タイプComboBoxの要素のみを返します。

最初のComboBoxのみが必要な場合(変数名が示すように)、クエリに FirstOrDefault() の呼び出しを追加できます。

_var myComboBox = this.MyContainer.Children.OfType<ComboBox>().FirstOrDefault();
_
37
Botz3000

これらの答えのすべては、再帰を使用します[〜#〜] imo [〜#〜]は単なるラメです:)

視覚的な子を取得:

public static IEnumerable<T> FindVisualChildren<T>([NotNull] this DependencyObject parent) where T : DependencyObject
{
    if (parent == null)
        throw new ArgumentNullException(nameof(parent));

    var queue = new Queue<DependencyObject>(new[] {parent});

    while (queue.Any())
    {
        var reference = queue.Dequeue();
        var count = VisualTreeHelper.GetChildrenCount(reference);

        for (var i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(reference, i);
            if (child is T children)
                yield return children;

            queue.Enqueue(child);
        }
    }
}

論理的な子を取得:

public static IEnumerable<T> FindLogicalChildren<T>([NotNull] this DependencyObject parent) where T : DependencyObject
{
    if (parent == null)
        throw new ArgumentNullException(nameof(parent));

    var queue = new Queue<DependencyObject>(new[] {parent});

    while (queue.Any())
    {
        var reference = queue.Dequeue();
        var children = LogicalTreeHelper.GetChildren(reference);
        var objects = children.OfType<DependencyObject>();

        foreach (var o in objects)
        {
            if (o is T child)
                yield return child;

            queue.Enqueue(o);
        }
    }
}

両方のツリーを深くトラバースすることに注意してください。最初に遭遇したときに停止したい場合は、両方のコードを変更してqueue.Enqueueelseブロック内。

3
Aybe

(スクリーンの)所定のポイントを含む特定のタイプの最初の子を検索します。

(param 'point'は 'PointToScreen'関数を呼び出した結果です(Visual型で宣言されています))

private TDescendantType FindDescendant<TDescendantType>(DependencyObject parent, Point screenPoint) 
         where TDescendantType : DependencyObject
{
    int count = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < count; i++)
    {
        var child = VisualTreeHelper.GetChild(parent, i);
        if (child is Visual)
        {
            Point point = ((Visual)child).PointFromScreen(screenPoint);
            Rect rect = VisualTreeHelper.GetDescendantBounds((Visual)child);

            if (!rect.Contains(point))
                continue;
        }

        if (child is TDescendantType)
        {
            return (TDescendantType)child;
        }

        child = FindDescendant<TDescendantType>(child, screenPoint);
        if (child != null)
        {
            return (TDescendantType)child;
        }
    }
    return null;
}
2
ale

これらの答えはすべて非常に素晴らしいですが、タイプTのspecific視覚的な子を見つけようとしている場合、それらすべてを取得してから必要なものを見つけるか、またはあなたが最初に得るものはあなたが望むものです。いくつかのアプローチをマージして、基準に基づいて特定のアプローチを見つけました。それはLINQに少し似ていますが、再帰的な列挙子に対処しようとは思いませんでした。

次のように使用します。

MyContainer.FirstOrDefaultChild<Label>(l => l.Content=="State")

拡張メソッドとして作成しました。

public static class DependencyObjectExtensions
{
    public static T FirstOrDefaultChild<T>(this DependencyObject parent, Func<T, bool> selector) 
        where T : DependencyObject
    {
        T foundChild;
        return FirstOrDefaultVisualChildWhere(parent, selector, out foundChild) ? foundChild : default(T);
    }

    private static bool FirstOrDefaultVisualChildWhere<T>(DependencyObject parent, Func<T, bool> selector,
        out T foundChild) where T : DependencyObject
    {
        var count = VisualTreeHelper.GetChildrenCount(parent);
        for (var i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            var tChild = child as T;
            if (tChild != null)
            {
                if (!selector(tChild)) continue;
                foundChild = tChild;
                return true;
            }

            if (FirstOrDefaultVisualChildWhere(child, selector, out foundChild))
            {
                return true;
            }
        }
        foundChild = default(T);
        return false;
    }
1
Reginald Blue