Search code examples
c#task-parallel-libraryasync-ctp

An Async Task issue - Cross-threaded access to forms


The Simplified 7 Steps :

  1. [MainForm] User Clicks btnAdd Button
  2. AddForm will be shown
  3. [AddForm] btnCreate is clicked
  4. within the btnCreate_Click we run AddProductProcess with an awaiter * We will close the AddForm as soon as the click * And show the MainForm
  5. Inside AddProductProcess we run AddProduct with an awaiter
  6. We run our AddProduct which will do the lengthy process for us and fill the Application-Level static collection : ProductCollection
  7. [MainForm] When the Process AddProductis done we will show the Added Product Item in our lstProducts ListBox.

5 Pieces of Code :

private void btnAddProduct_Click(object sender, EventArgs e)
{
    FormAddProduct fap = new FormAddProduct(SelCol);
    fap.ShowDialog();
}

private async void btnCreate_Click(object sender, EventArgs e)
{
    string stProduct = txtProductName.Text;

    await ProductCollection.AddProductProcess(stProduct);
    this.Close();
    MainForm.Show();
}           

public async Task AddProductProcess(string pName)
{
    await Task.Factory.StartNew(() => AddProduct(pName));
    // This would be our heavy process
}   

public void AddProduct(string pName)
{
    ProductItem p =  new ProductItem();
    p.Name = pName ;
    p.Position = Count;
    p.GetInfo(); // and some similar heavy methods are inside this
    //ProductCollection.Add(p);
}

public void Add(Product product)
{
    MainForm.lstProduct.Add(product.Name);
}

"MainForm.lstProduct.Add" cause a invalid cross-thread operation error

I need to add a Task Completion notification on it so can add the result the proper way to the ListBox Could you help me implement it ?

I should pass this line of code to the code that will execute right after the task is finished.

ProductCollection.Add(p);

Any Ideas on this piece of code and the subject are appreciated,


Solution

  • You are using the async/await pattern at some points but not where it matters...

    public async Task AddProductProcess(string pName)
    {
        await Task.Factory.StartNew(() =>
            AddProduct(pName));
    }   
    
    public void AddProduct(string pName)  // not async
    {
        ...
        ProductCollection.Add(p);
    }
    
    
    public void Add(Product product)  // not async
    {
        MainForm.lstProduct.Add(product.Name);
    }
    

    That last method just runs on a Task, so it is a 'normal' cross-threading error. You can resolve it in the normal way, using MainForm.Invoke(...) but then you might as well remove all the async and await keywords.

    To properly use async/await you would have to change it to something like:

    public async Task AddProductProcess(string pName)
    {
        await AddProduct(pName);
    }   
    
    public async void AddProduct(string pName)  
    {
        ProductItem p =  new ProductItem();
        p.Name = pName ;
        p.Position = Count;
        await p.GetInfo();    // assuming this is doing the heavy work, make async
        ProductCollection.Add(p);
    }