Application.DoEvents का उपयोग ()


272

Application.DoEvents()C # में इस्तेमाल किया जा सकता है?

क्या यह फ़ंक्शन जीयूआई को बाकी ऐप के साथ पकड़ने की अनुमति देने का एक तरीका है, उसी तरह से जो वीबी 6 DoEventsकरता है?


35
DoEventsविंडोज फॉर्म का हिस्सा है, C # भाषा नहीं है। जैसे, इसे किसी भी .NET लैंग्वेज से इस्तेमाल किया जा सकता है। हालाँकि, इसका उपयोग किसी भी .NET भाषा से नहीं किया जाना चाहिए
स्टीफन क्लीयर

3
यह 2017 है। Async / Await का उपयोग करें। विवरण के लिए @hansPassant उत्तर का अंत देखें।
फास्टआल

जवाबों:


468

Hmya, DoEvents का स्थायी रहस्य ()। इसके खिलाफ भारी मात्रा में प्रतिक्रिया हुई है, लेकिन कोई भी वास्तव में यह नहीं बताता है कि यह "बुरा" क्यों है। "एक संरचना को उत्परिवर्तित न करें" के समान ज्ञान। एर्म, क्यों रनटाइम और भाषा एक संरचना को बदलने का समर्थन करती है अगर यह इतना बुरा है? एक ही कारण: आप अपने आप को पैर में गोली मारते हैं यदि आप इसे सही नहीं करते हैं। सरलता। और इसे सही करने के लिए यह जानना आवश्यक है कि यह क्या करता है, जो DoEvents () के मामले में निश्चित रूप से आसान नहीं है।

बल्ले से राइट: लगभग किसी भी विंडोज फॉर्म प्रोग्राम में वास्तव में DoEvents () के लिए कॉल होता है। यह चतुराई से प्रच्छन्न है, हालांकि एक अलग नाम के साथ: ShowDialog ()। यह DoEvents () है जो एक संवाद को अनुप्रयोग में बाकी खिड़कियों को फ्रीज किए बिना मोडल होने की अनुमति देता है।

जब वे अपने स्वयं के मोडल लूप लिखते हैं, तो अधिकांश प्रोग्रामर अपने उपयोगकर्ता इंटरफ़ेस को ठंड से रोकने के लिए DoEvents का उपयोग करना चाहते हैं। यह निश्चित रूप से करता है; यह विंडोज संदेशों को भेजता है और किसी भी रंग अनुरोध को वितरित करता है। समस्या यह है कि यह चयनात्मक नहीं है। यह न केवल पेंट संदेश भेजता है, यह सब कुछ और भी वितरित करता है।

और सूचनाओं का एक सेट है जो परेशानी का कारण बनता है। वे मॉनिटर के सामने लगभग 3 फीट से आते हैं। उदाहरण के लिए उपयोगकर्ता मुख्य विंडो को बंद कर सकता है जबकि लूप जो DoEvents () कह रहा है, चल रहा है। यह काम करता है, यूजर इंटरफेस चला गया है। लेकिन आपका कोड बंद नहीं हुआ, यह अभी भी लूप निष्पादित कर रहा है। यह बुरी बात है। बहुत बहुत बुरा।

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

ShowDialog () पर वापस जाएं। यह DoEvents () निष्पादित करता है, लेकिन ध्यान दें कि यह कुछ और करता है। यह डायलॉग के अलावा एप्लिकेशन की सभी विंडो को निष्क्रिय कर देता है । अब जब 3 फीट की समस्या हल हो गई है, तो उपयोगकर्ता तर्क को गड़बड़ाने के लिए कुछ भी नहीं कर सकता है। क्लोज-ऑफ-विंडो और स्टार्ट-द-जॉब फिर से विफलता दोनों मोड हल किए गए हैं। या इसे दूसरे तरीके से रखने के लिए, उपयोगकर्ता के पास आपके प्रोग्राम को एक अलग क्रम में रन कोड बनाने का कोई तरीका नहीं है। यह अनुमानित रूप से निष्पादित करेगा, ठीक उसी तरह जैसे आपने अपने कोड का परीक्षण किया था। यह संवादों को बेहद कष्टप्रद बनाता है; जो एक संवाद सक्रिय होने से नफरत नहीं करता है और किसी अन्य विंडो से कुछ कॉपी और पेस्ट करने में सक्षम नहीं है? लेकिन यही कीमत है।

