C#, обработчики событий и потоки

Я пишу небольшое приложение чата, и у меня есть этот обработчик событий:

void o_Typing(object sender, EventArgs e)
{
    MessageBox.Show("Fired!");
    this.Text = "Fired!";
}

o_Typing это метод в классе, производном от TabPage, По сути, я хочу, чтобы у каждого разговора была своя вкладка.

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

Когда событие происходит, MessageBox всплывает, но заголовок вкладки не меняется. После того, как событие сработало один раз, оно никогда не сработало снова, что заставило меня поверить, что событие вызывается в рабочем потоке, хотя оно определено в потоке пользовательского интерфейса.

Как я могу получить мои события для вызова из рабочего потока, и использовать Invoke() заставить их выполнить в потоке пользовательского интерфейса?

2 ответа

Решение

Есть два варианта:

1) Сделайте обработчики событий потокобезопасными: используйте Control.Invoke/BeginInvoke в любом обработчике событий, который должен общаться с потоком пользовательского интерфейса.

2) Заставьте рабочий поток маршалировать обратно в поток пользовательского интерфейса, прежде чем вызывать событие - другими словами, используйте Control.Invoke как часть процесса создания события, так что все обработчики событий будут вызываться в потоке пользовательского интерфейса. В зависимости от того, как структурировано ваше приложение, вы можете не захотеть, чтобы ваш компонент сбора событий явно знал об интерфейсе пользователя, но когда он создается, вы можете передать ISynchronizeInvoke (который Control реализует), и ваш компонент может использовать это для поднятия своих событий в нужном потоке. Конечно, это работает (просто, во всяком случае), если каждый обработчик событий рад работать в том же потоке - но это часто бывает. Вы бы написали что-то вроде:

protected void OnFoo(EventArgs args)
{
    if (sync != null && sync.InvokeRequired)
    {
        sync.Invoke((Action) delegate { OnFoo(args) }, null);
        return;
    }
    EventHandler handler = Foo; // Where Foo is the event name
    if (handler != null)
    {
        handler (this, args);
    }
}

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

Для GUI-элементов вам нужно взглянуть на Invoke-методы.

С уважением

Другие вопросы по тегам