C # GUI Thread Error - PullRequest
       1

C # GUI Thread Error

3 голосов
/ 09 марта 2012

Я разрабатываю приложение, которое должно получать команды через интерфейс сокетов, а затем выполнять их в графическом интерфейсе.Это приложение разрабатывается на C # .NET 4.0 и использует WPF для своего графического интерфейса.

Интерфейс сокета имеет рабочий поток, который продолжает прослушивать сокет и обрабатывать его команды, так что если, например, команда Show PopupПолучено, рабочий поток вызывает класс менеджера, который отвечает за создание всплывающего окна и показывает его на главном экране.

Метод менеджера, который создает всплывающее окно и затем вызывает главный экран, выглядит следующим образом:

public void ProcessPopup(PopupModel model)
{
    switch (model.ScreenType)
    {
        case Screens.Type1:
            popup = new PopupType1();
            break;
        case Screens.Type2:
            popup = new PopupType2();
            break;
        case Screens.Type3:
            popup = new PopupType3();
            break;
        case Screens.Type4:
            popup = new PopupType4();
            break;
    }

    viewModel.SetModel(model);

    if (!Dispatcher.CurrentDispatcher.Equals(App.Current.Dispatcher))
    {
        App.Current.Dispatcher.Invoke((ThreadStart)delegate { mainScreen.ShowPopup(popup); });
    }
    else
    {
        mainScreen.ShowPopup(popup);
    }
}

Класс PopupType1:

public partial class PopupType1 : UserControl
{
    public PopupType1 ()
    {
        InitializeComponent();
    }
}

Проблема заключается в том, что при создании нового объекта PopupType1 я получаю следующее исключение:

System.InvalidOperationException: The calling thread must be STA, because many UI components require this.
   at System.Windows.Input.InputManager..ctor()
   at System.Windows.Input.InputManager.GetCurrentInputManagerImpl()
   at System.Windows.Input.InputManager.get_Current()
   at System.Windows.Input.KeyboardNavigation..ctor()
   at System.Windows.FrameworkElement.FrameworkServices..ctor()
   at System.Windows.FrameworkElement.EnsureFrameworkServices()
   at System.Windows.FrameworkElement..ctor()
   at System.Windows.Controls.Control..ctor()
   at System.Windows.Controls.UserControl..ctor()
   at MyApp.Views.PopupType1..ctor()
   at MyApp.Manager.ProcessPopup(PopupModel model)
   at MyApp.CommunicationController.ProcessAsync(XDocument messageXml)

У меня естьуже пробовал несколько вещей, таких как преобразование моего рабочего потока в поток STA или создание нового потока STA просто для обработки Popup, но они вызывали больше проблем, чем решали.

Наконец, важно упомянутьчто я делаю это, потому что мое приложение испытывает несколько "зависаний" во время своей работы, и яПолагаю, что они связаны с тем, что поток GUI WPF слишком перегружен задачами, чтобы быть должным образом реагирующим, поэтому я пытаюсь отделить обработку без GUI от потока GUI.

Ответы [ 5 ]

3 голосов
/ 09 марта 2012

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

2 голосов
/ 09 марта 2012

Вы создаете всплывающее окно в фоновом потоке.
Это не сработает;Вы можете создавать элементы управления только в потоке пользовательского интерфейса.

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

1 голос
/ 09 марта 2012

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

0 голосов
/ 09 марта 2012

Вы должны создать элемент пользовательского интерфейса в потоке пользовательского интерфейса. Например, вы можете сделать следующее:

 private static UserControl CreatePopup(PopupModel model){
    switch (model.ScreenType)
    {
        case Screens.Type1:
            popup = new PopupType1();
            break;
        case Screens.Type2:
            popup = new PopupType2();
            break;
        case Screens.Type3:
            popup = new PopupType3();
            break;
        case Screens.Type4:
            popup = new PopupType4();
            break;
    }
 }

И изменить ProcessPopup на:

public void ProcessPopup(PopupModel model)
{
    viewModel.SetModel(model);
    App.Current.Dispatcher.BeginInvoke(
              new Action(()=>{
                             mainScreen.ShowPopup(CreatePopup(model)); 
                             }
              ));
}

Также обратите внимание на использование BeginInvoke вместо Invoke. Это делается для того, чтобы ваш пользовательский интерфейс обновлялся асинхронно, чтобы вам не приходилось блокировать ваш рабочий поток в некоторых вещах интерфейса.

0 голосов
/ 09 марта 2012

По проекту только поток, создавший объект пользовательского интерфейса (основной), может получить доступ к объекту пользовательского интерфейса.Из вашего описания «интерфейс сокета имеет рабочий поток», поэтому интерфейс не имеет доступа к пользовательскому интерфейсу.Обратный вызов от фонового работника - то, где вы получаете доступ к пользовательскому интерфейсу.Поскольку вы хотите держать слушателя в горячем состоянии, я думаю, что "прогресс" может работать лучше для доступа к пользовательскому интерфейсу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...