WinForms межпроцессная модификация - PullRequest
2 голосов
/ 10 июля 2009

Всякий раз, когда я хочу изменить winform из другого потока, мне нужно использовать

->Invoke(delegate, params)

, чтобы изменение происходило в собственном потоке winform.

Для каждой функции, которая нуждается в изменении графического интерфейса, мне нужна другая функция делегата.

Существует ли какая-либо схема, которая позволяет мне ограничивать количество необходимых функций делегата? У меня есть класс контроллера, который обрабатывает весь графический интерфейс в одном месте, я думал о повторном использовании делегатов, но это плохо пахнет.

Я думаю, что мой вопрос может относиться ко всем языкам, где может работать winform

Ответы [ 4 ]

6 голосов
/ 10 июля 2009

Если вы используете C # 3, вы можете использовать лямбду, а в C # 2 - анонимных делегатов. Это упрощает синтаксис, когда нет необходимости повторно использовать поведение. Я всегда делаю синхронизацию в коде формы, а не в контроллере. Контроллер не должен быть обеспокоен подобными «водопроводными» проблемами, которые более специфичны для технологии, чем для логики контроллера.

public void ResetFields()
{
    // use "delegate" instead of "() =>" if .Net version < 3.5
    InvokeOnFormThread(() => 
    {
        firstInput.Text = Defaults.FirstInput;
        secondInput.Text = Defaults.SecondInput;
        thirdChoice.SelectedIndex = Defaults.ThirdChoice;
    });
}

// change Action to MethodInvoker for .Net versions less than 3.5
private void InvokeOnFormThread(Action behavior) 
{
    if (IsHandleCreated && InvokeRequired)
    {
        Invoke(behavior);
    }
    else
    {
        behavior();
    }
}

На практике, сделайте все открытые методы в вашей форме вызывающими «InvokeOnFormThread». С другой стороны, вы можете использовать AOP для перехвата открытых вызовов методов в вашей форме и вызова «InvokeOnFormThread», но вышеприведенное работает достаточно хорошо (если вы последовательны и не забываете всегда делать это с открытыми методами в форме или UserControls). 1004 *

2 голосов
/ 10 июля 2009

Посмотрите, как использовать существующие делегаты System.Action<T> и System.Func<T,T>:

control.Invoke(
    new Action<int, string>(
        (i, s) => MessageBox.Show(String.Format(s, i))), 1, "{0}");
int iret = (int) control.Invoke(new Func<int, int>(i1 => i1 + 1));
1 голос
/ 10 июля 2009

Ответ Майкла Медоуза - хороший пример того, как централизовать логику обновления формы графического интерфейса.

Что касается производительности (которой мы можем легко стать одержимыми) при вызове многочисленных делегатов для синхронизации внешнего интерфейса, некоторое время назад я написал программное обеспечение, которое превзошло эквивалентное C ++ (собственное) приложение Windows в плане синхронизации GUI! И это все благодаря BeginInvoke и классу ThreadPool.

Использование делегатов Action<> и Func<> и класса ThreadPool также полезно и учитывает общий шаблон Invoke (раскрытый Майклом выше):

public void TheGuiInvokeMethod(Control source, string text)
{
   if (InvokeRequired)
      Invoke(new Action<Control, string>(TheGuiInvokeMethod, source, text);
   else
   {
       // it is safe to update the GUI using the control
      control.Text = text;
   }
}

где TheGuiInvokeMethod действительно находится в форме или другом элементе управления.

0 голосов
/ 10 июля 2009

Я помню, как выключил чеки и проверил вручную, что каждый использованный мной вызов был безопасным.

Удивительное число из них можно назвать перекрестными, потому что у меня была гарантия того, где находятся определенные потоки (семафоры), или потому, что они вызывали базовые функции API, которые могли использоваться в других процессах.

У меня все еще было много вызовов, обычно для объектов контекста, поэтому я мог использовать MethodInvoker.

Я также столкнулся с неприятной ошибкой в ​​Control.Invoke, заставившей меня написать пользовательскую библиотеку invoker.

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