Обновление GUI при запуске класса обработчика событий в отдельном потоке? - PullRequest
0 голосов
/ 07 апреля 2009

У нас есть DLL, которая отслеживает изменения в статусе и ищет события из отдельного, приобретенного продукта. На самом деле, это API-интерфейс ScreenPop от Siemens, для тех из вас, кто может знать, что это такое. Я использую C # .NET 3.5 в качестве платформы.

Этот API требует много времени для инициализации, поэтому мы хотим использовать отдельный поток для его инициализации. В настоящее время у нас есть функциональность в классе ScreenPop. Класс отслеживает 2 события, событие изменения статуса и событие всплывающего окна (данные, которые говорят нам, кто звонит клиенту).

Способ, которым это в настоящее время реализовано, не работает или, по крайней мере, не работает надежно. Внутри класса ScreenPop есть метод инициализации, в который помещается весь долгосрочный код запуска. Это вызывается из конструктора класса, например:

public ScreenPop( string Address, int Ext, CallbackStatusType pStatusFunc,
      CallbackScreenPopType pPopFunc 
)
{
    CallbackStatus = pStatusFunc;
    CallbackPopup = pPopupFunc;

    Thread t = new Thread( StartInBackground );
    t.Start();
}

В коде GUI, функция в pStatusFunc обновляет метку состояния, а функция в pPopupFunc запускает другой код для всплывающего окна - прямо сейчас он просто отображает данные из события.

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

Итак, мой вопрос, должен ли я отказаться от этого в пользу подхода BackgroundWorker? Или я что-то упускаю при обновлении графического интерфейса?

Больше информации по запросу ... Спасибо, Dave

Ответы [ 2 ]

3 голосов
/ 07 апреля 2009

Вы никогда не сможете обновить GUI из другого потока - только из потока UI, который является тем, который запустил приложение. Вам нужно использовать метод Control.Invoke для запуска кода в потоке пользовательского интерфейса. Экземпляр формы, frmMain.Invoke.

2 голосов
/ 07 апреля 2009

Вы не можете использовать WinForms в многопоточной квартире, чтобы обойти это, вы должны выполнить маршалинг в поток пользовательского интерфейса для выполнения действий над ним или получения результатов. Поскольку вы используете C # 3.5, вы можете использовать лямбда-выражения, обобщения и методы расширения, чтобы создать действительно чистое и простое в использовании решение.

public static class ControlExtensions
{
  public static TResult InvokeEx<TControl, TResult>(this TControl control,
                            Func<TControl, TResult> func)
    where TControl : Control
  {
    if (control.InvokeRequired)
    {
      return (TResult)control.Invoke(func, control);
    }
    else
    {
      return func(control);
    }
  }
}

Теперь вы можете безопасно и легко вносить изменения или получать значения.

this.InvokeEx(f => f.label1.Text = "Hello from another thread");

new Thread(() =>
  {
    string formTitle = this.InvokeEx(f => f.Text); // Safely get form title
  }).Start();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...