2009-07-10 6 views
2

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

->Invoke(delegate, params)

так, что изменение происходит в отдельном потоке в Winform в.

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

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

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

+0

Какая версия .NET? –

ответ

6

Если вы используете C# 3, вы можете использовать lambda, а в 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).

2

Посмотрите на использование существующих 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)); 
+0

Печально, но эти делегаты недоступны в .net 2. Однако их можно легко объявить самим. – arbiter

+0

См. Http://msdn.microsoft.com/en-us/library/018hxwa8.aspx. 2.0 и выше. –

0

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

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

У меня все еще было много вызовов, обычно на объектах контекста, чтобы я мог MethodInvoker.

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

+0

В чем была ошибка ... Я никогда не сталкивался с неприятным, но знание, как говорится, в половине битвы, и, возможно, я избежал невежества. :) –

+0

Закрытие элемента управления, у которого есть ожидающий вызов, имеет состояние гонки для сбоя. Моя пользовательская версия будет вызываться правильно, пока собственный поток все еще находится в цикле сообщений. – Joshua

1

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

Что касается производительности (которую мы можем легко одержим) вызова многочисленных делегатов для синхронизации интерфейса, некоторое время назад я написал программное обеспечение, которое превосходило эквивалентное C++ (собственное) приложение Windows с точки зрения синхронизации графического интерфейса пользователя! И это было всем благодаря 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 действительно находиться в форме или другом элементе управления.

Смежные вопросы