Click here to Skip to main content
15,918,808 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am using a BackgroundWorker to process data and update my UI with the ProgressChanged event. However, I get a System.InvalidOperationException when I try to change my ToolStripProgressBar.Visible to true, but not sure why. Here's my code for the ProgressChanged event:
C#
toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Value = e.ProgressPercentage;
if (e.UserState != null) toolStripStatusLabel1.Text = e.UserState.ToString();
If I comment out the first line, I don't get the Exception, like so:
C#
//toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Value = e.ProgressPercentage;
if (e.UserState != null) toolStripStatusLabel1.Text = e.UserState.ToString();

Also, the details of the Exception says:
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
All the questions that I've searched for seem to have the name of the control, not just empty quote marks, but I'm having a hard time searching for that specifically. I'm not sure why I can access the Value member but not the Visible member of the ToolStripProgressBar.

UPDATE:
I tested a small sample project of what I was trying to do and get the same results. Again, if I comment out the Visible statement, I don't get any exceptions.
C#
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void btnDoWork_Click(object sender, EventArgs e)
    {
        if (bgw.IsBusy == false) bgw.RunWorkerAsync();
    }

    private void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        ImportData dataImporter = new ImportData();
        dataImporter.ProgChanged += bgw_ProgressChanged;
        dataImporter.Import();
    }

    private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        
        toolStripProgressBar1.Visible = true; // Comment line out prevents Exception
        toolStripProgressBar1.Value = e.ProgressPercentage;
        if (e.UserState != null)
            toolStripStatusLabel.Text = e.UserState.ToString();
    }

    private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        toolStripProgressBar1.Visible = false;
        toolStripStatusLabel.Text = "Finished";
    }        
}

public class ImportData
{
    public ProgressChangedEventHandler ProgChanged;
    private void UpdateProgress(int percent, string state)
    {
        if (ProgChanged != null) ProgChanged(this, new ProgressChangedEventArgs(percent, state));
    }
        
    public void Import()
    {            
        UpdateProgress(100, "Work Completed");
    }
}
Posted
Updated 23-Nov-15 4:10am
v3
Comments
BillWoodruff 22-Nov-15 18:53pm    
We do need to see the code for the BackgroundWorker: are you using the RunWorkerCompleted Event ? Have you looked at RunWorkerAsynch ?

The problem is that your DataImporter class is firing its ProgressChanged event from a background thread. As a result, the code in the bgw_ProgressChanged handler cannot access or modify the UI.

You could change the handler to invoke the code on the UI thread:
C#
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (InvokeRequired)
    {
        Invoke((ProgressChangedEventHandler)bgw_ProgressChanged, sender, e);
        return;
    }
    
    ...
}

Or you could add an extra layer of indirection:
C#
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
    ImportData dataImporter = new ImportData();
    dataImporter.ProgChanged += imp_ProgressChanged;
    dataImporter.Import();
}

private void imp_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    bgw.ReportProgress(e.ProgressPercentage, e.UserState);
}

private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // Hooked up to the BackgroundWorker's ProgressChanged event.
    ...
}

Or, you could modify your ImportData class to raise the event on the UI thread:
C#
public class ImportData
{
    private readonly SendOrPostCallback _progressReporter;
    private AsyncOperation _asyncOperation;
    
    public ImportData()
    {
        _progressReporter = arg => OnProgChanged((ProgressChangedEventArgs)arg);
    }
    
    public ProgressChangedEventHandler ProgChanged;
    
    protected virtual void OnProgChanged(ProgressChangedEventArgs e)
    {
        ProgressChangedEventHandler handler = ProgChanged;
        if (handler != null) handler(this, e);
    }
    
    private void UpdateProgress(int percent, string state)
    {
        ProgressChangedEventArgs arg = new ProgressChangedEventArgs(percent, state);
        if (_asyncOperation != null)
        {
            _asyncOperation.Post(_progressReporter, arg);
        }
        else
        {
            OnProgChanged(arg);
        }
    }
        
    public void Import()
    {
        _asyncOperation = AsyncOperationManager.CreateOperation(null);
        UpdateProgress(100, "Work Completed");
    }
}
 
Share this answer
 
Comments
hpjchobbes 23-Nov-15 11:40am    
Thanks! I tested with the first option using the InvokeRequired and it fixed the problem. Does creating an instance of an object (ImportData) and calling it's functions cause the function calls to run on a separate thread than the bgw_DoWork function? I'm still not clear on is why only the ToolStripProgressBar.Visible member caused the issue and not the ToolStripProgressBar.Value or ToolStripStatusLabel.Text member.
Richard Deeming 23-Nov-15 11:52am    
The ImportData code is running on the same thread as the BackgroundWorker's DoWork event. It's the BackgroundWorker that causes the code to execute on a background thread.

I'm not sure why accessing the Value and Text members from the background thread didn't throw an exception. Some members work from background threads, but it's not entirely obvious which ones.
We need to see more of the background worker code and the code subscribed to any of its events.
 
Share this answer
 
Comments
BillWoodruff 22-Nov-15 18:37pm    
This is a comment/request to the user, not a solution ! If CP MVP's don't set an example, why should we expect the general quality of QA here to improve from its current poor state ?
Dave Kreskowiak 22-Nov-15 19:52pm    
Whoops. I'm playing a mulligan.
It will be great if you can share the DoWork event on your background worker!

Please take a look the MSDN referene on Making Thread-Safe Calls by using BackgroundWorker[^]
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900