Türe göre WPF penceresindeki tüm denetimleri bulun


Yanıtlar:


430

Bu hile yapmalı

public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
    if (depObj != null)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
            if (child != null && child is T)
            {
                yield return (T)child;
            }

            foreach (T childOfChild in FindVisualChildren<T>(child))
            {
                yield return childOfChild;
            }
        }
    }
}

o zaman kontrolleri numaralandırırsınız

foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
    // do something with tb here
}

68
Not: Bunu çalıştırmaya çalışıyorsanız ve Pencerenizde (örneğin) 0 görsel alt öğe olduğunu tespit ediyorsanız, Loaded olay işleyicisinde bu yöntemi çalıştırmayı deneyin. Yapıcıda çalıştırırsanız (InitializeComponent () öğesinden sonra bile), görsel alt öğeler henüz yüklenmez ve çalışmaz.
Ryan Lundy

24
VisualTreeHelper'dan LogicalTreeHelpers'a geçiş görünmez öğelerin de eklenmesine neden olur.
Mathias Lykkegaard Lorenzen

11
"Child! = Null && child is T" satırı gereksiz değil mi? Sadece "çocuk T"
yazmazsa

1
Ben daha thisönce bir insering ile bir uzatma yöntemine dönüştürmek istiyorum DependencyObject=>this DependencyObject depObj
Johannes Wanzek

1
Foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}: @JohannesWanzek da sen çocuğa diyoruz biraz değiştirmek gerekir unutmayın Do
Will

66

Bu en kolay yol:

IEnumerable<myType> collection = control.Children.OfType<myType>(); 

burada kontrol pencerenin kök öğesidir.


1
"kök element" ne demek? Ana pencere formuma bağlanmak için ne yazmalıyım?
deadfish

Anladım, xaml görünümünde ızgara için isim ayarlamak zorunda kaldım <Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>ve sonra kullanabilirdimAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
deadfish

68
Bu, sorulan soruya cevap vermez. Çocuk kontrollerini sadece bir seviye derinliğe döndürür.
Jim

21

@Bryce Kahle'nin cevabını @Mathias Lykkegaard Lorenzen'in önerisini ve kullanımını takip etmek için uyarladım LogicalTreeHelper.

Tamam görünüyor. ;)

public static IEnumerable<T> FindLogicalChildren<T> ( DependencyObject depObj ) where T : DependencyObject
{
    if( depObj != null )
    {
        foreach( object rawChild in LogicalTreeHelper.GetChildren( depObj ) )
        {
            if( rawChild is DependencyObject )
            {
                DependencyObject child = (DependencyObject)rawChild;
                if( child is T )
                {
                    yield return (T)child;
                }

                foreach( T childOfChild in FindLogicalChildren<T>( child ) ) 
                {
                    yield return childOfChild;
                }
            }
        }
    }
}

(Sırasıyla @Benjamin Berry & @David R tarafından belirtildiği gibi Sekme kontrollerini veya Grup Kutuları içindeki Izgaraları kontrol etmeyecektir.) (Ayrıca @ öğlenin önerisini takip etti ve gereksiz çocuğu kaldırdı!


nasıl tüm metin kutuları temizlemek için bir süre arıyordum, birden fazla sekme var ve bu işe
yarayan

13

Yardımcı sınıfları VisualTreeHelperveya ilgilendiğiniz ağacaLogicalTreeHelper bağlı olarak kullanın . Her ikisi de bir öğenin alt öğelerini almak için yöntemler sağlar (sözdizimi biraz farklı olsa da). Genellikle bu sınıfları belirli bir türün ilk oluşumunu bulmak için kullanırım, ancak bu türdeki tüm nesneleri bulmak için kolayca değiştirebilirsiniz:

public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
{
    if (obj != null)
    {
        if (obj.GetType() == type)
        {
            return obj;
        }

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject childReturn = FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type);
            if (childReturn != null)
            {
                return childReturn;
            }
        }
    }

    return null;
}

Açıklama ve yazı için +1 ama Bryce Kahle tamamen çalışan bir fonksiyon gönderdi Teşekkürler
Andrija

Bu, sorunun sorununu çözmez ve ayrıca genel türle ilgili cevap çok daha açıktır. VisualTreeHelper.GetChildrenCount (obj) kullanımı ile birleştirilmesi sorunu çözecektir. Bununla birlikte, bir seçenek olarak düşünmek yararlıdır.
Vasil Popov

9

