2012-01-23 147 views
9

我有一個基於vb.net的Windows應用程序,當點擊「GO」按鈕時,一堆數據被加載到數據庫中。所以在我的應用程序中,只要點擊「GO」按鈕,我想只是禁用它,並希望在上傳完成後啓用它。 現在,在我的btnGo_Click()具體方法我:按鈕禁用和啓用

btnGo.Enabled = False 

作爲一線和

btnGo.Enabled = True 

在相同的方法,最後一行。

但我不明白爲什麼「GO」雖然顯示爲禁用,但在處理過程中仍然允許點擊。此外,如果我刪除最後一行,它將被永久禁用,並且不允許單擊事件。

請提出我做錯了什麼?

編輯(日期:2012年1月25日):我按照我們的同事們的建議進行了修改,但我在這裏面臨一個新問題。我面臨的問題是文本框被更新,但並非總是如此。我在後臺工作線程的「_ProgressChanged」事件中更新了我的文本框。在我的情況下,如果有10個記錄上傳。然後在texbox中有10行更新。但文本框中只顯示幾行。它再次是重繪問題嗎?請建議...因爲所有其他事情都按照您的建議完成

回答

14

您沒有做錯任何事情。問題在於,只有在事件處理程序方法內部的代碼完成執行之後,纔會更新UI。然後,該按鈕被禁用並立即以快速順序啓用。

這就解釋了爲什麼,如果你在事件處理程序方法的結尾忘記重新啓用按鈕控制,它仍然是禁用的,因爲你告訴它在方法的第一行以禁用按鈕。

這是一個經典的例子,你爲什麼不應該在事件處理程序方法中執行長時間運行的計算任務,因爲它阻止了UI的更新。計算實際上需要在單獨的線程上進行。但不要嘗試手動創建線程,並且絕對不要嘗試從單獨的線程更新您的UI。相反,請使用BackgroundWorker component自動爲您處理所有這些問題。鏈接的MSDN文檔有一個關於如何使用它的很好的示例。

在開始BackgroundWorker之前禁用該按鈕,然後在其Completed事件中重新啓用該按鈕,表示完成數據庫加載。

+0

我同意。使用Backgroundworker是去這裏的路。它會保持用戶界面的響應。 – BenR

+0

嗨科迪,尼斯的文章。我在這裏有一個查詢:我有一個文本框得到更新,上傳(數據庫更新)正在發生。那麼我應該將這個特定的代碼移動到「backgroundWorker1_ProgressChanged」事件處理程序中嗎?因爲否則在「backgroundWorker1_DoWork」事件處理程序中,它會引發我「Cross Thread usage ...」的異常 –

+0

@Justin:是的,如果您要響應進度更新,則該代碼需要進入ProgressChanged事件處理程序。 'DoWork'事件處理程序方法就是你在後臺線程上做什麼的地方。你必須記住的是,你只能從一個線程(主要的,被指定爲UI線程)與你的UI進行交互。其他一切都無法觸及它,否則你會得到一個關於交叉線程的例外。 'BackgroundWorker'使這種分離變得容易,因爲你可以將代碼放入事件處理程序中,讓它擔心在正確的線程中引發它。 –

0

如果您的btnGo_Click()在主線程內運行,那麼在耗時的任務中無法正確更新用戶界面。
您可以通過BackgroundWorker運行您的方法來執行所需的最佳方法。

+0

我只是不明白爲什麼人們仍然建議'Application.DoEvents'。是的,它可能在這裏工作。不,這不是一個好主意。不,對於不瞭解線程,重入和Windows消息循環的人來說,在代碼中使用它並不是一件好事。其明顯的簡單性掩蓋了其真正複雜和潛在困難的現實。在這裏推薦它特別有害,人們有一個衆所周知的傾向,將直接在答案中找到的粘貼代碼直接複製到他們的項目中,而不理解它是如何工作的,爲什麼會起作用,或者使用它的陷阱是什麼。 –

+0

