Search code examples
c#user-interfacebackgroundworker

BackgroundWorker ShowDialog causes app to stop


I have been trying to implement a BackgroundWorker into my application, and so far, it has not gone well. On a new thread, I want to open up a new Form that will have a progressbar and a label to report progress, however, this is not working well. When I call ShowDialog, the application does not respond any more. Is this because my code is running from my Form1, and I am showing WorkingForm? Also, can this be implemented cleaner?

private void button14_Click(object sender, EventArgs e)
{
    List<object> param = new List<object>();
    object[] objectparams = new object[1];
    objectparams[0] = null;
    Opera opera = new Opera();
    System.Reflection.MethodInfo clearOpera = opera.GetType().GetMethod("ClearOpera");
    param.Add(clearOpera);
    param.Add(opera);
    param.Add(objectparams);
    backgroundWorker1.RunWorkerAsync(param);
}

private void button2_Click_1(object sender, EventArgs e)
{
    Browser.cancelPending = true;
}
private delegate void getnewform();

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    mainForm main = new mainForm();
    TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
    var variab = (bool)form.Invoke(new getnewform(main.AskForConfirmation));
        List<object> param = e.Argument as List<object>;

        List<object> result = new List<object>();
        var method = param[0] as MethodInfo;
        object[] parameters = param[2] as object[];
        if (parameters[0] == null)
        {
            result.Add(method.Invoke(param[1], null));
            result.Add(false);
        }
        else
        {
            result.Add(method.Invoke(param[1], parameters));
            if (parameters.Contains(true))
                result.Add(true);
        }
        int progress = (100 * Browser.progressValue) / Browser.progressMax;

        backgroundWorker1.ReportProgress(progress);

        // If the BackgroundWorker.CancellationPending property is true, cancel
        if (backgroundWorker1.CancellationPending)
        {
            Console.WriteLine("Cancelled");
            Browser.cancelPending = true;
        }
        e.Result = result;
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
        TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
        form.progressBar1.Value = e.ProgressPercentage;

        form.label1.Text = Browser.progressValue + "/" + Browser.progressMax;
        Application.DoEvents();
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    List<object> param = e.Result as List<object>;
    if (e.Cancelled == false && param.Contains(true))
    {
        Display.DisplayURLs(param[0] as SortableBindingList<URL>);
        TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
        MessageBox.Show("Done");

    }
    else if (e.Cancelled == false && param.Contains(false))
    {
        TestURLGUI4.WorkingForm form = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
        MessageBox.Show("Done");
    }


}

    public class mainForm
{
public void AskForConfirmation()
{
    TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
    var workingForm = new TestURLGUI4.WorkingForm();
    workingForm.ShowDialog(form);
    workingForm.DialogResult = DialogResult.None;

}
}

Edit: Ok, I have updated my code according to the suggestions, and now, this produces a stackoverflowexception in System.Windows.Forms.dll:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    mainForm main = new mainForm();
    TestURLGUI4.Form1 form = (TestURLGUI4.Form1)Application.OpenForms[0];
        List<object> param = e.Argument as List<object>;
        List<object> result = new List<object>();
        var method = param[0] as MethodInfo;
        object[] parameters = param[2] as object[];
        if (parameters[0] == null)
        {
            result.Add(method.Invoke(param[1], null));
            result.Add(false);
        }
        else
        {
            result.Add(method.Invoke(param[1], parameters));
            if (parameters.Contains(true))
                result.Add(true);
        }
        int progress = (100 * Browser.progressValue) / Browser.progressMax;

        backgroundWorker1.ReportProgress(progress);

        // If the BackgroundWorker.CancellationPending property is true, cancel
        if (backgroundWorker1.CancellationPending)
        {
            Console.WriteLine("Cancelled");
            Browser.cancelPending = true;
        }
        e.Result = result;


}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    TestURLGUI4.Form1 form1 = (TestURLGUI4.Form1)Application.OpenForms[0];
    if (Application.OpenForms.Count >= 2)
    {
        TestURLGUI4.WorkingForm form2 = (TestURLGUI4.WorkingForm)Application.OpenForms[1];
        form2.progressBar1.Value = e.ProgressPercentage;

        form2.label1.Text = Browser.progressValue + "/" + Browser.progressMax;
        Application.DoEvents();
    }
    else if(Application.OpenForms.Count == 1)
    {
        var workingForm = new TestURLGUI4.WorkingForm();
        workingForm.ShowDialog(form1);
    }
}

Solution

  • The purpose of a BackgroundWorker is to invoke code on another thread (not the UI thread). By calling Invoke in the DoWork method, you're completely circumventing the purpose of BackgroundWorker. Do all your UI work before you start the worker. If you need to interact with the user while the worker is working, do it in the ProgressChanged handler--it runs on the UI thread and you don't need to use Invoke in ProgressChanged.

    By invoking UI work in DoWork, you run the risk of a deadlock, which will hang your program