【问题标题】:Synchronization context not preserved in winforms eventwinforms 事件中未保留同步上下文
【发布时间】:2017-08-30 10:09:04
【问题描述】:

我有以下 winforms 事件

public MainForm()
{
    InitializeComponent();

    new Form().ShowDialog(); // This causes the problem
}

private async void MainForm_Load(object sender, EventArgs e)
{
    LoadingLabel.Text = "Initializing...";

    try
    {
        await Task.Delay(500);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error initializing");
        Environment.Exit(0);
    }

    Console.WriteLine(LoadingLabel.InvokeRequired);
}

预期:程序打印false
结果:程序打印true

据我了解,await 应该将同步上下文设置回原来的状态,并且不需要 Invoke。然而,这种情况并非如此。尝试更新LoadingLabelText 属性会引发InvalidOperationException。我错过了什么吗?

我正在使用 .NET 4.5.2。

【问题讨论】:

  • 代码在我的机器上打印false,当然除非我输入ConfigureAwait(false)。您能否仅使用您发布的代码重现此问题,或者是否有其他任何可能影响结果的内容?
  • 我有全局变量和其他函数+事件,但显示了该函数中的所有代码。上面的确切代码对我来说会出现问题。
  • @JSteward 你是对的。我已编辑问题以包含有问题的代码。
  • ShowDialog 不是构造函数中通常使用的东西,因为它会阻塞直到对话框关闭。如果您可以等到加载MainForm 后再显示您的对话框,则将调用移至MainForm_Load 将不再需要Invoke
  • 我需要在表单出现之前显示我的对话框。我在构造函数中这样做是为了设置一个只读变量。您能解释一下为什么我的代码会导致这种意外行为吗?

标签: c# winforms async-await


【解决方案1】:

在调用ShowDialog(创建嵌套消息循环)后,WindowsFormsSyncronizationContext 将替换为默认的SyncronizationContext,因此您需要Invoke。之后会恢复上下文。延伸阅读How to get a Synchronization Context for the second form shown

你有一些选择:

(1) 构造您的代码,以便在Load 事件或OnLoad override 中调用ShowDialog。我认为这是最好的方法,并且可以长期为您服务。

(2) 但是,您也可以这样做:

public MainForm() {
    InitializeComponent();
    var uiContext = SynchronizationContext.Current;
    new Form().ShowDialog();
    SynchronizationContext.SetSynchronizationContext(uiContext);
}

这只是在对话框关闭时重置SyncronizationContext

【讨论】:

  • Main 中的Application.Run(...) 之前添加.ShowDialog() 也可以。
猜你喜欢
  • 2022-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-26
  • 2014-12-31
  • 1970-01-01
  • 2021-07-23
  • 1970-01-01
相关资源
最近更新 更多