3

Working on a C# project which I would like to implement a "waiting" (throbber) indicator in a separate form. After much research and trial and error it appears as the suggested method of doing this is to load a form using a separate thread from the one from the current form/thread.

The reason I went with this method was because initially using the Show() method on the throbber form produced a transparent form. I cannot use ShowDialog because I need to run some code after the throbber is displayed, after which that completes I would like to close the throbber form.

Anyway .. after trying many different methods to load the throbber form in a separate thread I still get an error about trying to access it from a thread which is different from the one it was created in. Here is a skelton version of the project code that should shed some light on my issue:

the example I was working off of for multithreading was this popular link for creating your own spashscreen in a separate thread ... http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C

public class Main
{
    public void CheckData()
    {
        try
        {
            ProgressBar pb = new ProgressBar();
            pb.ShowProgressBar();
            //do data checking here
            pb.CloseForm()
        }   
        catch(Exception e)
        {
        }
   }
}

public partial class ProgressBar : Form
{
    static Thread ms_oThread = null;
    public bool shouldStop = false;
    static ProgressBar ms_ProgBar = null;
    public ProgressBar()
    {
        InitializeComponent();
        //DoWork();
    }

    public void ShowForm()
    {
        ms_ProgBar = new ProgressBar();
        Application.Run(ms_ProgBar);
    }
    public void CloseForm()
    {
        ms_ProgBar.Close();
    }

    public void ShowProgressBar()
    {
        // Make sure it is only launched once.
        if (ms_ProgBar != null)
            return;
        ms_oThread = new Thread(new ThreadStart(ShowForm));
        ms_oThread.IsBackground = true;
        ms_oThread.SetApartmentState(ApartmentState.STA);
        ms_oThread.Start();
        while (ms_ProgBar == null || ms_ProgBar.IsHandleCreated == false)
        {
            System.Threading.Thread.Sleep(1000);
        }
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
user981017
  • 33
  • 4
  • possible duplicate of [How to update the GUI from another thread in C#?](http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c) – mbeckish May 08 '14 at 15:59
  • I'm not actually looking to alter or edit any controls on the form after load. Just to close the form when my work is done. Should I be using the Invoke method to access the CloseForm() method? – user981017 May 08 '14 at 16:16
  • 1
    You should not be creating multiple UI thread. That is almost certainly the wrong solution to your problem, and will cause you nothing but pain. Rather than creating a second UI thread so that your main UI thread can be blocked; preventing it from doing anything, just show your progress bar from your main UI thread. – Servy May 08 '14 at 17:17

1 Answers1

0

You are creating your ProgressBar twice. Once in your main function, and once in your new thread. You are also calling your CloseWindow method from your main function (and on the window that is never shown), rather than on your new thread window.

You only want to create ProgressBar and show it using your new thread. Make your static ProgressBar field public so you can call close on it directly from Main, but make sure to use Invoke to do it since it's not on that Window's GUI thread.

Also, ShowProgressBar should be static.

Here's a rewrite attempt:

public class Main
{
    public void CheckData()
    {
        try
        {
            ProgressBar.ShowProgressBar();
            //do data checking here
            ProgressBar.CloseForm();
        }   
        catch(Exception e)
        {
        }
   }

}

public partial class ProgressBar : Form
{
    static ProgressBar _progressBarInstance;

    public ProgressBar()
    {
        InitializeComponent();
        //DoWork();
    }

    static void ShowForm()
    {
        _progressBarInstance = new ProgressBar();
        Application.Run(ms_ProgressBar);
    }

    public static void CloseForm()
    {
         _progressBarInstance.Invoke(new Action(_progressBarInstance.Close));
         _progressBarInstance= null;
    }

    public static void ShowProgressBar()
    {
        // Make sure it is only launched once.
        if (_progressBarInstance != null)
           return;

        var ms_oThread = new Thread(new ThreadStart(ShowForm));
        ms_oThread.IsBackground = true;
        ms_oThread.SetApartmentState(ApartmentState.STA);
        ms_oThread.Start();
    }
}
Nathan A
  • 11,059
  • 4
  • 47
  • 63
  • VS is giving me "Expected class, delegate, enum, interface,or struct" in the progress bar class on the following items: get and set, public ProgressBar(),void ShowForm(), void CloseForm(), and Action ... my apologies for not being able to figure out the issue. More background info, I tried using static methods instead of using an instance variable but got the same issue and agree that it appears to be opening 2x, but unsure how to reference the class and Run it in it's own thread – user981017 May 08 '14 at 16:43
  • Sorry, must be a naming issue. Try renaming the Property to something else. I'll update my example. – Nathan A May 08 '14 at 16:47
  • Getting the following .. An object reference is required for the non-static field, method, or property 'system.Windows.forms.Control.Invoke(System.Delegate)' for the Invoke ... if i change that to just _progressBarInstance.Close() i get the original error about attempting to access something that exists in a different thread – user981017 May 08 '14 at 17:00
  • Sorry, should be `_progressBarInstance.Invoke(new Action(_progressBarInstance.Close));` Updating example. – Nathan A May 08 '14 at 17:02
  • ah success!! thank you. now the hard part. Can you explain what _progressBarInstance.Invoke(new Action(_progressBarInstance.Close)); is doing differently than just .Close() ? – user981017 May 08 '14 at 17:09
  • Had to update that because `CloseForm` is a static method, and `Form.Invoke()` and `Form.Close()` are not. So we have to reference those methods method on the `_progressBarInstance` instance. – Nathan A May 08 '14 at 17:10