जो आपके कोड में सुरक्षित रूप से DoEvents का उपयोग करने के लिए लेता है। समस्याओं से बचने के लिए आपके सभी रूपों की झूठी संपत्ति सेट करना एक त्वरित और कुशल तरीका है। बेशक, कोई भी प्रोग्रामर वास्तव में ऐसा करना पसंद नहीं करता है। और नहीं करता है। यही कारण है कि आपको DoEvents () का उपयोग नहीं करना चाहिए। आपको थ्रेड्स का उपयोग करना चाहिए। भले ही वे आपको अपने पैरों को रंगीन और अचूक तरीके से शूट करने के तरीकों का पूरा शस्त्रागार सौंप दें। लेकिन इस लाभ के साथ कि आप केवल अपने ही पैर को गोली मारते हैं; यह (आम तौर पर) उपयोगकर्ता को उसे गोली मारने नहीं देगा।

C # और VB.NET के अगले संस्करण नए प्रतीक्षा और async कीवर्ड के साथ एक अलग बंदूक प्रदान करेंगे। DoEvents और थ्रेड्स के कारण होने वाली परेशानी से छोटे हिस्से में प्रेरित है, लेकिन WinRT के एपीआई डिजाइन के द्वारा बड़े हिस्से में आपको अपने यूआई को अद्यतन रखने की आवश्यकता है जबकि एक अतुल्यकालिक ऑपरेशन हो रहा है। जैसे किसी फाइल से पढ़ना।


1
यह बर्फ बर्ग की केवल टिप है, हालांकि। मैं Application.DoEventsसमस्याओं के बिना उपयोग कर रहा हूं जब तक कि मैंने अपने कोड में कुछ यूडीपी विशेषताओं को नहीं जोड़ा, जिसके परिणामस्वरूप यहां वर्णित समस्या हुई । मैं वहाँ के चारों ओर एक तरीका है जानकर प्रसन्नता होगी कि साथ DoEvents
darda

यह एक अच्छा जवाब है, केवल एक चीज जो मुझे महसूस होती है कि यह याद आती है, यह सामान्य रूप से उपज निष्पादन और थ्रेड-सुरक्षित डिज़ाइन या गुणा करने की एक व्याख्या / चर्चा है - लेकिन यह आसानी से तीन गुना अधिक पाठ है। :)
जेरिको

5
आम तौर पर "थ्रेड्स का उपयोग करें" की तुलना में अधिक व्यावहारिक समाधान से लाभ होगा। उदाहरण के लिए, BackgroundWorkerघटक आपके लिए थ्रेड्स का प्रबंधन करता है, फुट-शूटिंग के अधिकांश रंगीन परिणामों से बचता है, और इसके लिए एज सी # भाषा संस्करणों में रक्तस्राव की आवश्यकता नहीं होती है।
बेन वोइग्ट

29

यह हो सकता है, लेकिन यह एक हैक है।

देखें क्या ईविल्स ईविल है?

से सीधे MSDN पेज कि thedev संदर्भित:

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

इसलिए Microsoft इसके उपयोग के खिलाफ चेतावनी देता है।

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

यहां कोई माचिस नहीं है - अगर यह एक मजबूत समाधान के रूप में काम करता है तो मैं इसे खत्म कर दूंगा। हालाँकि, .NET में DoEvents का उपयोग करने की कोशिश से मुझे दर्द के अलावा कुछ नहीं मिला।


1
यह ध्यान देने योग्य है कि यह पोस्ट 2004 से है। .NET 2.0 से पहले और BackgroundWorker"सही" तरीके से सरल बनाने में मदद की।
जस्टिन

माना। साथ ही, .NET 4.0 में टास्क लाइब्रेरी भी अच्छी है।
RQDQ

1
यह हैक क्यों होगा यदि Microsoft ने बहुत उद्देश्य के लिए एक फ़ंक्शन प्रदान किया है जिसके लिए अक्सर इसकी आवश्यकता होती है?
क्रेग जॉन्सटन

1
@ क्रेग जॉनसन - मेरे उत्तर को और अधिक पूरी तरह से विस्तार से अपडेट किया कि मैं क्यों मानता हूं कि DoEvents हैकरी की श्रेणी में आता है।
RQDQ

उस कोडिंग हॉरर आर्टिकल ने इसे "DoEvents spackle" कहा। प्रतिभाशाली!
गोनाज़ोब्रिन्स

24

हाँ, System.Windows.Forms नाम स्थान में अनुप्रयोग वर्ग में एक स्थिर DoEvents विधि है। System.Windows.Forms.Application.DoEvents () UI थ्रेड में लंबे समय से चल रहे कार्य को करते समय UI थ्रेड पर कतार में प्रतीक्षा कर रहे संदेशों को संसाधित करने के लिए उपयोग किया जा सकता है। इससे यूआई अधिक प्रतिक्रियाशील लगता है और "लॉक अप" नहीं है, जबकि एक लंबा काम चल रहा है। हालांकि, यह लगभग हमेशा चीजों को करने का सबसे अच्छा तरीका नहीं है। DoEvents को कॉल करने वाले Microsoft के अनुसार "... वर्तमान थ्रेड को निलंबित करने का कारण बनता है जबकि सभी प्रतीक्षा विंडो संदेशों को संसाधित किया जाता है।" यदि किसी घटना को ट्रिगर किया जाता है तो अप्रत्याशित और रुक-रुक कर आने वाली बग के लिए एक संभावना है जो नीचे ट्रैक करना मुश्किल है। यदि आपके पास एक व्यापक कार्य है तो इसे अलग थ्रेड में करना बेहतर है। एक अलग थ्रेड में लंबे कार्यों को चलाने से उन्हें सुचारू रूप से चलने वाले UI के साथ हस्तक्षेप किए बिना संसाधित किया जा सकता है। देखोअधिक जानकारी के लिए यहाँ

यहाँ DoEvents का उपयोग करने का एक उदाहरण है; ध्यान दें कि Microsoft इसका उपयोग करने के प्रति सावधानी भी प्रदान करता है।


13

अपने अनुभव से मैं .NET में DoEvents का उपयोग करने के साथ बहुत सावधानी की सलाह दूंगा। DataGridViews वाले TabControl में DoEvents का उपयोग करते समय मुझे कुछ बहुत ही अजीब परिणाम का अनुभव हुआ। दूसरी ओर, यदि आप सभी के साथ काम कर रहे हैं तो एक प्रगति पट्टी के साथ एक छोटा रूप है तो यह ठीक हो सकता है।

लब्बोलुआब यह है: यदि आप DoEvents का उपयोग करने जा रहे हैं, तो आपको अपने आवेदन को तैनात करने से पहले इसे अच्छी तरह से परखने की आवश्यकता है।


अच्छा जवाब है, लेकिन अगर मैं प्रगति की चीज के लिए एक समाधान सुझा सकता हूं जो बेहतर है , तो मैं कहूंगा कि अपना काम एक अलग सूत्र में करें, अपनी प्रगति के संकेतक को एक अस्थिर, इंटरलॉक किए गए चर में उपलब्ध कराएं, और एक टाइमर से अपनी प्रगति पट्टी को ताज़ा करें। । इस तरह कोई रखरखाव कोडर आपके लूप में हैवीवेट कोड जोड़ने के लिए लुभाता है।
mg30rg

