WPF विंडो में सभी नियंत्रण प्रकार से खोजें


218

मैं विंडो पर उनके प्रकार द्वारा सभी नियंत्रणों को खोजने का एक तरीका खोज रहा हूं,

उदाहरण के लिए: सभी TextBoxesको खोजें, विशिष्ट इंटरफ़ेस को लागू करने वाले सभी नियंत्रणों को ढूंढें।


जब हम इस विषय पर हैं, यह प्रासंगिक है goo.gl/i9RVx
Andrija

मैंने इस विषय पर एक ब्लॉग पोस्ट भी लिखी थी: रनटाइम में एक कंट्रोलटेम्पलेट को संशोधित करना
एडॉल्फो पेरेज

जवाबों:


430

यह काम कर जाना चाहिए

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;
            }
        }
    }
}

फिर आप नियंत्रणों पर इस तरह से भरोसा करते हैं

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

68
नोट: यदि आप इसे काम में लाने की कोशिश कर रहे हैं और यह पता लगा रहे हैं कि आपकी विंडो (उदाहरण के लिए) में 0 दृश्य बच्चे हैं, तो लोडेड इवेंट हैंडलर में इस विधि को चलाने का प्रयास करें। यदि आप इसे कंस्ट्रक्टर में चलाते हैं (इनिशियलाइज़कम्पोनेंट () के बाद भी), विजुअल बच्चे अभी तक लोड नहीं हुए हैं, और यह काम नहीं करेगा।
रयान लूनी

24
VisualTreeHelper से LogicalTreeHelpers पर स्विच करने से अदृश्य तत्व भी शामिल हो जाएंगे।
बजे मथियास लिकेगार्ड लोरेनजेन

11
क्या लाइन "चाइल्ड! = Null && चाइल्ड टी" निरर्थक नहीं है? क्या यह सिर्फ "बच्चे को टी" नहीं है
दोपहर

1
मैं इसे एक विस्तार विधि में बदल दूंगा बस एक thisपहले DependencyObject=>this DependencyObject depObj
जोहान्स

1
@JohannesWanzek मत भूलना तुम भी थोड़ा जहां आप इसे बच्चे पर फोन को बदलने के लिए की आवश्यकता होगी: foreach (ChildofChild.FindVisualChildren <टी> ()) {bla bla bla}
विल

66

यह सबसे आसान तरीका है:

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

जहां नियंत्रण खिड़की का मूल तत्व है।


1
आपका क्या मतलब है "मूल तत्व"? मुझे अपने मेनविंडो फॉर्म से जुड़ने के लिए क्या लिखना चाहिए?
डेडफिश

मैं इसे प्राप्त करता हूं, xaml दृश्य में मुझे ग्रिड के लिए नाम सेट करना था <Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>और फिर मैं इस्तेमाल कर सकता थाAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
डेडफिश

68
यह उस प्रश्न का उत्तर नहीं देता है जो पूछा गया था। यह केवल बच्चे को एक स्तर के गहरे नियंत्रण को वापस करता है।
जिम

21

मैंने @Mathias Lykkegaard Lorenzen के सुझाव और उपयोग का पालन करने के लिए @Bryce Kahle के उत्तर को अनुकूलित किया LogicalTreeHelper

ठीक काम करने लगता है। ;)

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;
                }
            }
        }
    }
}

(यह अभी भी GroupBoxes के अंदर टैब नियंत्रण या ग्रिड की जाँच नहीं करेगा जैसा कि क्रमशः @Benjamin Berry & @Did R द्वारा उल्लेख किया गया है।) (इसके अलावा @ noonand के सुझाव और निरर्थक बच्चे को हटा दिया! = Null)


थोड़ी देर के लिए देख रहा था कि मेरे सभी टेक्स्ट बॉक्स कैसे साफ़ करें, मेरे पास कई टैब हैं और यह एकमात्र कोड है जो काम करता है :) धन्यवाद
JohnCris

13

सहायक वर्गों का उपयोग करें VisualTreeHelperया आप LogicalTreeHelperकिस पेड़ में रुचि रखते हैं इसके आधार पर । वे दोनों एक तत्व के बच्चों को प्राप्त करने के लिए तरीके प्रदान करते हैं (हालांकि वाक्यविन्यास थोड़ा भिन्न होता है)। मैं अक्सर विशिष्ट प्रकार की पहली घटना को खोजने के लिए इन वर्गों का उपयोग करता हूं, लेकिन आप इसे आसानी से संशोधित कर उस प्रकार की सभी वस्तुओं को पा सकते हैं:

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;
}

विवरण और पोस्ट लेकिन ब्राइस Kahle के लिए +1 समारोह है कि पूरी तरह से काम करता है धन्यवाद तैनात
Andrija

यह प्रश्न के मुद्दे को ठीक नहीं करता है, और सामान्य प्रकार के साथ उत्तर भी बहुत स्पष्ट है। VisualTreeHelper.GetChildrenCount (obj) के उपयोग के साथ इसे जोड़ने से समस्या ठीक हो जाएगी। हालांकि एक विकल्प के रूप में माना जाना उपयोगी है।
वासिल पोपोव

9

मैंने पाया कि VisualTreeHelper.GetChildrenCount(depObj);ऊपर के कई उदाहरणों में उपयोग की गई रेखा, GroupBoxes के लिए एक गैर-शून्य गणना नहीं लौटाती है, विशेष रूप से, GroupBoxजिसमें ए Gridऔर Gridजिसमें बच्चे तत्व शामिल हैं। मेरा मानना ​​है कि ऐसा इसलिए हो सकता है क्योंकि GroupBoxइसमें एक से अधिक बच्चे रखने की अनुमति नहीं है, और यह इसकी Contentसंपत्ति में संग्रहीत है । GroupBox.Childrenसंपत्ति का कोई प्रकार नहीं है । मुझे यकीन है कि मैंने इसे बहुत कुशलता से नहीं किया था, लेकिन मैंने पहले "FindVisualChildren" उदाहरण को इस प्रकार संशोधित किया:

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

एक विशिष्ट प्रकार के सभी चिल्ड की सूची प्राप्त करने के लिए जिसका आप उपयोग कर सकते हैं:

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

उदाहरण के लिए आप कर सकते हैं पुनरावृत्ति के लिए छोटे परिवर्तन एक टैब नियंत्रण के बच्चे टैब नियंत्रण पा सकते हैं।

    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

जेनेरिक सिंटैक्स के साथ एक और कॉम्पैक्ट संस्करण यहां है:

    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

और यह इसी तरह से ऊपर की ओर काम करता है

    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);
                }
            }
        }
    }

2

ध्यान दें कि VisualTreeHelper का उपयोग केवल Visual या Visual3D से निकलने वाले नियंत्रणों पर काम करता है। अगर आपको विज़ुअलट्री हेल्पर का उपयोग करके अन्य तत्वों (जैसे टेक्सटब्लॉक, फ़्लोडक्लोमेंट इत्यादि) का भी निरीक्षण करने की आवश्यकता है, तो इसका अपवाद होगा।

यहाँ एक विकल्प है जो यदि आवश्यक हो तो तार्किक पेड़ पर वापस आ जाता है:

http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways


1

मैं एक टिप्पणी जोड़ना चाहता था, लेकिन मेरे पास ५० पीटी से कम है इसलिए मैं केवल "उत्तर" दे सकता हूं। ध्यान रखें कि यदि आप XAML "TextBlock" ऑब्जेक्ट्स को पुनः प्राप्त करने के लिए "VisualTreeHelper" विधि का उपयोग करते हैं तो यह XAML "बटन" ऑब्जेक्ट को भी हड़प लेगा। यदि आप Textblock.Text पैरामीटर को लिखकर "TextBlock" ऑब्जेक्ट को फिर से इनिशियलाइज़ करते हैं तो आप Button.Content पैरामीटर का उपयोग करके बटन टेक्स्ट को बदल नहीं पाएंगे। बटन स्थायी रूप से टेक्स्टब्लॉक से लिखे गए पाठ को दिखाएगा। टेक्स्ट एक्शन लिखें (जब यह पुनः प्राप्त किया गया था -

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.
}

