簡體   English   中英

使用Application.DoEvents()

[英]Use of Application.DoEvents()

可以在C#中使用Application.DoEvents()嗎?

此功能是否可以讓GUI跟VB6的DoEvents幾乎一樣地趕上應用程序的其余部分?

Hmya,DoEvents()持久的奧秘。 人們對此有強烈的反對意見,但是沒有人真正解釋它為什么是“不好的”。 與“不要變異結構”一樣的智慧。 嗯,為什么運行時和語言如此糟糕,為什么支持對結構進行突變? 同樣的原因:如果做得不好,就會用腳開槍射擊自己。 容易。 而正確做到這一點需要確切地知道它的作用,在DoEvents()的情況下,這絕對不容易理解。

馬上:幾乎所有Windows Forms程序實際上都包含對DoEvents()的調用。 巧妙地偽裝了它,但是使用了另一個名稱:ShowDialog()。 正是DoEvents()允許對話框成為模態對話框,而不會凍結應用程序中的其余窗口。

大多數程序員都希望使用DoEvents在編寫自己的模態循環時停止其用戶界面凍結。 它確實做到了; 它分派Windows消息並獲取所有繪畫請求。 但是,問題在於它不是選擇性的。 它不僅可以發送繪畫消息,還可以傳遞其他所有消息。

並且有一組導致麻煩的通知。 它們來自顯示器前方約3英尺。 例如,用戶可以在調用DoEvents()的循環運行時關閉主窗口。 那行得通,用戶界面不見了。 但是您的代碼沒有停止,它仍在執行循環。 那很糟。 非常非常糟糕。

還有更多:用戶可以單擊相同的菜單項或按鈕,以使相同的循環開始。 現在,您有兩個執行DoEvents()的嵌套循環,上一個循環已暫停,新循環從頭開始。 那可能行得通,但是男孩的可能性很小。 尤其是當嵌套循環結束且掛起的循環恢復時,嘗試完成已經完成的作業。 如果那沒有例外,那么可以肯定的是數據全部被打亂了。

回到ShowDialog()。 它執行DoEvents(),但請注意它還會執行其他操作。 除了對話框之外,它禁用了應用程序中的所有窗口 現在解決了三英尺的問題,用戶無法做任何事情來弄亂邏輯。 解決了關閉窗口和重新開始工作兩種故障模式。 換句話說,用戶沒有辦法使程序以不同的順序運行代碼。 就像您測試代碼時一樣,它將可預測地執行。 它使對話非常煩人。 誰不討厭激活對話框並且不能夠復制和粘貼其他窗口中的內容? 但這就是價格。

這是在代碼中安全使用DoEvents所需要的。 將所有表單的Enabled屬性設置為false是避免問題的一種快速有效的方法。 當然,沒有程序員真正喜歡這樣做。 而且沒有。 這就是為什么您不應該使用DoEvents()的原因。 您應該使用線程。 即使它們為您提供了豐富的方法,也可以用豐富多彩且難以理解的方式射腳。 但是,這樣做的好處是您只用自己的腳射擊; 它不會(通常)讓用戶拍攝她的照片。

C#和VB.NET的下一版本將使用新的await和async關鍵字提供另一種方式。 一部分是由DoEvents和線程引起的麻煩啟發的,而另一部分則是由WinRT的API設計啟發的,該API設計要求您在進行異步操作時保持UI更新。 就像從文件中讀取一樣。

可以,但是這是hack。

請參閱DoEvents是否有害?

直接從devdev引用的MSDN頁面進行:

調用此方法將導致在處理所有等待窗口消息時掛起當前線程。 如果消息導致事件被觸發,則您的應用程序代碼的其他區域可能會執行。 這可能會導致您的應用程序表現出難以調試的意外行為。 如果執行耗時較長的操作或計算,通常最好在新線程上執行這些操作。 有關異步編程的更多信息,請參見異步編程概述。

因此,Microsoft告誡不要使用它。

另外,我認為它是一種hack,因為它的行為是不可預測的,並且容易產生副作用(這是由於嘗試使用DoEvents而不是旋轉新線程或使用后台工作程序而產生的)。

