tags:

views:

122

answers:

4

I'm running a data import, using a Windows form to kick off the import and show progress. I've got this whole thing so nice and user friendly, with major and minor progress bars and everything... but just one problem... the form refresh keeps going AWOL.

I have a call to Form.Refresh() every time I update my labels/progress bars, and it usually starts off working. But if ever I need to break into debug mode, just to hand-hold the import a bit, the Refresh() call stops working, and sometimes even if I'm running without Debug mode, at some unpredictable point the same thing happens: the labels and progress bars do not get updated, and if you hide the form and reopen it, the form does not repaint at all - it just shows up entirely white.

Why, oh why, does Form.Refresh() stop working, and how can I fix this?

A: 

You may want to change your code into using BeginUpdate and EndUpdate, like so:

Control.BeginUpdate();
// Do something to the control, e.g. add items or whatnot
Control.EndUpdate();

This way Refresh shouldn't be necessary.

AFAIK constantly calling Refresh is really a hack and should be avoiding, as it stresses the CPU quite a bit (it has to refresh everything instead of just the things which are changed).

Edit: If the form starts being white, it seems the drawing code is not been called at all, which indicates it's somewhat not responding.

I'd check the code for anything that can deadlock or otherwisely hang.

Steffen
No such method as BeginUpdate or EndUpdate on a C# control...?
Shaul
I think it's `myControl.SuspendLayout();` and `myControl.ResumeLayout();`
herzmeister der welten
It's not available on all controls no, however it is usually for controls which can have children. E.g. Listview.Could you post the code where you call Refresh ?
Steffen
Just added a comment further down the page, I think I finally realize what's happening :-)
Steffen
A: 

You could use observer pattern..in short if anything changes in model observer pattern will make sure that change is visible on form..

google it for some examples..

shake
Please clarify what you mean. What model is changing? Why do you think an observer pattern is indicated here? It's just a progress indicator showing how far the import has got...
Shaul
I think I finally understand what you're doing: You have some code running in a loop, and you increment the progressbar inside the loop as well.Then you call Refresh to reflect the change to the progressbar to the user, right ?The solution to this is to run the loop in a thread, and just remove Refresh. That way your main thread (which is responsible for drawing) will redraw as required automatically.Remember to use Invoke when changing the Progressbar from your thread.
Steffen
A: 

You might need to give the window time to redraw itself. I understand you're doing the import in a loop and the loop is running on the main UI thread? Try adding this line to the loop:

Application.DoEvents();
Alexander Shirshov
OMG that's even more hackish than calling Refresh all the time, seriously Sleep is almost NEVER a solution.
Steffen
Agree with Steffen: also, if the import is running on the UI thread (which I get the impression that it is) this will still block that thread, preventing the redraw from happen anyway.
Fredrik Mörk
Steffen's comment is correct. It's horribly hackish - and it doesn't work, either. I know, I tried... (blushes)... :)
Shaul
Oh, well, you're all correct, apologies. In my defense I thought the loop is happening on the worker thread. Sometimes even though it's a separate thread the app doesn't have enough time to redraw itself.
Alexander Shirshov
Shaul, I edited my answer.
Alexander Shirshov
-1 Sorry, still every bit as hackish as Thread.Sleep(). Look at the answer I've accepted for the right way to do this.
Shaul
+5  A: 

It sounds as if the import runs on the UI thread, which means that this thread is blocked, preventing the form from repainting itself. A better approach would be to use a BackgroundWorker component, do the import in the DoWork event handler and use the ProgressChanged to update the UI.

Example:

private void StartImport()
{
    backgroundWorker.WorkerReportsProgress = true;
    backgroundWorker.RunWorkerAsync();
}

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // do some work simulating a lenghy process which occasionally
    // reports progress with data back to the caller
    for (int i = 0; i < 100; i++)
    {
        Thread.Sleep(200);
        backgroundWorker.ReportProgress(i, "Item No " + i.ToString());
    }
}

private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    listBox.Items.Add(e.UserState.ToString());
}

Using this approach you will typically not need to call Refresh to force a repaint of the form.

Fredrik Mörk
+1 +answer credit - spot on - thank you!
Shaul