2014-02-12 44 views
0

由於我的英語不好,我簡單解釋我的問題,並在此處粘貼代碼片段來描述問題。C#Application.DoEvents不能在我的應用程序中工作

這個問題是我們的winForm應用程序中的一個多線程問題。我簡單的邏輯如下面的代碼示例。

在測試代碼中,mainForm中有1個mainForm Form1和一個名爲「Start」的按鈕。當用戶點擊按鈕時,將從2個後臺線程中顯示form2和form3兩個表單。 Form2關閉後,Form1將被觸發關閉。但是form3顯示在這裏,所以我需要用戶自己關閉form3。所以我處理了form.Closing事件並使用Application.DoEvents()讓用戶關閉了form3。它看起來很有用。但實際上,form3可以接受用戶的操作,但form3不會按預期方式關閉。

請解釋爲什麼form3不能在這裏關閉,以及如何修改代碼以使用戶的關閉操作工作。

using System; 
using System.ComponentModel; 
using System.Threading; 
using System.Windows.Forms; 

namespace CloseFormFromMainThread 
{ 
public partial class Form1 : Form 
{ 
    private Form2 _form2; 
    private Form2 _form3; 
    private SynchronizationContext _synchronizationContext; 

    public Form1() 
    { 
     InitializeComponent(); 
     Closing += Form1Closing; 
    } 

    void Form1Closing(object sender, CancelEventArgs e) 
    { 
     while (_form3 != null) 
     { 
      Application.DoEvents(); 
      Thread.Sleep(100); 
     } 
    } 

    private void ButtonStartClick(object sender, EventArgs e) 
    { 
     var thread = new Thread(StartForm3); 
     thread.Start(); 

     var thread2 = new Thread(StartForm2); 
     thread2.Start(); 
    } 

    private void StartForm3() 
    { 
     Thread.Sleep(200); 
     var action = new Action(() => 
            { 
             _form3 = new Form2(); 
             _form3.Text = "form 3"; 
             _form3.ShowDialog(); 
             _form3 = null; 
            }); 
     ExecuteActionInUiThread(action); 
    } 

    private void Form1Load(object sender, EventArgs e) 
    { 
     _synchronizationContext = SynchronizationContext.Current; 
    } 

    private void StartForm2() 
    { 
     Thread.Sleep(500); 
     var action = new Action(() => 
     { 
      _form2 = new Form2(); 
      _form2.Text = "form 2"; 
      _form2.ShowDialog(); 

      Close(); 
     }); 
     ExecuteActionInUiThread(action); 
    } 

    private void ExecuteActionInUiThread(Action action) 
    { 
     var sendOrPostCallback = new SendOrPostCallback(o => action()); 
     _synchronizationContext.Send(sendOrPostCallback, null); 
    } 
} 
} 
+2

爲什麼使用線程?一切似乎都運行在UI線程上?第1步是擺脫所有不必要的複雜性並刪除線程。然後你就有機會看到你的程序真的做了什麼。 –

+2

實際代碼不應該需要Thread.Sleep()。而且GUI始終是1個線程,不要試圖做到這一點。 –

+0

@HenkHolterman:這是行不通的。 WinForms不支持多線程。它只會崩潰。 – PMF

回答

2

第一個建議:不要使用Application.DoEvents()。永遠。每當你認爲你需要它時,你的代碼流中就有一個概念性問題,你應該首先修復它。我猜你的代碼只是創建一個死鎖,因爲它會等待OnClosing回調才能處理更多事件(如關閉另一個表單)。

+0

如果我刪除應用程序。DoEvents(),如何讓對話接受用戶的點擊操作並處理關閉操作? – user3256047

+1

您正在以模式模式打開對話框(使用'ShowDialog()')。這會自動運行消息循環並處理事件。 – PMF

+0

已經執行了form3.Close()方法,但是沒有返回form3.ShowDialog()。爲什麼? – user3256047

0

當我檢查Form.Close()的源代碼,對於模態對話框,只提出了FormClosing事件,沒有提出關閉事件。沒有發送信號讓form3.ShowDialog繼續。所以我認爲只使用主線程和Application.DoEvents不能使代碼繼續。然後它是主線程中的一個軟死鎖。

當前,我使用另一個線程來檢查(_form3!= null)並讓主線程執行_form3.ShowDialog()邏輯。以下是我關於Form1Closing的代碼。

private bool _isFormClosing; 
    void Form1Closing(object sender, CancelEventArgs e) 
    { 
     if (_form3 == null) return; 

     e.Cancel = true; 
     if (!_isFormClosing) 
     { 
      _isFormClosing = true; 
      Task.Factory.StartNew((() => 
      { 
       while (_form3 != null) 
       { 
        Thread.Sleep(50); 
       } 

       ExecuteActionInUiThread(Close); 
      })); 
     } 
    } 
+0

不要這樣做。從後臺線程打開表單是一個非常糟糕的主意,除非你創建一個像Windows資源管理器這樣的多線程UI應用程序,其中每個UI線程都獨立於另一個線程。 – Noseratio

+0

在我的代碼中,所有UI都是在Ui線程中創建/關閉的。 – user3256047

相關問題