這里沒有大男子主義-如果它作為一個強大的解決方案,我將無所不在。 但是,嘗試在.NET中使用DoEvents只是讓我感到痛苦。

是的,System.Windows.Forms命名空間的Application類中有一個靜態DoEvents方法。 在UI線程中執行長時間運行的任務時,可以使用System.Windows.Forms.Application.DoEvents()處理UI線程在隊列中等待的消息。 這樣做的好處是,在執行長任務時,UI看起來更具響應性,並且不會“鎖定”。 但是,這幾乎總是不是做事的最佳方法。 據微軟稱,DoEvents“……導致當前線程在處理所有等待的窗口消息時被掛起。” 如果觸發了事件,則可能會出現難以跟蹤的意外和間歇性錯誤。 如果您有一項繁重的任務,最好在單獨的線程中執行。 在單獨的線程中運行長任務,可以對其進行處理,而不會干擾UI繼續平穩運行。 在這里查看更多詳細信息。

是一個如何使用DoEvents的示例; 請注意,Microsoft還警告您不要使用它。

根據我的經驗,我建議在.NET中使用DoEvents時要格外謹慎。 在包含DataGridViews的TabControl中使用DoEvents時,我遇到了一些非常奇怪的結果。 另一方面,如果您要處理的只是帶有進度條的小表格,那么可能就可以了。

最重要的是:如果要使用DoEvents,則需要在部署應用程序之前對其進行徹底的測試。

是。

但是,如果您需要使用Application.DoEvents ,這通常表明應用程序設計不良。 也許您想在單獨的線程中做一些工作?

我在上面看到了jheriko的評論,並且最初同意,如果您最終旋轉主UI線程等待另一個線程上長時間運行的異步代碼來完成,那么我將無法避免使用DoEvents。 但是從Matthias的答案來看,在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的情況下,也會在初始屏幕后面的窗體上觸發鼠標單擊。

好的/答案:用對Respesh的簡單刷新調用替換DoEvents行,該調用位於我的初始屏幕中央FormSplash.Panel1.Refresh() UI很好地更新了,其他人已經警告過的DoEvents怪異已經消失了。

我已經看到許多使用“ DoEvents-Hack”的商業應用程序。 尤其是在渲染開始發揮作用時,我經常看到以下內容:

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

他們都知道這種方法的弊端。 但是,他們使用了hack,因為他們不知道任何其他解決方案。 以下是Tom Miller博客文章中采取的一些方法:

  • 設置您的窗體以使所有繪圖都在WmPaint中進行,然后在此處進行渲染。 在OnPaint方法結束之前,請確保執行this.Invalidate();。 這將導致立即再次觸發OnPaint方法。
  • P /調用Win32 API,然后調用PeekMessage / TranslateMessage / DispatchMessage。 (Doevents實際上執行類似的操作,但是您可以在沒有額外分配的情況下執行此操作)。
  • 編寫自己的窗體類,該窗體類是CreateWindowEx的小包裝,並讓您完全控制消息循環。 -確定DoEvents方法適合您,並堅持使用。

請查閱MSDN文檔中的Application.DoEvents方法。

如果在消息隊列中放置圖形處理以外的其他內容,Application.DoEvents可能會產生問題。

如果需要一些時間,它對於更新進度條並通知用戶MainForm構造和加載之類的進度很有用。

在我最近制作的一個應用程序中,每次在MainForm的構造函數中執行代碼塊時,我都使用DoEvents更新加載屏幕上的一些標簽。 在這種情況下,UI線程忙於在不支持SendAsync()調用的SMTP服務器上發送電子郵件。 我可能用Begin()和End()方法創建了一個不同的線程,並從它們中調用了Send(),但是該方法容易出錯,我希望我的應用程序的Main Form在構造期間不拋出異常。

DoEvents確實允許用戶單擊或鍵入並觸發其他事件,而后台線程是一種更好的方法。

但是,在某些情況下,您可能會遇到需要刷新事件消息的問題。 我遇到一個問題,當控件在隊列中要處理消息時,RichTextBox控件會忽略ScrollToCaret()方法。

以下代碼在執行DoEvent時阻止所有用戶輸入:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM