使工作者与UI线程同步

Phi*_*ppe 10 c# multithreading winforms

在现有项目上工作,我必须使用WinForms(暂时没有使用它)并且遇到与UI线程同步的问题.

我必须集成的设计如下:A BackgroundWorker获取Action一个参数并异步执行它.我正在采取的行动有两个部分; 核心类(包含业务逻辑)和GUI部分,如果必须请求用户交互,则由核心通过事件通知.

我已经将句柄创建添加到表单的构造函数中

if (!IsHandleCreated)
{
    //be sure to create the handle in the constructor
    //to allow synchronization with th GUI thread
    //when using Show() or ShowDialog()
    CreateHandle();
}
Run Code Online (Sandbox Code Playgroud)

有了这个,下面的代码工作:

private DialogResult ShowDialog(Form form)
{
    DialogResult dialogResult = DialogResult.None;
    Action action = delegate { dialogResult = form.ShowDialog(); };
    form.Invoke(action);
    return dialogResult;
}
Run Code Online (Sandbox Code Playgroud)

对于此示例,启动位置已设置为Windows默认值.

如果我将其更改为:

Action action = delegate { dialogResult = form.ShowDialog(ParentWindow); };
Run Code Online (Sandbox Code Playgroud)

哪里ParentWindow是实例IWin32WindowWindowStartupLocation设置为CenterParent.调用时遇到跨线程异常form.Invoke(action).

跨线程操作无效:控制从其创建的线程以外的线程访问的"ActivationConfirmationForm".

问题:

  • 为什么仅在将启动位置设置为时存在交叉线程异常CenterParent?我该如何避免呢?
  • form.InvokeRequired总是为什么false

两者都可能有关系!?

[编辑] @Reniuz:你在这里没有遗漏任何东西;)这个电话来自一个被核心通知的听众

private static void OnActivationConfirmationRequired(DmsPackageConfiguratorCore sender,
ConfigurationActivationConfirmationEventArgs args)
{
    args.DoAbort = (ShowDialog(new ActivationConfirmationForm(args.Data)) == DialogResult.No);
}
Run Code Online (Sandbox Code Playgroud)

我可以使用的一切都在GUI界面中

/// <summary>
/// Interface defining methods and properties used to show dialogs while performing package specific operations
/// </summary>
public interface IPackageConfiguratorGui
{
/// <summary>
/// Gets or sets the package configurator core.
/// </summary>
/// <value>The package configurator core.</value>
IPackageConfiguratorCore PackageConfiguratorCore { get; set; }

/// <summary>
/// Gets or sets the parent window.
/// </summary>
/// <value>The parent window.</value>
IWin32Window ParentWindow { get; set; }

/// <summary>
/// Gets the package identifier.
/// </summary>
/// <value>The package identifier.</value>
PackageIdentifier PackageIdentifier { get; }
}
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 4

看到 form.InvokeRequired 为false是问题的核心。你知道这一定是真的。简单的解释是传递给 ShowDialog() 方法的表单对象是错误的对象。典型的错误是使用new来创建实例,而不是使用表单对象的现有实例(用户正在查看并在主线程上创建的实例)。确保线程代码具有对该表单对象的引用,以便它可以传递正确的引用。仅当您无法正确使用时才使用 Application.OpenForms[0]。

一般来说,将线程代码与用户界面分离。工作线程不需要显示对话框。你可以让它发挥作用,但在实践中效果不佳。该对话框在用户没有预料到的情况下弹出。用户可能会在弹出对话框之前单击或按下某个键不到一秒,从而很可能发生意外。甚至没有看到它就关闭该对话框。同样,CreateHandle() 黑客也不应该出现在您的代码中。只是在用户界面准备好之前不要启动线程。由表单的 Load 事件发出信号。