Есть ли вариант Control.BeginInvoke, который работает до / после уничтожения дескриптора? - PullRequest
4 голосов
/ 23 декабря 2010

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

Проблема возникает при утилизации элемента управления. Поскольку все происходит асинхронно, то есть всегда возможно, что обратный вызов события ставится в очередь во время удаления, я иногда получаю InvalidOperationException (Invoke или BeginInvoke нельзя вызывать для элемента управления, пока не будет создан дескриптор окна.).

Это не то поведение, которое я хочу. Я хочу, чтобы обратный вызов выполнялся, даже если элемент управления был удален (даже если это вызывает исключение в обратном вызове; для меня это гораздо более полезное исключение!). Я хочу обработать поведение состояния удаления в каждом обратном вызове (обычно просто пропустить, если уничтожено, но иногда нет [например, один элемент управления регистрирует события (необязательно в файл), и я не хочу терять данные журнала!].).

Есть ли метод, который работает так, как я хочу? Могу ли я сам написать нехрупкий?

1 Ответ

6 голосов
/ 23 декабря 2010

Попробуйте SynchronizationContext.Current вместо. Здесь есть элементы Post и Send, которые примерно отображаются на BeginInvoke и Invoke на Control. Эти операции будут продолжать функционировать до тех пор, пока поток пользовательского интерфейса активен против определенного элемента управления.

Тип SynchronizationContext не является специфичным для WinForms, и решения, использующие его, будут переносимы на другие фреймворки, такие как WPF.

Например.

Код BeginInvoke

void OnButtonClicked() {
  DoBackgroundOperation(this); 
}

void DoBackgroundOperation(ISynchronizedInvoke invoke) {
  ThreadPool.QueueUserWorkItem(delegate { 
    ...
    delegate.BeginInovke(new MethodInvoker(this.BackgroundOperationComplete), null);
  });
}

Код СинхронизацииКонтекста

void OnButtonClicked() {
  DoBackgroundOperation(SynchronizationContext.Current);
}

void DoBackgroundOperation(SynchronizationContext context) {
  ThreadPool.QueueUserWorkItem(delegate {
    ...
    context.Post(delegate { this.BackgroundOperationComplete() }, null);
  });
}
...