beeker
beeker

Reputation: 810

Form Unresponsive While Async Tasks are Completing

I have a windows form that takes some time to load the values from the database into some comboboxes (3 comboboxes). In an attempt to get the form up, I added some async code to await the loading of each combobox. The goal is to load the form and assign the datasource/re-enable the comboboxes as the data comes through without blocking the form while the combobox datasources are being assigned.

Even with the async calls, the form is blocked until all the datasources for the comboboxes have been loaded.

Any advice to point me in the right direction will be very much appreciated.

private void FrmOrderSearch_Load(object sender, EventArgs e)
{
    //set everything disabled to let the user know we're working on getting the long running data
    cbLanguages.DataSource = null;
    cbLanguages.Items.Clear();
    cbLanguages.Items.Add("Retrieving...");
    cbLanguages.SelectedIndex = 0;
    cbLanguages.Enabled = false;

    cbInterpreter.DataSource = null;
    cbInterpreter.Items.Clear();
    cbInterpreter.Items.Add("Retrieving...");
    cbInterpreter.SelectedIndex = 0;
    cbInterpreter.Enabled = false;

    cbCustomer.DataSource = null;
    cbCustomer.Items.Clear();
    cbCustomer.Items.Add("Retrieving...");
    cbCustomer.SelectedIndex = 0;
    cbCustomer.Enabled = false;

    Cursor = Cursors.Default;
}

//added so that form renders before async stuff starts
private async void FrmOrderSearch_Shown(object sender, EventArgs e)
{
    Application.DoEvents(); //finish everything up before doing async loads

    await getCustomersAsync();
    await getInterpretersAsync();
    await getLanguagesAsync();
}

private async Task getLanguagesAsync()
{
    cbLanguages.DataSource = await Task.Run(() => MemoryObjects.LanguagesList);

    cbLanguages.ValueMember = "Code";
    cbLanguages.DisplayMember = "Language";
    cbLanguages.SelectedIndex = 0;
    cbLanguages.Enabled = true;
}

private async Task getInterpretersAsync()
{
    cbInterpreter.DataSource = await Task.Run(() => MemoryObjects.InterpreterNamesFacadeList);

    cbInterpreter.ValueMember = "IntNo";
    cbInterpreter.DisplayMember = "FullName";
    cbInterpreter.Enabled = true;
}

private async Task getCustomersAsync()
{
    cbCustomer.DataSource = await Task.Run(() => MemoryObjects.CustomerNamesFacadeList);

    cbCustomer.ValueMember = "CustNo";
    cbCustomer.DisplayMember = "Name";
    cbCustomer.Enabled = true;
}

Upvotes: 0

Views: 149

Answers (1)

Poul Bak
Poul Bak

Reputation: 10929

I think the (visual) problem is that that the Shown event of the form is called before it fully finished painting.

One trick I use in such cases is to use the Application.Idle event to call those background jobs instead of using the Shown event.

Subscribe to the Idle event in form Load`, like this:

private void FrmOrderSearch_Load(object sender, EventArgs e)
{
    // ........other code
    Application.Idle += Application_Idle;
}

Now the Idle event handler:

private void Application_Idle(object sender, EventArgs e)
{
    Application.Idle -= Application_Idle; // Should only be called once.
    // Add all your background jobs (from the `Shown` event)    
}

Now the Application has finished painting before the background jobs are called.

However, this does not fix the real problem, why those background jobs are so slow. We can't help you with that with the current info.

Upvotes: 1

Related Questions