2011-04-19 21 views
24

WPF可以有多個GUI線程嗎?或者它總是隻有一個GUI線程(即使我有多個窗口/對話框)?可以/不可以WPF有多個GUI線程?

我在問,因爲我有來自其他線程的事件,我想在GUI線程中處理它們(因爲我需要根據事件修改主窗口的控件)。

btw:我知道我需要使用Dispatcher對象來達到此目的。所以,我可以改述我的問題,並問:WPF中的所有GUI元素總是隻有一個Dispatcher對象?

+1

是否已經閱讀了WPF線程模型文檔? http://msdn.microsoft.com/en-us/library/ms741870.aspx – 2011-04-19 13:13:18

回答

25

基於第一個答案中的鏈接,我自己做了一些驗證。我想在此分享結果。首先:

可以有多個GUI線程(因此可以有多個Dispatcher實例)。

但是:

只需在創建一個新的窗口(模與否)創建一個新的GUI線程。需要明確創建線程(通過創建Thread的新實例)。

注:而不是使用單獨的線程中,模態對話框很可能是通過利用Dispatcher.PushFrame()哪些塊被分派此方法的調用者,同時仍允許事件實現。

我已經創建了一個簡單的WPF類(再次,基於第一個答案的鏈接)來驗證所有這些東西。我在這裏分享它,所以你可以稍微玩一下。

MainWindow.xaml:

<Window x:Class="WindowThreadingTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Width="250" Height="130"> 
    <StackPanel> 
    <StackPanel Orientation="Horizontal"> 
     <TextBlock Text="Thread's ID is "/> 
     <TextBlock x:Name="m_threadId"/> 
    </StackPanel> 
    <StackPanel Orientation="Horizontal"> 
     <TextBlock Text="Thread's threading apartment is "/> 
     <TextBlock x:Name="m_threadTA"/> 
    </StackPanel> 
    <Button Click="OnCreateNewWindow" Content="Open New Window"/> 
    <Button Click="OnAccessTest" Content="Access Test"/> 
    </StackPanel> 
</Window> 

MainWindow.xaml.cs:

using System; 
using System.Threading; 
using System.Windows; 
using System.Windows.Media; 

namespace WindowThreadingTest { 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window { 
    private static uint s_windowNumber = 0; 

    private readonly MainWindow m_prevWindow; 

    public MainWindow() : this(null) { } 

    public MainWindow(MainWindow prevWindow) { 
     InitializeComponent(); 

     this.m_prevWindow = prevWindow; 

     this.Title = String.Format("Window {0}", ++s_windowNumber); 

     Thread thread = Thread.CurrentThread; 
     this.m_threadId.Text = thread.ManagedThreadId.ToString(); 
     this.m_threadTA.Text = thread.GetApartmentState().ToString(); 
    } 

    private void OnCreateNewWindow(object sender, RoutedEventArgs e) { 
     CreateNewWindow(true, false, true); 
    } 

    private void CreateNewWindow(bool newThread, bool modal, bool showInTaskbar) { 
     MainWindow mw = this; 

     if (newThread) { 
     Thread thread = new Thread(() => { 
      MainWindow w = new MainWindow(this); 
      w.ShowInTaskbar = showInTaskbar; 

      if (modal) { 
      // ShowDialog automatically starts the event queue for the new windows in the new thread. The window isn't 
      // modal though. 
      w.ShowDialog(); 
      } else { 
      w.Show(); 
      w.Closed += (sender2, e2) => w.Dispatcher.InvokeShutdown(); 
      System.Windows.Threading.Dispatcher.Run(); 
      } 
     }); 

     thread.SetApartmentState(ApartmentState.STA); 
     thread.Start(); 

     } else { 
     MainWindow w = new MainWindow(this); 
     w.ShowInTaskbar = showInTaskbar; 
     if (modal) { 
      // Even modal dialogs run in the same thread. 
      w.ShowDialog(); 
     } else { 
      w.Show(); 
     } 
     } 
    } 

    private void OnAccessTest(object sender, RoutedEventArgs e) { 
     if (m_prevWindow == null) { 
     return; 
     } 

     this.Background = Brushes.Lavender; 
     try { 
     m_prevWindow.Background = Brushes.LightBlue; 
     } catch (InvalidOperationException excpt) { 
     MessageBox.Show("Exception: " + excpt.Message, "Invalid Operation"); 
     } 
     m_prevWindow.Dispatcher.Invoke((Action)(() => m_prevWindow.Background = Brushes.Green)); 

     this.Dispatcher.Invoke((Action)(() => { 
     try { 
      m_prevWindow.Background = Brushes.Red; 
     } catch (InvalidOperationException excpt) { 
      MessageBox.Show("Exception: " + excpt.Message, "Invalid Dispatcher Operation"); 
     } 
     })); 
    } 
    } 
} 
+0

那麼如果你不能,那麼你不能,+1 – CloudyMarble 2013-02-21 13:36:25