Dispatcher.Invoke का उपयोग करके एक गैर-मुख्य थ्रेड से WPF नियंत्रण बदलें


81

मैंने हाल ही में WPF में प्रोग्रामिंग शुरू की है और निम्नलिखित समस्या से टकरा रहा है। मुझे समझ में नहीं आता कि कैसे Dispatcher.Invoke()विधि का उपयोग करना है। मुझे थ्रेडिंग का अनुभव है और मैंने कुछ सरल विंडोज़ फॉर्म प्रोग्राम बनाए हैं जहाँ मैंने अभी उपयोग किया है

Control.CheckForIllegalCrossThreadCalls = false;

हाँ, मुझे पता है कि यह बहुत ही लंगड़ा है लेकिन ये सरल निगरानी अनुप्रयोग थे।

तथ्य यह है कि अब मैं एक WPF एप्लिकेशन बना रहा हूं, जो बैकग्राउंड में डेटा को पुनः प्राप्त करता है, मैं डेटा को पुनः प्राप्त करने के लिए कॉल करने के लिए एक नया थ्रेड शुरू करता हूं (एक वेबसर्वर से), अब मैं इसे अपने WPF फॉर्म पर प्रदर्शित करना चाहता हूं। बात यह है कि, मैं इस धागे से कोई नियंत्रण स्थापित नहीं कर सकता। लेबल या कुछ भी नहीं। इसका समाधान कैसे हो सकता है?

उत्तर टिप्पणियाँ:
@ जालफ: तो जब मैं डेटा प्राप्त करता हूं तो इस डिस्पैचर पद्धति का उपयोग 'नए ट्रेडर
' में करता हूं? या क्या मुझे एक बैकग्राउंड वर्कर को डेटा प्राप्त करना चाहिए, इसे एक फ़ील्ड में डालना चाहिए और एक नया थ्रेड शुरू करना चाहिए जो इस फ़ील्ड के भर जाने तक प्रतीक्षा करता है और प्रेषित डेटा को कंट्रोल में दिखाने के लिए डिस्पैचर को कॉल करता है?


वह CheckForIllegalCrossThreadCalls कमाल है। काश मैं जानता था कि जल्दी से पहले "कौन परवाह करता है" अनुप्रयोगों के लिए
Gaspa79

जवाबों:


177

पहली बात यह समझना है कि, डिस्पैचर को लंबे अवरोधक संचालन (जैसे कि वेबसर्वर से डेटा पुनर्प्राप्त करने के लिए ...) के लिए डिज़ाइन नहीं किया गया है। आप डिस्पैचर का उपयोग कर सकते हैं जब आप एक ऑपरेशन चलाना चाहते हैं जिसे यूआई थ्रेड पर निष्पादित किया जाएगा (जैसे कि एक प्रगति बार के मूल्य को अपडेट करना)।

आप एक पृष्ठभूमि कार्यकर्ता में अपने डेटा को पुनः प्राप्त करने और UI थ्रेड में परिवर्तनों को प्रचारित करने के लिए ReportProgress विधि का उपयोग करने के लिए क्या कर सकते हैं।

यदि आपको सीधे डिस्पैचर का उपयोग करने की आवश्यकता है, तो यह बहुत आसान है:

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => this.progressBar.Value = 50));

22
आप 'नई क्रिया (' भाग से छुटकारा पा सकते हैं, और बस एक लंबो अभिव्यक्ति का उपयोग कर सकते हैं: DispatcherPyerity.Background, () => this.progressBar.Value = 50
jrista

हाँ, मुझे नहीं पता कि मैंने यहाँ एक एक्शन क्यों डाला: p
japf

1
@Carsten यह उत्तर WPF अनुप्रयोगों के लिए है जो System.Windows.Application वर्ग का उपयोग करते हैं।
जोशुआपोहल्स

10
@ जिरस्टा: क्या आप वास्तव में कर सकते हैं? मैं कोशिश कर रहा हूँ जब बिना CS1660 मिल रहा है new Action(...)
या मैपर

6
@jrista: सामान्य तौर पर, यह सच है - हालांकि यह लेख बताता है कि यह पैरामीटर रहित तरीकों के मामले में काम क्यों नहीं करता है जैसे कि पास किए गए BeginInvokeऔर इसके बजाय संकलक त्रुटि CS1660 उपज है।
या मैपर

31

जापफ ने इसका सही उत्तर दिया है। बस अगर आप बहु-पंक्ति क्रियाओं को देख रहे हैं, तो आप नीचे लिख सकते हैं।

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => { 
    this.progressBar.Value = 50;
  }));

अन्य उपयोगकर्ताओं के लिए सूचना जो प्रदर्शन के बारे में जानना चाहते हैं:

यदि आपका कोड उच्च प्रदर्शन के लिए लिखा जाना चाहिए, तो आप पहले देख सकते हैं कि CheckAccess ध्वज का उपयोग करके इनवोक आवश्यक है या नहीं।

if(Application.Current.Dispatcher.CheckAccess())
{
    this.progressBar.Value = 50;
}
else
{
    Application.Current.Dispatcher.BeginInvoke(
      DispatcherPriority.Background,
      new Action(() => { 
        this.progressBar.Value = 50;
      }));
}

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


4

जब कोई थ्रेड निष्पादित होता है और आप मुख्य UI थ्रेड को निष्पादित करना चाहते हैं जो कि वर्तमान थ्रेड द्वारा अवरुद्ध है, तो नीचे का उपयोग करें:

वर्तमान धागा:

Dispatcher.CurrentDispatcher.Invoke(MethodName,
    new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.

मुख्य UI थ्रेड:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background, new Action(() => MethodName(parameter)));

MethodName वर्तमान संदर्भ में मौजूद नहीं है
मेषकॉन्लाई

0

ऊपर @japf उत्तर ठीक काम कर रहा है और मेरे मामले में सीईएफ ब्राउज़र पेज लोड करने के बाद माउस स्पिनिंग को स्पिनिंग व्हील से वापस सामान्य एरो में बदलना चाहता था । मामले में यह किसी की मदद कर सकता है, यहाँ कोड है:

private void Browser_LoadingStateChanged(object sender, CefSharp.LoadingStateChangedEventArgs e) {
   if (!e.IsLoading) {
      // set the cursor back to arrow
      Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
         new Action(() => Mouse.OverrideCursor = Cursors.Arrow));
   }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.