简体   繁体   中英

How to update some GUI control in a WinForm from another thread in C# using async

I'm trying to update the text of some GUI controls from a second thread but Visual Studio shows an exception:

An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code

Additional information:

Invalid operation through sub-processes: access to 'impPROF_combo' took control from a other thread than that in which he created .


My code is:

    private async void checkBox1_CheckedChanged(object sender, EventArgs e)
    {

        sciLoadingCircle1.Visible = true;
        sciLoadingCircle1.Active = true;

        await Task.Run(() => refreshComboColumnsNames());

        sciLoadingCircle1.Visible = false;
        sciLoadingCircle1.Active = false;

    }
    private void refreshComboColumnsNames()
    {
        object[] columnsNames = this.generateComboColumnsNames();
        int impPROF_combo_selected = impPROF_combo.SelectedIndex; //the exceptions  throws from here 
        impPROF_combo.Items.Clear();

        impPROF_combo.Items.AddRange(columnsNames);

        impPROF_combo.SelectedIndex = impPROF_combo_selected;
    }

How I can do this the right way? Thank you.

UI components should only be accessed from their UI thread. In this case, you're putting too much code in the Task.Run :

private async void checkBox1_CheckedChanged(object sender, EventArgs e)
{
  sciLoadingCircle1.Visible = true;
  sciLoadingCircle1.Active = true;

  await refreshComboColumnsNamesAsync();

  sciLoadingCircle1.Visible = false;
  sciLoadingCircle1.Active = false;
}

private async Task refreshComboColumnsNamesAsync()
{
  object[] columnsNames = await Task.Run(() => this.generateComboColumnsNames());
  int impPROF_combo_selected = impPROF_combo.SelectedIndex;
  impPROF_combo.Items.Clear();
  impPROF_combo.Items.AddRange(columnsNames);
  impPROF_combo.SelectedIndex = impPROF_combo_selected;
}

In order to update GUI (or use) elements from another thread you should use Control.BeginInvoke() https://msdn.microsoft.com/ru-ru/library/system.windows.forms.control.begininvoke(v=vs.110).aspx And here is how to use it. How to use BeginInvoke C#

Usual reason why you want to execute some code in a thread other than GUI thread is that the operation takes to much time to finish and blocks GUI thread.

I can see in refreshComboColumnsNames method the only line of code, which can potentially take time - generateComboColumnsNames . Other statements are GUI update operations, which have to be executed in GUI thread. As I understand, generateComboColumnsNames does some heavy calculations or executes db query or sends http request or does any other long operations. Result of this operation needs to be displayed in UI. Correct me, if I am wrong.

I would suggest to make generateComboColumnsNames async operating, which is executed in separate thread. This operation must not update UI - just return data. Result will look like next:

private async void checkBox1_CheckedChanged(object sender, EventArgs e)
{
    sciLoadingCircle1.Visible = true;
    sciLoadingCircle1.Active = true;

    await refreshComboColumnsNames();

    sciLoadingCircle1.Visible = false;
    sciLoadingCircle1.Active = false;

}

private async Task refreshComboColumnsNames()
{
    object[] columnsNames = await this.generateComboColumnsNames();

    int impPROF_combo_selected = impPROF_combo.SelectedIndex;             
    impPROF_combo.Items.Clear();
    impPROF_combo.Items.AddRange(columnsNames);
    impPROF_combo.SelectedIndex = impPROF_combo_selected;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM