C# · 12月 27, 2021

c# – 将工作人员与UI线程同步

在现有的项目中,我必须使用WinForms(一段时间没有使用它),并且与UI线程同步有一个问题.

我必须与以下工作集成的设计: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();}

这样,以下代码工作:

private DialogResult ShowDialog(Form form){ DialogResult dialogResult = DialogResult.None; Action action = delegate { dialogResult = form.ShowDialog(); }; form.Invoke(action); return dialogResult;}

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

如果我把它改成:

Action action = delegate { dialogResult = form.ShowDialog(ParentWindow); };

其中ParentWindow是IWin32Window的实例,并且WindowStartupLocation设置为CenterParent.调用form.Invoke(action)时,我得到一个跨线程异常.

Cross-thread operation not valid: Control ‘ActivationConfirmationForm’ accessed from a thread other than the thread it was created on.

问题:

>为什么只有在将启动位置设置为CenterParent时,才会出现跨线程异常?我该如何避免呢?
>为什么form.Invokerequired总是假的?

两者都可能相关!

[编辑]
@Reniuz:
你没有在这里丢失任何东西;)
该呼叫正在由核心通知的听众进行

private static void OnActivationConfirmationrequired(DmsPackageConfiguratorCore sender,ConfigurationActivationConfirmationEventArgs args){ args.DoAbort = (ShowDialog(new ActivationConfirmationForm(args.Data)) == DialogResult.No);}

我处理的一切都在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; }}解决方法 看到form.Invokerequired在false是你的问题的核心.你知道它必须是真实的.简单的解释是传递给您的ShowDialog()方法的表单对象是错误的对象.经典的错误是使用new来创建实例,而不是使用窗体对象的现有实例,即用户正在查看并在主线程上创建的实例.确保线程代码具有对该表单对象的引用,以便它可以传递正确的引用.只有使用Application.OpenForms [0],如果你不能正确的话.

一般来说,将线程代码与用户界面分离.工作线程没有显示对话的业务.你可以使它工作,但它在实践中不能很好地工作.弹出对话框,无需用户期待.发生事故可能,用户可能会在弹出对话框之前点击或按下键几分之一秒.关闭对话框,甚至不看它. CreateHandle()hack同样不应该在您的代码中.只需在用户界面准备好之前不要启动线程.由表单的Load事件发出信号.