Может / имеет ли WPF несколько потоков GUI? - PullRequest
31 голосов
/ 19 апреля 2011

Может / WPF имеет несколько потоков GUI? Или он всегда имеет только один поток GUI (даже если у меня несколько окон / диалогов)?

Я спрашиваю, потому что у меня есть события, поступающие из других потоков, и я бы хотел обработать их в потоке GUI (потому что мне нужно изменить элементы управления моего главного окна в соответствии с событиями).

Кстати: я знаю, что для этой цели мне нужно использовать объект Dispatcher. Итак, я мог бы перефразировать мой вопрос и спросить: всегда ли для всех элементов графического интерфейса в WPF только один Dispatcher объект?

1 Ответ

30 голосов
/ 28 апреля 2011

Основываясь на ссылке в первом ответе, я провел некоторую проверку самостоятельно.Я хотел бы поделиться результатами здесь.Прежде всего:

Может быть несколько потоков 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");
        }
      }));
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...