tags:

views:

212

answers:

4

I have a grid and when a row is double clicked a form is loaded. However a lot of data must be loaded, so I'd like to display a simple form with the text 'loading, please wait..'. And when all loading is finished, the form must disappear.

This is what I have right now, but it doesn't work:

Code that invokes the form with lots of data:

FormWithLotData form = new FormWithLotData();
form.ShowDialog(this);

Constructor of FormWithLotData:

// Show load form
FormIsLoading frm = new FormIsLoading();
_CloseLoadForm closeForm = new _CloseLoadForm(frm.Close);
System.Threading.Thread thread = new System.Threading.Thread(frm.Show);

thread.Start();

InitializeComponent();

this.Visible = false;

LoadAllData();

this.Visible = true;

// Close load form
Invoke(closeForm);

Hope you can help me out.

EDIT: I'd like to show an animated gif on the loading form.

SOLUTION: I've created a background worker. The DoWork event handles all the loading and by using the invoke() method I add nodes to the treeview. Now, the GUI doesn't hang and the user don't have the idea that the application is hanging.

+1  A: 

In the Form_Load event, add the following:

this.Visible = true;
Application.DoEvents();

before any other processing occurs. The Application.DoEvents caused the UI to show the form at the current state, where normally the UI thread is locked while you other processing is taking place.

David Stratton
I've created a load event and put that piece of code into the event. Now I get this exception: `Invoke or BeginInvoke cannot be called on a control until the window handle has been created.`
Martijn
+3  A: 

how about ...

FormIsLoading frm = new FormIsLoading();
frm.Show();
Application.DoEvents();

// ... load data ...

frm.Close();
Hinek
Thnx this works, but... My gif is standing still. A solution for this problem?
Martijn
@Martijn DoEvents (I believe) only pumps the message queue once, so you'll have to call it at every opportunity within LoadAllData.
Will
+4  A: 

You need to reverse your code.

The constructor of FormWithLotData is running in the UI thread. This is the thread that must show your FormIsLoading form. So instead of trying to display this form using the new Thread, do your data loading with it.

The DoEvents way others have suggested is the easiest to implement and (possibly? never done it myself) may work well.

The better pattern to use is to do your data loading on a worker thread. Before you show your FormWithLotData, Begin loading data on a background thread and show your Loading dialog. The method that loads the data should have a callback method into the Loading dialog to signal when it should Close(). Once it closes you can then construct a new FWLD, pass it the already loaded data, and Show it.

Trying to load your data after the form has already been invoked mixes your UI with your data operations, forcing your form to not only be in charge of the UI but also be in charge of data retrieval. Bad for KISS and Single Responsibility, imho.


After your update, it seems like DoEvents is going to be the only real answer to your question, but with some caveats.

You will not be able to show another form MODALLY while you construct your tree. You will still have to do your heavy work within your form's constructor. You will still have to hide your main form and Show() (not ShowDialog) your loading form. You will also have to call DoEvents at every single possible moment while constructing your tree. Its not exactly an elegant solution, but it will probably be your best bet at this point.

Will
+1. Better idea than mine, and should address the GIF not being animated... It's more complicated to do, but not by much, and I think worth the effort.
David Stratton
The heavy loading in my case is building a large treeview. If I put this method in a thread then it can't access the treeview because the treeview is in another thread.
Martijn
@Martijn: You can invoke the event of that object on that thread. That is what you'll need to do if you want to update the GUI object.
galford13x
@Martijn that's unfortunate. In WPF it would be relatively easy. You can possibly still do this. What you would have to do is construct a real `Thread` (i.e., not use ThreadPool) and set its apartment state to STA. Then construct it in your worker thread, passing it back out when done. You would then have to add this preconstructed tree to the UI, hoping that it won't explode because of thread affinity issues. This seems like its getting much more complex than you need in your app.
Will
@Martijn updated answer. Add a call to DoEvents in every other line of your LoadAllData method and see if it works out for you.
Will
A: 

Don't do your LoadAllData() directly in a UI thread, instead start up a background thread to do it. The in your Form_Loaded event handler, use an AutoResetEvent and wait till it becomes signalled by the background data retrieving thread. Once it is signalled you can then continue to do whatever you need to do with the data, like bind it into the UI.

This method is still a little clunky for various reasons, but it will get you started.

Edit: i was being lazy with my answer above... a better option is to pass a delegate (callback) to the background thread, when the delegate is invoked (upon completion of the data retrieval) it marshals itself back on to the UI thread, and starts doing the required work with the data.

slugster
Actually, I was going to suggest that. But... when you block the UI thread waiting on an ARE, you're *blocking the ui thread*. So it makes the whole background worker thing pointless. What you need is a callback that the LoadAllData method can invoke to signal the UI thread that the data has been loaded.
Will
Note my comment that it is "clunky". It still works, as the background thread is not blocked, and it is the one that signals the ARE, which means the UI thread can then continue. In any case, i have made an edit with a better suggestion.
slugster
I don't really get this. Can you give a small code example?
Martijn
If his background thread works for ten minutes, your UI thread is blocked for ten minutes. Users will think the application is locked up, which is what @Mart is trying to avoid. Your update is correct.
Will