Подтвердить что ты не робот

Может ли WPF иметь несколько потоков графического интерфейса?

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

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

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

4b9b3361

Ответ 1

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

Могут быть несколько потоков GUI (и для них несколько экземпляров Dispatcher).

Однако:

Простое создание нового окна (модальное или нет) не создает новый поток графического интерфейса.. Необходимо создать поток явно (путем создания нового экземпляра 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 ID is "/>
      <TextBlock x:Name="m_threadId"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="Thread 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");
        }
      }));
    }
  }
}