是否有一种在句柄销毁前/后仍可正常工作的Control.BeginInvoke变体?

4
我有一个控件,用于显示底层异步对象的状态。该对象会触发事件,在表单中排队并最终使用BeginInvoke调用。
当控件被释放时,问题就出现了。由于事情是异步发生的,意味着在释放期间可能会排队事件回调,因此有时会收到InvalidOperationException(无法在控件的窗口句柄创建之前调用Invoke或BeginInvoke)。
这不是我想要的行为。即使控件已被释放,我也希望回调能够执行(即使这会在回调函数中引发异常;对我来说,这是一个更有用的异常!)。我想在每个回调中处理已释放状态的行为(通常只是跳过,但有时不会 [例如,一个控件记录事件(可选地记录到文件中),我不想丢失日志数据!])。
是否有一种方法可以按照我想要的方式工作?我能否自己编写一个不易损坏的方法?
1个回答

6
请尝试使用SynchronizationContext.Current代替。它具有PostSend成员,这些成员与Control上的BeginInvokeInvoke大致相对应。只要UI线程存活,这些操作就会继续运行,而不是特定控件。 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);
  });
}

你能给一个使用示例吗?它是控件的成员还是全局的东西? - Craig Gidney
谢谢,这非常有用。它可能在许多其他场景中也有效。 - Craig Gidney
我有点不清楚如何获取控件的同步上下文,如果句柄可能不存在。 - Craig Gidney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接