1
DoEvents या समतुल्य अपरिहार्य है यदि आपके पास UI की प्रक्रियाएं हैं और UI को लॉक नहीं करना चाहते हैं। पहला विकल्प यूआई प्रसंस्करण के बड़े हिस्से नहीं करना है, लेकिन यह कोड को कम बनाए रखने योग्य बना सकता है। हालाँकि, Dispatcher.Yield () का इंतजार DoEvents के समान ही करता है और अनिवार्य रूप से एक UI प्रक्रिया की अनुमति दे सकता है जो स्क्रीन को सभी इरादों और उद्देश्यों के लिए बनाया जा सकता है।
मेलबर्न डेवलपर

11

हाँ।

हालांकि, अगर आपको उपयोग करने की आवश्यकता है, तो Application.DoEventsयह ज्यादातर खराब एप्लिकेशन डिज़ाइन का संकेत है। शायद आप इसके बजाय एक अलग धागे में कुछ काम करना चाहते हैं?


3
क्या होगा अगर आप इसे चाहते हैं तो आप स्पिन कर सकते हैं और काम को दूसरे धागे में पूरा करने की प्रतीक्षा कर सकते हैं?
जेरिको

@jheriko तो आपको वास्तव में async-इंतजार करना चाहिए।
mg30rg

5

मैंने ऊपर jheriko की टिप्पणी देखी और शुरू में सहमति व्यक्त कर रहा था कि मैं DoEvents का उपयोग करने से बचने का कोई तरीका नहीं ढूंढ सकता, यदि आप अपने मुख्य UI थ्रेड को समाप्त करने के लिए एक लंबे धागे के लंबे समय तक चलने वाले अतुल्यकालिक टुकड़े को पूरा करने के लिए प्रतीक्षा कर रहे हैं। लेकिन मैथियस के जवाब से मेरे यूआई पर एक छोटे पैनल का एक साधारण रिफ्रेश DoEvents को बदल सकता है (और बुरा साइड इफेक्ट से बच सकता है)।

मेरे मामले पर अधिक विवरण ...

मैं निम्नलिखित (जैसा कि यहाँ सुझाव दिया गया है ) यह सुनिश्चित करने के लिए कर रहा था कि एक प्रगति बार टाइप स्प्लैश स्क्रीन ( लंबे समय तक चलने वाले SQL कमांड के दौरान अपडेट की गई "लोडिंग" ओवरले कैसे प्रदर्शित हो ):

IAsyncResult asyncResult = sqlCmd.BeginExecuteNonQuery();
while (!asyncResult.IsCompleted)  //UI thread needs to Wait for Async SQL command to return
{
      System.Threading.Thread.Sleep(10); 
      Application.DoEvents();  //to make the UI responsive
}

खराब: मेरे लिए DoEvents को कॉल करने का मतलब था कि माउस क्लिक कभी-कभी मेरी स्प्लैश स्क्रीन के पीछे के रूपों पर फायरिंग करते थे, भले ही मैंने इसे TopMost बनाया हो।

अच्छा / उत्तर: DoEvents लाइन को एक साधारण रीफ्रेश कॉल के साथ मेरी स्प्लैश स्क्रीन के केंद्र में एक छोटे पैनल में बदलें FormSplash.Panel1.Refresh()। UI अच्छी तरह से अद्यतन करता है और DoEvents अजीबता दूसरों को चेतावनी दी गई है।


3
ताज़ा करना विंडो को अपडेट नहीं करता है, हालाँकि। यदि कोई उपयोगकर्ता डेस्कटॉप पर किसी अन्य विंडो का चयन करता है, तो आपकी विंडो पर वापस क्लिक करने से कोई प्रभाव नहीं पड़ेगा और OS आपके एप्लिकेशन को अनुत्तरदायी के रूप में सूचीबद्ध करेगा। DoEvents () एक रिफ्रेश की तुलना में बहुत अधिक करता है, क्योंकि यह मैसेजिंग सिस्टम के माध्यम से OS के साथ इंटरैक्ट करता है।
थंडरग्रो

4

मैंने "DoEvents-Hack" का उपयोग करके कई वाणिज्यिक एप्लिकेशन देखे हैं। विशेष रूप से जब प्रतिपादन नाटक में आता है, तो मैं अक्सर यह देखता हूं:

while(running)
{
    Render();
    Application.DoEvents();
}

वे सभी उस तरीके की बुराई के बारे में जानते हैं। हालाँकि, वे हैक का उपयोग करते हैं, क्योंकि वे किसी अन्य समाधान को नहीं जानते हैं। टॉम मिलर द्वारा ब्लॉग पोस्ट से लिए गए कुछ दृष्टिकोण इस प्रकार हैं :

  • WmPaint में सभी ड्राइंग होने के लिए अपना फॉर्म सेट करें, और वहां अपना रेंडरिंग करें। OnPaint विधि के अंत से पहले, सुनिश्चित करें कि आप यह करते हैं। Invalidate (); इससे ऑनपैंट विधि को तुरंत फिर से निकाल दिया जाएगा।
  • Win32 API में P / Invoke और PeekMessage / TranslateMessage / DispatchMessage पर कॉल करें। (Doevents वास्तव में कुछ ऐसा ही करते हैं, लेकिन आप अतिरिक्त आवंटन के बिना भी ऐसा कर सकते हैं)।
  • अपना स्वयं का प्रपत्र वर्ग लिखें जो CreateWindowEx के आसपास एक छोटा आवरण है, और संदेश लूप पर अपने आप को पूर्ण नियंत्रण दें। -देखें कि DoEvents मेथड आपके लिए ठीक काम करता है और इसके साथ रहना है।


3

DoEvents उपयोगकर्ता को आसपास क्लिक करने या टाइप करने और अन्य घटनाओं को ट्रिगर करने की अनुमति देता है, और पृष्ठभूमि थ्रेड एक बेहतर दृष्टिकोण है।

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

निम्नलिखित कोड DoEvents निष्पादित करते समय सभी उपयोगकर्ता इनपुट को अवरुद्ध करता है:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Integrative.Desktop.Common
{
    static class NativeMethods
    {
        #region Block input

        [DllImport("user32.dll", EntryPoint = "BlockInput")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt);

        public static void HoldUser()
        {
            BlockInput(true);
        }

        public static void ReleaseUser()
        {
            BlockInput(false);
        }

        public static void DoEventsBlockingInput()
        {
            HoldUser();
            Application.DoEvents();
            ReleaseUser();
        }

        #endregion
    }
}

1
आपको हमेशा कबूतरों को बुलाते समय घटनाओं को रोकना चाहिए। अन्यथा आपके ऐप पर अन्य ईवेंट प्रतिक्रिया में आग लगा देंगे और आपका ऐप एक ही बार में दो काम करना शुरू कर सकता है।
फास्टआल

2

Application.DoEvents समस्याएँ पैदा कर सकते हैं, अगर ग्राफिक्स प्रोसेसिंग के अलावा कुछ और संदेश कतार में डाल दिया जाए।

यह प्रगति सलाखों को अपडेट करने और मेनफ़ॉर्म निर्माण और लोडिंग जैसी किसी चीज़ में प्रगति के उपयोगकर्ता को सूचित करने के लिए उपयोगी हो सकता है, अगर कुछ समय लगता है।

हाल ही में किए गए एक आवेदन में, मैंने हर बार लोड स्क्रीन पर कुछ लेबलों को अपडेट करने के लिए DoEvents का उपयोग किया, जब कोड को मेरे MainForm के निर्माता में निष्पादित किया जाता है। UI थ्रेड इस मामले में, एक SMTP सर्वर पर एक ईमेल भेजने के साथ व्याप्त है, जिसने SendAsync () कॉल का समर्थन नहीं किया था। मैं शायद शुरुआत () और अंत () विधियों के साथ एक अलग धागा बना सकता था और उनके पास से एक सेंड () कहा जाता था, लेकिन वह तरीका त्रुटि-प्रवण है और मैं निर्माण के दौरान अपवाद नहीं फेंकने वाले अपने आवेदन के मुख्य रूप को प्राथमिकता दूंगा।

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