Dot-Net

WPF 可以/是否有多個 GUI 執行緒?

  • April 19, 2011

WPF 可以/是否有多個 GUI 執行緒?還是它總是只有一個 GUI 執行緒(即使我有多個視窗/對話框)?

我問是因為我有來自其他執行緒的事件,我想在 GUI 執行緒中處理它們(因為我需要根據事件修改主視窗的控制項)。

順便說一句:我知道我需要Dispatcher為此目的使用一個對象。所以,我可以改寫我的問題並問:WPF 中的所有 GUI 元素是否總是只有一個Dispatcher對象?

根據第一個答案中的連結,我自己做了一些驗證。我想在這里分享結果。首先:

可以有多個 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");
       }
     }));
   }
 }
}

引用自:https://stackoverflow.com/questions/5716804