WPF उपयोगकर्ता नियंत्रण जनक


183

मेरे पास एक उपयोगकर्ता नियंत्रण है जिसे मैं एक MainWindowरनटाइम में लोड करता हूं । मुझे युक्त विंडो से हैंडल नहीं मिल सकता है UserControl

मैंने कोशिश की है this.Parent, लेकिन यह हमेशा अशक्त है। क्या कोई जानता है कि WPF में उपयोगकर्ता नियंत्रण से युक्त विंडो को कैसे संभालना है?

यहां बताया गया है कि नियंत्रण कैसे लोड किया जाता है:

private void XMLLogViewer_MenuItem_Click(object sender, RoutedEventArgs e)
{
    MenuItem application = sender as MenuItem;
    string parameter = application.CommandParameter as string;
    string controlName = parameter;
    if (uxPanel.Children.Count == 0)
    {
        System.Runtime.Remoting.ObjectHandle instance = Activator.CreateInstance(Assembly.GetExecutingAssembly().FullName, controlName);
        UserControl control = instance.Unwrap() as UserControl;
        this.LoadControl(control);
    }
}

private void LoadControl(UserControl control)
{
    if (uxPanel.Children.Count > 0)
    {
        foreach (UIElement ctrl in uxPanel.Children)
        {
            if (ctrl.GetType() != control.GetType())
            {
                this.SetControl(control);
            }
        }
    }
    else
    {
        this.SetControl(control);
    }
}

private void SetControl(UserControl control)
{
    control.Width = uxPanel.Width;
    control.Height = uxPanel.Height;
    uxPanel.Children.Add(control);
}

जवाबों:


346

निम्नलिखित का उपयोग करके देखें:

Window parentWindow = Window.GetWindow(userControlReference);

यह GetWindowविधि आपके लिए विजुअलट्री पर चलेगी और आपके नियंत्रण की मेजबानी कर रही खिड़की का पता लगाएगी।

नियंत्रण को लोड करने के बाद (और विंडो कंस्ट्रक्टर में नहीं) लोड करने की GetWindowविधि को रोकने के लिए आपको यह कोड चलाना चाहिए null। एक घटना की तरह तार:

this.Loaded += new RoutedEventHandler(UserControl_Loaded); 

6
अभी भी अशक्त है। यह ऐसा है जैसे कि नियंत्रण का कोई अभिभावक नहीं है।
donniefitz2

2
मैंने ऊपर दिए गए कोड का उपयोग किया और मुझे मिल गया। मेरे लिए भी।
पीटर वॉके

106
मुझे पता चला कि यह अशक्त है। मैं इस कोड को अपने यूजर कंट्रोल के कंस्ट्रक्टर में डाल रहा था। नियंत्रण लोड होने के बाद आपको यह कोड चलाना चाहिए। ईजी तार एक घटना: यह। खुला + = नया RoutedEventHandler (UserControl_Loaded);
पीटर वॉक

2
पॉल से प्रतिक्रिया की समीक्षा करने के बाद, यह भारित के बजाय OnInitialized पद्धति का उपयोग करने के लिए समझ में आ सकता है।
पीटर वॉके

@PeterWalke आप मेरी बहुत लंबे समय तक समस्या हल करती है ... धन्यवाद
वकास शब्बीर

34

मैं अपना अनुभव जोड़ूंगा। हालाँकि लोड की गई घटना का उपयोग करने से काम चल सकता है, लेकिन मुझे लगता है कि यह ऑनइंस्टीट्यूशनल विधि को ओवरराइड करने के लिए अधिक उपयुक्त हो सकता है। विंडो पहली बार प्रदर्शित होने के बाद लोड होता है। OnInitialized आपको कोई भी बदलाव करने का मौका देता है, उदाहरण के लिए, इसे रेंडर करने से पहले विंडो पर नियंत्रण जोड़ें।


