6

我正在编写一个 WinForms 应用程序,它有两种模式:控制台或 GUI。同一解决方案中的三个项目,一个用于控制台应用程序,一个用于 UI 表单,第三个用于保存两个界面也将连接的逻辑。控制台应用程序运行绝对流畅。

一个保存用户选择的模型,它有一个IList<T>where T 是一个本地对象,Step它实现INotifyPropertyChanged了 ,所以在 UI 中它被挂载到一个 DataGridView 上。运行时一切正常,对象的初始状态反映在屏幕上。

每个Step对象都是一个任务,依次执行;一些属性会改变,被反射回 IList 并传递给 DataGridView。

UI 版本中的此操作是通过创建 BackgroundWorker 将事件返回给 UI 来完成的。执行此Step操作并生成一个StepResult对象,该对象是一个指示结果(例如 Running、NotRun、OK、NotOK、Caveat)的枚举类型和一个指示消息的字符串(因为该步骤已运行但不完全符合预期,即带有警告)。通常,这些操作将涉及数据库交互,但在调试模式下,我会随机生成一个结果。

如果消息为空,则永远不会有问题,但是如果我生成这样的响应:

StepResult returnvalue = new StepResult(stat, "completed with caveat")

我收到一条错误消息,指出 DataGridView 是从创建它的线程以外的线程访问的。(我通过一个自定义处理程序传递它,该处理程序应该在需要时处理调用 - 也许它没有?)

然后,如果我生成一个独特的响应,例如使用随机数r

StepResult returnvalue = new StepResult(stat, r.ToString());

操作成功,没有问题,数字被干净地写入 DataGridView。

我很困惑。我假设它在某种程度上是一个字符串文字问题,但任何人都可以提出更清晰的解释吗?

4

3 回答 3

4

您已经回答了自己的问题:-

我收到一条错误消息,指出 DataGridView 是从创建它的线程以外的线程访问的。

WinForms 坚持所有对表单和控件执行的操作都是在创建表单的线程的上下文中完成的。这样做的原因很复杂,但与底层的 Win32 API 有很大关系。有关详细信息,请参阅The Old New Thing博客上的各种条目。

您需要做的是使用 InvokeRequired 和 Invoke 方法来确保始终从同一线程访问控件(伪代码):

object Form.SomeFunction (args)
{
  if (InvokeRequired)
  {
    return Invoke (new delegate (Form.Somefunction), args);
  }
  else
  {
    return result_of_some_action;
  }
}
于 2008-10-14T08:10:09.897 回答
3

由于您正在通过事件订阅进行 UI 绑定,您可能会发现这很有帮助;这是我不久前写的一个例子,它展示了如何子类BindingList<T>化,以便将通知自动编组到 UI 线程。

如果没有同步上下文(即控制台模式),那么它会恢复为简单的直接调用,因此没有开销。在 UI 线程中运行时,请注意这实质上使用Control.Invoke,如果它在 UI 线程上,它本身只是直接运行委托。因此,如果数据是从非 UI 线程编辑的,则只有任何开关 - 正是我们想要的;-p

于 2008-10-14T09:28:09.550 回答
0

我发现这篇文章 - “从不同的线程更新 IBindingList ” - 将责任归咎于 BindingList -

由于没有为异步操作设置 BindingList,因此您必须从控制它的同一线程更新 BindingList。

显式地将父表单作为对象传递并为该技巧ISynchronizeInvoke创建一个包装器。BindingList<T>

于 2008-10-14T10:48:04.913 回答