2011-05-03 44 views
7

的行爲困惑有人可以解釋我遇到的問題嗎?Dispatcher.BeginInvoke()

我正在做一個wpf項目。場景如下:

我需要在主UI線程上彈出一個窗口(模型窗口),然後關閉它。這些作品從另一個UI線程開始(以阻止用戶單擊主UI窗口),然後關閉此窗口。主代碼顯示在下面。它的工作原理。

據我所知在ShowDialog()返回(至少這是在UI線程的情況下,我的意思是沒有調度程序的代碼)之前,close方法不會得到執行,有沒有人有多線程的經驗?

Window window; 
    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     Thread thread = new Thread(() => 
      { 


       //create a window and let user work from this thread 

      //code is omitted. 



       //create another window on main UI thread 

       Application.Current.Dispatcher.BeginInvoke(new Action(() => 
       { 
        window = new Window(); 
        window.ShowDialog(); 
       })); 



       //do some work here 

       Thread.Sleep(1000); 

       Application.Current.Dispatcher.BeginInvoke(new Action(() => 
       { 
        //Thread.Sleep(1000); 
        window.Close(); 
       })); 
      }); 

     thread.Start(); 
    } 

謝謝你的時間!

回答

14

所以,如果我正確地理解你的問題,你說,這個代碼的工作正是你想要的方式,但你只是想了解如何(和爲什麼)它的工作原理?

這是它的工作原理。首先,你的線程運行這段代碼:

Application.Current.Dispatcher.BeginInvoke(new Action(() => 
{ 
    window = new Window(); 
    window.ShowDialog(); 
})); 

那排隊主(UI)線程的調度隊列中的動作,然後立即返回:您的工作線程繼續運行。

當應用程序第一次啓動時(通常通過編譯器生成的代碼初始化您的App.xaml對象,儘管您也可以通過調用Application.Run來顯式執行它),但它啓動了它的消息循環,這(僞代碼,非常非常簡單):

public class Application { 
    public void Run() { 
     while (!Exited && action = Dispatcher.DequeueAction()) 
      action(); 
    } 
} 

因此,在某些時候,你排隊的行動後不久,UI線程將得到周圍拉着你的行動從隊列中並運行它,此時你的動作產生一個窗口並以模態顯示。

模態窗口現在開始自己的消息循環,這是這樣的(再次,很簡單的):

public class Window { 
    public bool? ShowDialog() { 
     DisableOtherWindowsAndShow(); 
     while (!IsClosed && action = Dispatcher.DequeueAction()) 
      action(); 
     EnableOtherWindowsAndHide(); 
     return DialogResult; 
    } 
} 

後,你的工作線程運行此代碼:

Application.Current.Dispatcher.BeginInvoke(new Action(() => 
{ 
    window.Close(); 
})); 

再次,您的操作將排隊等待UI線程的調度程序隊列,然後BeginInvoke調用立即返回並且您的工作線程繼續運行。

因此,UI線程的消息循環遲早會出現並執行您的操作,這會告訴窗口關閉。這與用戶點擊標題欄的「X」按鈕具有基本相同的效果,即使在模態對話框中,這當然也完全可以做到。這會導致ShowDialog的消息循環終止(因爲窗口現在已關閉),此時對話框被隱藏,並且其他窗口被重新啓用,ShowDialog返回,您的原始(ShowDialog)操作已完成並返回,回到Application.Run中的原始消息循環。

請注意,每個線程有一個調度程序隊列,不是每個消息循環一個。所以你的「關閉」動作進入到你的「顯示對話」動作所做的同一隊列中。這是執行消息循環輪詢的一段不同的代碼(ShowDialog中的一個,而不是Application.Run中的一個),但循環的基礎是相同的。

+0

謝謝你的解釋,這真的有助於理解它是如何工作的!我實際上看了一下showdialog代碼,但是有一些難以理解的東西,比如ComponentDispatcher.PushModal,我把它們放在波紋管中,請你幫我看一下它? – xiaoheixiaojie 2011-05-10 06:33:05

3

BeginInvoke是一種非阻塞方法;它將該操作添加到調度程序隊列中,並不等待其完成。您應該使用Invoke代替,它在調度程序線程上同步調用方法。

+0

你好,謝謝你的回覆。恐怕這不是答案,我需要在第一次開始調用之後做一些工作,這會禁用主UI上的窗口。所以如果我使用Invoke,應用程序將被阻止。現在行爲不會導致任何問題。 – xiaoheixiaojie 2011-05-09 02:38:41

相關問題