@CodyGray:對於你的評論我感到抱歉:我知道'Application.DoEvents'不好,事實上我首先想到的是我使用Backgroundworker。我的例子只是告訴OP可以完成,即使它不好...我要編輯我的文章... – Marco

3

由於您試圖執行一個可能需要一些時間的函數,因此我建議您使用線程。在.NET中,有一個BackgroundWorker組件,非常適合執行異步任務。

在按一下按鈕,調用BackgroundWorker的是這樣的:

if not bgwWorker.IsBusy then 
    btnGo.enabled = false 
    bgwWorker.RunWorkerAsync() 
end if 

,並使用完畢事件再次啓用該按鈕:

Private Sub bgwWorker_DoWork(ByVal sender As System.Object, _ 
       ByVal e As System.ComponentModel.DoWorkEventArgs) _ 
       Handles bgwWorker.DoWork 
' Do your things  
End Sub 

Private Sub bgwWorker_RunWorkerCompleted(ByVal sender As System.Object, _ 
         ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _ 
         Handles bgwWorker.RunWorkerCompleted 
' Called when the BackgroundWorker is completed. 
btnGo.enabled = true 
End Sub 

在上面的例子中,我使用bgwWorker作爲BackgroundWorker的實例。

+0

嗨狂想曲,好文章。我在這裏有一個查詢:我有一個文本框得到更新,上傳(數據庫更新)正在發生。那麼我應該將這個特定的代碼移動到「backgroundWorker1_ProgressChanged」事件處理程序中嗎?因爲在「backgroundWorker1_DoWork」事件處理程序中,它會引發我「Cross Thread usage ...」的異常 –

+0

是的,將BackgroundWorker的WorkerReportsProgress屬性設置爲true,並將bgwWorker.ReportProgress與ProgressChanged事件組合使用以更新用戶界面。 – Rhapsody

+0

我在這裏面臨一個問題,即文本框被更新,但並非總是如此。就像我的情況,如果有10個記錄上傳。然後在texbox中有10行更新。但文本框中只顯示幾行。它再次是重繪問題嗎?請建議... Bcos所有其他事情都按照您的建議完成 –

0

我只是試圖禁用按鈕,Update ing表格,Sleep ing,並重新啓用它。它仍然執行了點擊操作(點擊是在「禁用」按鈕時「睡眠」時完成的)。

我想形式「記住」點擊。

(編輯:我在C#這樣做)

+0

@downvoter謹慎解釋爲什麼?我的回答是第一個真正回答問題的人。 (並且不會在用戶界面上提供錯誤的信息,並且沒有更新。) – ispiro

+0

這是如何回答這個問題的?我不明白你所做的「測試」如何證明什麼,更不用說證明問題不是阻塞UI的結果。 –

+0

問題是他做錯了什麼,按鈕「雖然顯示爲禁用仍允許點擊」。答案是(可能) - 他沒有做錯任何事情。這就是表單如何工作。至於用戶界面,儘管這也是我的第一個想法 - a)「更新」未能糾正這種情況。 b)OP寫道:「顯示爲禁用」。 – ispiro

0

按鈕單擊事件一旦UI線程空閒時間處理。 禁用按鈕之後,UI線程會被代碼佔用。在方法結束時,重新啓用按鈕,然後退出該方法並允許空閒時間。 因此,該按鈕將在點擊事件處理的時間點啓用,因此您的點擊「已被識別」。

正如其他人所建議的那樣,解決方案是使用Backgroundworker。

不要嘗試使用doEvents()作爲解決方案(從來不這樣做),因爲這很容易引入其他細微問題。也就是說,你可以用代碼中的一些實驗性的doEvents來證明上面的解釋。如果在重新啓用按鈕之前執行了doEvents,您將看到該點擊被丟棄。另一方面,在button.disable(直到「更新GUI」)之後直接執行doEvents,如果在點擊之前執行它將無濟於事。

0

管理提交按鈕的狀態通常不是一個好主意。相反,對提交進行驗證。