8
सही के लिए +1। यह समझना कि किस तकनीक का उपयोग करना समय के लिए सूक्ष्म हो सकता है, खासकर जब आपको ईवेंट मिल गए हैं और ओवरडाइड को मिश्रण में फेंक दिया गया है (लोड की गई घटना, ओनलोडेड ओवरराइड, इनिशियलाइज़्ड ईवेंट, ऑनइंलिनेटेड ओवरराइड, वेटसेट)। इस मामले में, OnInitialized समझ में आता है क्योंकि आप माता-पिता को ढूंढना चाहते हैं, और नियंत्रण को माता-पिता के लिए "अस्तित्व में" होना चाहिए। लोडेड का मतलब कुछ अलग है।
ग्रेग डी

3
Window.GetWindowअभी भी अंदर लौटता nullहै OnInitializedLoadedकेवल घटना में काम करने लगता है ।
फिजिकबुद्ध

आरंभिक घटना को InitializeComponent () से पहले परिभाषित किया जाना चाहिए; वैसे भी, मेरे बाइंडेड (XAML) तत्व स्रोत (विंडो) को हल नहीं कर सके। इसलिए मैं भरी हुई घटना का उपयोग करने के लिए समाप्त हो गया।
लेनोर

15

VisualTreeHelper.GetParent का उपयोग करने का प्रयास करें या मूल विंडो को खोजने के लिए bellow पुनरावर्ती फ़ंक्शन का उपयोग करें।

 public static Window FindParentWindow(DependencyObject child)
    {
        DependencyObject parent= VisualTreeHelper.GetParent(child);

        //CHeck if this is the end of the tree
        if (parent == null) return null;

        Window parentWindow = parent as Window;
        if (parentWindow != null)
        {
            return parentWindow;
        }
        else
        {
            //use recursion until it reaches a Window
            return FindParentWindow(parent);
        }
    }

मैंने अपने उपयोगकर्ता नियंत्रण में से इस कोड का उपयोग करके पास करने की कोशिश की। मैंने इसे इस पद्धति में पारित किया लेकिन यह शून्य हो गया, यह दर्शाता है कि यह पेड़ का अंत है (आपकी टिप्पणी के अनुसार)। क्या आप जानते हैं कि ऐसा क्यों है? उपयोगकर्ता नियंत्रण में एक माता-पिता होते हैं जो युक्त रूप है। मुझे इस फॉर्म को कैसे संभालना है?
पीटर वॉके

2
मुझे पता चला कि यह अशक्त है। मैं इस कोड को अपने यूजर कंट्रोल के कंस्ट्रक्टर में डाल रहा था। नियंत्रण लोड होने के बाद आपको यह कोड चलाना चाहिए। ईजी तार एक घटना: यह। खुला + = नया RoutedEventHandler (UserControl_Loaded)
पीटर

एक अन्य मुद्दा डीबगर में है। VS लोड इवेंट के कोड को निष्पादित करेगा, लेकिन यह विंडो पैरेंट को नहीं मिलेगा।
बोहदन_ट्रोत्सेंको

1
यदि आप अपना स्वयं का तरीका लागू करने जा रहे हैं, तो आपको VisualTreeHelper और LogicalTreeHelper के संयोजन का उपयोग करना चाहिए। ऐसा इसलिए है क्योंकि कुछ गैर-विंडो नियंत्रण (जैसे पॉपअप) में दृश्य माता-पिता नहीं होते हैं और ऐसा प्रतीत होता है कि डेटा टेम्पलेट से उत्पन्न नियंत्रण तार्किक माता-पिता नहीं हैं।
ब्रायन रीचेल

14

मुझे लोडेड इवेंट हैंडलर के भीतर Window.GetWindow (यह) विधि का उपयोग करने की आवश्यकता है। दूसरे शब्दों में, मैंने एक उपयोगकर्ता नियंत्रण के माता-पिता को प्राप्त करने के लिए एलेक्स के जवाब के साथ संयोजन में इयान ओक्स के उत्तर दोनों का उपयोग किया।

public MainView()
{
    InitializeComponent();

    this.Loaded += new RoutedEventHandler(MainView_Loaded);
}

void MainView_Loaded(object sender, RoutedEventArgs e)
{
    Window parentWindow = Window.GetWindow(this);

    ...
}

7

इस दृष्टिकोण ने मेरे लिए काम किया लेकिन यह आपके प्रश्न जितना विशिष्ट नहीं है:

App.Current.MainWindow

7

अगर आपको यह सवाल लग रहा है और VisualTreeHelper आपके लिए काम नहीं कर रहा है या छिटपुट रूप से काम कर रहा है, तो आपको अपने एल्गोरिथ्म में LogicalTreeHelper को शामिल करना पड़ सकता है।

यहाँ मैं उपयोग कर रहा हूँ:

public static T TryFindParent<T>(DependencyObject current) where T : class
{
    DependencyObject parent = VisualTreeHelper.GetParent(current);
    if( parent == null )
        parent = LogicalTreeHelper.GetParent(current);
    if( parent == null )
        return null;

    if( parent is T )
        return parent as T;
    else
        return TryFindParent<T>(parent);
}

आप LogicalTreeHelper.GetParentकोड में एक विधि का नाम याद करते हैं।
xmedeko

यह मेरे लिए सबसे अच्छा समाधान था।
जैक बी निंबले

6

इस बारे में कैसा है:

DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);

public static class ExVisualTreeHelper
{
    /// <summary>
    /// Finds the visual parent.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sender">The sender.</param>
    /// <returns></returns>
    public static T FindVisualParent<T>(DependencyObject sender) where T : DependencyObject
    {
        if (sender == null)
        {
            return (null);
        }
        else if (VisualTreeHelper.GetParent(sender) is T)
        {
            return (VisualTreeHelper.GetParent(sender) as T);
        }
        else
        {
            DependencyObject parent = VisualTreeHelper.GetParent(sender);
            return (FindVisualParent<T>(parent));
        }
    } 
}

5

मैंने पाया है कि एक UserControl के माता-पिता हमेशा कंस्ट्रक्टर में शून्य होते हैं, लेकिन किसी भी घटना में अभिभावक सही तरीके से सेट हो जाते हैं। मुझे लगता है कि जिस तरह से नियंत्रण पेड़ को लोड किया गया है, उसके साथ कुछ करना होगा। तो इसके आस-पास जाने के लिए आप केवल अभिभावकों को लोड की गई घटना पर नियंत्रण में ला सकते हैं।

एक उदाहरण चेकआउट के लिए इस प्रश्न पर WPF उपयोगकर्ता नियंत्रण का DataContext अशक्त है


1
आपको इसके लिए पहले "पेड़" में होने की प्रतीक्षा करनी होगी। कई बार बहुत अप्रिय।
user7116

3

दूसरा रास्ता:

var main = App.Current.MainWindow as MainWindow;

मेरे लिए काम किया, इसे कंस्ट्रक्टर के बजाय "लोडेड" इवेंट में डालना होगा (प्रॉपर्टीज विंडो को ऊपर लाएं, डबल क्लिक करें और यह आपके लिए हैंडलर जोड़ देगा)।
कंटैंगो

(मेरा वोट इयान द्वारा स्वीकार किए गए उत्तर के लिए है, यह सिर्फ रिकॉर्ड के लिए है) यह तब काम नहीं किया जब उपयोगकर्ता नियंत्रण ShowDialog के साथ किसी अन्य विंडो में है, उपयोगकर्ता नियंत्रण के लिए सामग्री सेट कर रहा है। इसी तरह का दृष्टिकोण App.Current.Windows के माध्यम से चलना है और उस विंडो का उपयोग करना है जहां से निम्नलिखित स्थिति, idx के लिए (Current.Windows.Count - 1) से 0 (App.Current.Windows [idx] == userControlRef सत्य है) । यदि हम इसे उल्टे क्रम में करते हैं, तो इसकी अंतिम विंडो होने की संभावना है और हमें केवल एक पुनरावृत्ति के साथ सही विंडो मिलती है। userControlRef आम तौर पर है इस UserControl वर्ग के भीतर।
एमएसंजय

