4

I have a WinForm load method that takes a long time to gather some data to display to the user.

I display a form with a large font with the word "Loading" while this method is executing.

However sometimes this error comes up and the "Loading" progress form does not close and then eventually my whole application will just exit:

Error creating window handle. at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)

Is there a better way to display my progress/loading form while I am executing code in the load method?

This is my code:

//I launch a thread here so that way the Progress_form will display to the user
//while the Load method is still executing code.  I can not use .ShowDialog here
//or it will block.

//Progress_form displays the "Loading" form    
Thread t = new Thread(new ThreadStart(Progress_form));  

t.SetApartmentState(System.Threading.ApartmentState.STA);
t.IsBackground = true;
t.Start();

//This is where all the code is that gets the data from the database.  This could
//take upwards of 20+ seconds.

//Now I want to close the form because I am at the end of the Load Method                         

try
{
   //abort the Progress_form thread (close the form)
   t.Abort();
   //t.Interrupt();
}
catch (Exception)
{
}
fraXis
  • 3,141
  • 8
  • 44
  • 53
  • 1
    perhaps some ideas here : http://stackoverflow.com/q/15566176/327083 – J... Apr 02 '13 at 16:28
  • 2
    You should do it the other way. Do the loading job in the background thread and use the main UI thread to display some sort of progress bar. – Vaibhav Desai Apr 02 '13 at 16:29
  • 1
    @VaibhavDesai - sometimes this is not possible in load handlers if the time consuming operations involve loading intensive UI objects. In that case it must be done by the UI thread since it eventually must own those objects. That's why some applications use splash screens, really. – J... Apr 02 '13 at 16:55

3 Answers3

10

A BackgroundWorker is a great way to perform a long running operation without locking the UI thread.

Use the following code to start a BackgroundWorker and display a loading form.

// Configure a BackgroundWorker to perform your long running operation.
BackgroundWorker bg = new BackgroundWorker()
bg.DoWork += new DoWorkEventHandler(bg_DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);

// Start the worker.
bg.RunWorkerAsync();

// Display the loading form.
loadingForm = new loadingForm();
loadingForm.ShowDialog();

This will cause the following method to be executed on a background thread. Note that you cannot manipulate the UI from this thread. Attempting to do so will result in an exception.

private void bg_DoWork(object sender, DoWorkEventArgs e)
{
    // Perform your long running operation here.
    // If you need to pass results on to the next
    // stage you can do so by assigning a value
    // to e.Result.
}

When the long running operation completes, this method will be called on the UI thread. You can now safely update any UI controls. In your example, you would want to close the loading form.

private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Retrieve the result pass from bg_DoWork() if any.
    // Note, you may need to cast it to the desired data type.
    object result = e.Result;

    // Close the loading form.
    loadingForm.Close();

    // Update any other UI controls that may need to be updated.
}
Timothy Schoonover
  • 3,195
  • 4
  • 29
  • 44
3

Ive successfully tested this on .NET 4.0. (WinForms) I'm reasonably certain that this will work on .NET 4.0+ and should be a useful code snippet to reuse in most of your projects that require closing forms at the end of a process.

private void SomeFormObject_Click(object sender, EventArgs e)
{
    myWait = new YourProgressForm();//YourProgressForm is a WinForm Object
    myProcess = new Thread(doStuffOnThread);
    myProcess.Start();
    myWait.ShowDialog(this);
}

private void doStuffOnThread()
{
    try
    {
        //....
        //What ever process you want to do here ....
        //....

        if (myWait.InvokeRequired) {
            myWait.BeginInvoke( (MethodInvoker) delegate() { closeWaitForm(); }  );
        }
        else
        {
            myWait.Close();//Fault tolerance this code should never be executed
        }
    }
    catch(Exception ex) {
        string exc = ex.Message;//Fault tolerance this code should never be executed
    }
}

private void closeWaitForm() {
    myWait.Close();
    MessageBox.Show("Your Process Is Complete");
}
user2288580
  • 2,210
  • 23
  • 16
1

I would take the code that you have in your load method and place that into a thread. Setup a progress bar somewhere on your form and increment it at key stages in the code that's gathering the data - be careful not to do this in the thread itself though, i.e. don't tamper with ui elements in a separate thread, you'll need to invoke them using a delegate.

Crwydryn
  • 840
  • 6
  • 13