VisualTreeHelper.GetChildrenCount(depObj);Yukarıdaki birkaç örnekte kullanılan satırın, GroupBoxözellikle GroupBoxa içeren Gridve alt Gridöğeleri içeren es için sıfırdan farklı bir sayı döndürmediğini buldum . Bunun GroupBoxbirden fazla çocuk içermesine izin verilmemesi ve bunun kendi Contentmülkünde depolanması nedeniyle olabileceğine inanıyorum . GroupBox.ChildrenMülk türü yoktur . Bunu çok verimli bir şekilde yapmadığımdan eminim, ancak bu zincirdeki ilk "FindVisualChildren" örneğini aşağıdaki gibi değiştirdim:

public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject 
{ 
    if (depObj != null) 
    {
        int depObjCount = VisualTreeHelper.GetChildrenCount(depObj); 
        for (int i = 0; i <depObjCount; i++) 
        { 
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i); 
            if (child != null && child is T) 
            { 
                yield return (T)child; 
            }

            if (child is GroupBox)
            {
                GroupBox gb = child as GroupBox;
                Object gpchild = gb.Content;
                if (gpchild is T)
                {
                    yield return (T)child; 
                    child = gpchild as T;
                }
            }

            foreach (T childOfChild in FindVisualChildren<T>(child)) 
            { 
                yield return childOfChild; 
            } 
        }
    }
} 

4

Belirli bir türdeki tüm alt öğelerin bir listesini almak için şunları kullanabilirsiniz:

private static IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, Type type)
{
    if (obj != null)
    {
        if (obj.GetType() == type)
        {
            yield return obj;
        }

        for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
            {
                if (child != null)
                {
                    yield return child;
                }
            }
        }
    }

    yield break;
}

4

Örneğin, bir sekme denetiminin alt sekme denetimini bulabilmeniz için özyinelemede küçük bir değişiklik.

    public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
    {
        if (obj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);

                if (child.GetType() == type)
                {
                    return child;
                }

                DependencyObject childReturn = FindInVisualTreeDown(child, type);
                if (childReturn != null)
                {
                    return childReturn;
                }
            }
        }

        return null;
    }

3

İşte jenerik sözdizimine sahip başka bir kompakt sürüm:

    public static IEnumerable<T> FindLogicalChildren<T>(DependencyObject obj) where T : DependencyObject
    {
        if (obj != null) {
            if (obj is T)
                yield return obj as T;

            foreach (DependencyObject child in LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>()) 
                foreach (T c in FindLogicalChildren<T>(child)) 
                    yield return c;
        }
    }

2

Ve işte böyle çalışıyor

    private T FindParent<T>(DependencyObject item, Type StopAt) where T : class
    {
        if (item is T)
        {
            return item as T;
        }
        else
        {
            DependencyObject _parent = VisualTreeHelper.GetParent(item);
            if (_parent == null)
            {
                return default(T);
            }
            else
            {
                Type _type = _parent.GetType();
                if (StopAt != null)
                {
                    if ((_type.IsSubclassOf(StopAt) == true) || (_type == StopAt))
                    {
                        return null;
                    }
                }

                if ((_type.IsSubclassOf(typeof(T)) == true) || (_type == typeof(T)))
                {
                    return _parent as T;
                }
                else
                {
                    return FindParent<T>(_parent, StopAt);
                }
            }
        }
    }


1

Ben bir yorum eklemek istedim ama ben sadece "Cevap" olabilir 50'den az puan var. XAML "TextBlock" nesnelerini almak için "VisualTreeHelper" yöntemini kullanırsanız, bunun da XAML "Button" nesnelerini yakalayacağını unutmayın. "TextBlock" nesnesini Textblock.Text parametresine yazarak yeniden başlatırsanız, Button.Content parametresini kullanarak Button metnini artık değiştiremezsiniz. Düğme, Textblock'tan kendisine yazılan metni kalıcı olarak gösterir. Metin yazma eylemi (alındığı andan itibaren -

foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
   tb.Text = ""; //this will overwrite Button.Content and render the 
                 //Button.Content{set} permanently disabled.
}

Bu soruna geçici bir çözüm bulmak için, bir XAML "TextBox" kullanmayı deneyebilir ve bir XAMAL Düğmesini taklit etmek için yöntemler (veya Olaylar) ekleyebilirsiniz. XAML "TextBox", "TextBlock" aramasıyla toplanmaz.


Görsel ve mantıksal ağaç arasındaki fark budur. Görsel ağaç her kontrolü içerir (bir kontrolün yapıldığı kontroller de dahil olmak üzere kontroller şablonunda tanımlanır), mantıksal ağaç sadece gerçek kontrolleri içerir (şablonlarda tanımlanmamış olanlar hariç ). Bu kavramın güzel bir görselleştirmesi var: link
lauxjpn