3

यह मेरे लिए काम कर रहा है:

DependencyObject GetTopLevelControl(DependencyObject control)
{
    DependencyObject tmp = control;
    DependencyObject parent = null;
    while((tmp = VisualTreeHelper.GetParent(tmp)) != null)
    {
        parent = tmp;
    }
    return parent;
}

3

यह मेरे लिए काम नहीं आया, क्योंकि यह पेड़ से बहुत दूर चला गया, और पूरे आवेदन के लिए पूर्ण जड़ खिड़की मिली:

Window parentWindow = Window.GetWindow(userControlReference);

हालाँकि, इसने तत्काल खिड़की लाने का काम किया:

DependencyObject parent = uiElement;
int avoidInfiniteLoop = 0;
while ((parent is Window)==false)
{
    parent = VisualTreeHelper.GetParent(parent);
    avoidInfiniteLoop++;
    if (avoidInfiniteLoop == 1000)
    {
        // Something is wrong - we could not find the parent window.
        break;
    }
}
Window window = parent as Window;
window.DragMove();

आपको एक अनियंत्रित 'avoidInfiniteLoop' वैरिएबल के बजाय एक अशक्त जांच का उपयोग करना चाहिए। अशक्त के लिए जाँच करने के लिए अपना 'जबकि' बदलें, और यदि अशक्त नहीं है, तो जाँच करें कि क्या यह एक खिड़की नहीं है। अन्यथा, बस तोड़ / बाहर निकलें।
मार्क ए। डोनोहे

@MarquelV मैं आपको सुनता हूं। आम तौर पर, मैं हर लूप के लिए एक "avoidInfiniteLoop" चेक जोड़ता हूं जो सिद्धांत में फंस सकता है अगर कुछ गलत हो जाता है। रक्षात्मक प्रोग्रामिंग का इसका हिस्सा है। हर बार अक्सर, यह अच्छे लाभांश का भुगतान करता है क्योंकि कार्यक्रम एक लटका से बचा जाता है। डिबगिंग के दौरान बहुत उपयोगी है, और उत्पादन में बहुत उपयोगी है यदि ओवररन लॉग किया गया है। मैं इस तकनीक (कई अन्य लोगों के बीच) का उपयोग करके मजबूत कोड लिखने में सक्षम बनाता हूं जो बस काम करता है।
कंटैंगो

मुझे रक्षात्मक प्रोग्रामिंग मिलती है, और मैं इसके बारे में सिद्धांत रूप में सहमत हूं, लेकिन एक कोड समीक्षक के रूप में, मुझे लगता है कि यह मनमाना डेटा प्रस्तुत करने के लिए फ़्लैग किया जाएगा जो वास्तविक तर्क प्रवाह का हिस्सा नहीं है। आपके पास पहले से ही अशक्तता को रोकने के लिए अनंत पुनरावृत्ति को रोकने के लिए आवश्यक सभी जानकारी है क्योंकि किसी पेड़ को अनंत रूप से पुनर्जीवित करना असंभव है। यकीन है कि आप माता-पिता को अपडेट करना भूल सकते हैं और एक अनंत लूप पा सकते हैं, लेकिन आप बस उस मनमाने ढंग से परिवर्तनशील अपडेट को आसानी से भूल सकते हैं। दूसरे शब्दों में, नए, असंबंधित डेटा की शुरुआत के बिना अशक्त की जांच करने के लिए यह पहले से ही रक्षात्मक प्रोग्रामिंग है।
मार्क ए। डोनोहे

1
@MarquelIV मुझे सहमत होना होगा। एक अतिरिक्त नल चेक जोड़ना बेहतर रक्षात्मक प्रोग्रामिंग है।
कंटैंगो

