Hele
Hele

Reputation: 1578

Synchronization context not preserved in winforms event

I have the following winforms event

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);
}

Expectation: Program prints false.
Result: Program prints true.

It is my understanding that await should set the synchronization context back to the original and no Invoke should be required. However, this is not the case. Attempting to update LoadingLabel's Text property throws an InvalidOperationException. Am I missing something?

I am using .NET 4.5.2.

Upvotes: 3

Views: 573

Answers (1)

JSteward
JSteward

Reputation: 7091

After the call to ShowDialog, which creates a nested message loop the WindowsFormsSyncronizationContext is replaced with the default SyncronizationContext causing you to need an Invoke. The context is then later restored. Further reading How to get a Synchronization Context for the second form shown

You have some options:

(1) Structure your code so that the call to ShowDialog occurs in the Load event or in the OnLoad override. I think this is the best approach and would serve you well long term.

(2) However, you can also do this:

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

This simply resets the SyncronizationContext back when the dialog is closed.

Upvotes: 2

Related Questions