1

C ++ / CLI sürümüm

template < class T, class U >
bool Isinst(U u) 
{
    return dynamic_cast< T >(u) != nullptr;
}

template <typename T>
    T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element, Platform::String^ name)
    {
        if (Isinst<T>(element) && dynamic_cast<Windows::UI::Xaml::FrameworkElement^>(element)->Name == name)
        {
            return dynamic_cast<T>(element);
        }
        int childcount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element);
        for (int i = 0; i < childcount; ++i)
        {
            auto childElement = FindVisualChildByType<T>(Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i), name);
            if (childElement != nullptr)
            {
                return childElement;
            }
        }
        return nullptr;
    };

1

Bazı nedenlerden dolayı, burada gönderilen cevapların hiçbiri MainWindow'umdaki belirli bir kontrolde bulunan tüm tip kontrolleri almama yardımcı oldu. Onları yinelemek için tüm menü öğelerini tek bir menüde bulmam gerekiyordu. Menünün tüm doğrudan torunları değildi, bu yüzden yukarıdaki kodlardan herhangi birini kullanarak sadece ilk lilne toplamak başardı. Bu eklenti yöntemi, buradan sonuna kadar okumaya devam edecek herkes için sorun için benim çözümüm.

public static void FindVisualChildren<T>(this ICollection<T> children, DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            var brethren = LogicalTreeHelper.GetChildren(depObj);
            var brethrenOfType = LogicalTreeHelper.GetChildren(depObj).OfType<T>();
            foreach (var childOfType in brethrenOfType)
            {
                children.Add(childOfType);
            }

            foreach (var rawChild in brethren)
            {
                if (rawChild is DependencyObject)
                {
                    var child = rawChild as DependencyObject;
                    FindVisualChildren<T>(children, child);
                }
            }
        }
    }

Umarım yardımcı olur.


1

Kabul edilen cevap , henüz ayrıştırılmamış ağaç dalları için adımları geri izlemeden ve tekrarlamadan önce, ilk alt dalı olabildiğince derin takip ederek keşfedilen unsurları mümkün olduğunca derin takip ederek keşfedilen unsurları az çok düzensiz döndürür .

İçinde soyundan elemanlarını gerekirse azalan sırayla doğrudan çocukların vb sonra, ilk çocuklarını vermiştir ve olacak, aşağıdaki algoritma çalışır:

public static IEnumerable<T> GetVisualDescendants<T>(DependencyObject parent, bool applyTemplates = false)
    where T : DependencyObject
{
    if (parent == null || !(child is Visual || child is Visual3D))
        yield break;

    var descendants = new Queue<DependencyObject>();
    descendants.Enqueue(parent);

    while (descendants.Count > 0)
    {
        var currentDescendant = descendants.Dequeue();

        if (applyTemplates)
            (currentDescendant as FrameworkElement)?.ApplyTemplate();

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

            if (child is Visual || child is Visual3D)
                descendants.Enqueue(child);

            if (child is T foundObject)
                yield return foundObject;
        }
    }
}

Ortaya çıkan unsurlar en yakından en uzağa doğru sıralanacaktır. Bu, örneğin bir tür ve koşulda en yakın alt öğeyi arıyorsanız yararlı olacaktır:

var foundElement = GetDescendants<StackPanel>(someElement)
                       .FirstOrDefault(o => o.SomeProperty == SomeState);

1
Bir şey eksik mi; childtanımsız.
kodlayıcı

1

@Bryce, gerçekten güzel bir cevap.

VB.NET sürümü:

Public Shared Iterator Function FindVisualChildren(Of T As DependencyObject)(depObj As DependencyObject) As IEnumerable(Of T)
    If depObj IsNot Nothing Then
        For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(depObj) - 1
            Dim child As DependencyObject = VisualTreeHelper.GetChild(depObj, i)
            If child IsNot Nothing AndAlso TypeOf child Is T Then
                Yield DirectCast(child, T)
            End If
            For Each childOfChild As T In FindVisualChildren(Of T)(child)
                Yield childOfChild
            Next
        Next
    End If
End Function

Kullanım (bu, bir penceredeki tüm TextBox'ları devre dışı bırakır):

        For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
          tb.IsEnabled = False
        Next

-1

Visual Tree Helpers olmadan daha kolay buldum:

foreach (UIElement element in MainWindow.Children) {
    if (element is TextBox) { 
        if ((element as TextBox).Text != "")
        {
            //Do something
        }
    }
};

3
Bu sadece bir seviye derinliktedir. XAML'de derin iç içe kontrollere sahipsiniz.
SQL Police
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.