1
DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);

कृपया इसे हटा दें और इसे किसी भी बिंदु पर एकीकृत करें जो पहले से ही आपके अन्य उत्तर में शामिल नहीं है (जो मैंने इसके अच्छे उत्तर के रूप में
उतारा है

1
DependencyObject GetTopParent(DependencyObject current)
{
    while (VisualTreeHelper.GetParent(current) != null)
    {
        current = VisualTreeHelper.GetParent(current);
    }
    return current;
}

DependencyObject parent = GetTopParent(thisUserControl);

0

उपरोक्त का गोल्ड प्लेटेड संस्करण (मुझे एक जेनेरिक फ़ंक्शन की आवश्यकता है जो Windowसंदर्भ के भीतर आ सकता है MarkupExtension:

public sealed class MyExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider) =>
        new MyWrapper(ResolveRootObject(serviceProvider));
    object ResolveRootObject(IServiceProvider serviceProvider) => 
         GetService<IRootObjectProvider>(serviceProvider).RootObject;
}

class MyWrapper
{
    object _rootObject;

    Window OwnerWindow() => WindowFromRootObject(_rootObject);

    static Window WindowFromRootObject(object root) =>
        (root as Window) ?? VisualParent<Window>((DependencyObject)root);
    static T VisualParent<T>(DependencyObject node) where T : class
    {
        if (node == null)
            throw new InvalidOperationException("Could not locate a parent " + typeof(T).Name);
        var target = node as T;
        if (target != null)
            return target;
        return VisualParent<T>(VisualTreeHelper.GetParent(node));
    }
}

MyWrapper.Owner() निम्न आधार पर विंडो को सही ढंग से अनुमान लगाएगा:

  • Windowदृश्य पेड़ चलने से जड़ (यदि एक के संदर्भ में प्रयोग किया जाता है UserControl)
  • वह खिड़की जिसके भीतर इसका उपयोग किया जाता है (यदि यह एक Window's मार्कअप के संदर्भ में प्रयोग किया जाता है )

0

अलग दृष्टिकोण और अलग रणनीति। मेरे मामले में मैं दिए गए प्रकार के माता-पिता को खोजने के लिए विज़िटरट्रीहेल्पर या टेलरिक से एक्सटेंशन विधियों का उपयोग करके अपने संवाद की खिड़की नहीं ढूंढ सका। इसके बजाय, मुझे अपना संवाद दृश्य मिला, जो Application.Current.Windows का उपयोग करके सामग्री के कस्टम इंजेक्शन को स्वीकार करता है।

public Window GetCurrentWindowOfType<TWindowType>(){
 return Application.Current.Windows.OfType<TWindowType>().FirstOrDefault() as Window;
}

0

Window.GetWindow(userControl)वास्तविक खिड़की के बाद ही खिड़की प्रारंभ किया गया था वापस आ जाएगी ( InitializeComponent()विधि समाप्त हो गया)।

इसका मतलब है, कि यदि आपका उपयोगकर्ता नियंत्रण इसकी विंडो के साथ एक साथ आरंभीकृत किया गया है (उदाहरण के लिए, आपने अपना उपयोगकर्ता नियंत्रण विंडो की xaml फ़ाइल में डाल दिया है), तो उपयोगकर्ता नियंत्रण की OnInitializedघटना पर आपको विंडो नहीं मिलेगी (यह शून्य होगा), में कारण उस स्थिति में OnInitializedजब विंडो प्रारंभ होने से पहले उपयोगकर्ता नियंत्रण की घटना आग हो जाती है।

इसका अर्थ यह भी है कि यदि आपका उपयोगकर्ता नियंत्रण इसके विंडो के बाद आरंभीकृत किया जाता है, तो आप उपयोगकर्ता नियंत्रण के निर्माण में पहले से ही खिड़की प्राप्त कर सकते हैं।

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