इसके आस-पास काम करने के लिए, आप एक XAML "टेक्स्टबॉक्स" का उपयोग करने की कोशिश कर सकते हैं और XAML बटन की नकल करने के लिए तरीकों (या घटनाओं) को जोड़ सकते हैं। XAML "टेक्स्टबॉक्स" "टेक्स्टब्लॉक" के लिए एक खोज द्वारा इकट्ठा नहीं किया गया है।


यह दृश्य और तार्किक पेड़ के बीच अंतर है। दृश्य ट्री में प्रत्येक नियंत्रण शामिल है (जिसमें एक नियंत्रण बनाया जाता है, जिसे नियंत्रण टेम्पलेट में परिभाषित किया गया है) जबकि तार्किक पेड़ में केवल वास्तविक नियंत्रण (टेम्पलेट में परिभाषित किए बिना) शामिल हैं। इस अवधारणा का एक अच्छा दृश्य यहाँ है: लिंक
lauxjpn

1

C ++ / CLI के लिए मेरा संस्करण

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

किसी कारण से, यहां पोस्ट किए गए किसी भी उत्तर ने मुझे अपने मेनविन्डो में दिए गए नियंत्रण में दिए गए प्रकार के सभी नियंत्रण प्राप्त करने में मदद नहीं की। मुझे उन्हें पुन: व्यवस्थित करने के लिए एक मेनू में सभी मेनू आइटम खोजने की आवश्यकता थी। वे सभी मेनू के प्रत्यक्ष वंशज नहीं थे, इसलिए मैं ऊपर दिए गए किसी भी कोड का उपयोग करके उनमें से केवल पहला लिलेन इकट्ठा करने में कामयाब रहा। यह विस्तार विधि किसी के लिए भी समस्या का समाधान है, जो यहाँ नीचे सभी तरह से पढ़ना जारी रखेगा।

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);
                }
            }
        }
    }

आशा करता हूँ की ये काम करेगा।


1

स्वीकार किए जाते हैं जवाब रिटर्न की खोज की तत्वों कम या ज्यादा अव्यवस्थित ,, संभव के रूप में गहरी के रूप में पहले बच्चे शाखा का अनुसरण करते हुए रास्ते की खोज की तत्वों उपज, उलटे पांव लौटने और अभी तक पार्स पेड़ की शाखाओं के लिए चरण दोहराते से पहले से।

यदि आपको अवरोही क्रम में अवरोही तत्वों की आवश्यकता है , जहां पहले बच्चों को पहले उतारा जाएगा, फिर उनके बच्चों और इसी तरह, निम्नलिखित एल्गोरिदम काम करेंगे:

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;
        }
    }
}

परिणामी तत्वों को निकटतम से दूर तक ऑर्डर किया जाएगा। यह उपयोगी होगा जैसे यदि आप किसी प्रकार और स्थिति के निकटतम बाल तत्व की तलाश कर रहे हैं:

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

1
कुछ याद आ रहा है; childअपरिभाषित है।
कोडबेंडर

1

@ अच्छा, वास्तव में अच्छा जवाब।

VB.NET संस्करण:

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

उपयोग (यह एक विंडो में सभी टेक्स्टबॉक्स को निष्क्रिय करता है):

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

-1

मुझे विजुअल ट्री हेल्पर्स के बिना यह आसान लगा:

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

3
यह केवल एक स्तर गहरा होता है। XAML में आपके पास गहरी नेस्टेड नियंत्रण हैं।
SQL पुलिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.