开发者

Why is Form.Refresh() not working?

I'm running a data import, using a Windows form to kick off the import and show progress. I've got开发者_Python百科 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?


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.


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.


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..


Depending on what .NET framework you're using, you can use the Task.Run approach:

private void btnShowProgress_Click(object sender, EventArgs e)
{
    progressBar1.Value = 0;

    Task.Run(() =>
    {
        for (int i = 0; i <= 100; i++)
        {
            Thread.Sleep(100);
            progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = i; }));                    
        }
    });
}

Task.Run info

Using invoke with controls


The solution may not be the best practice but it definitely works for small applications.

In Form1 create a bool to check to see if the form is closed.

public bool formclosed = false

Then in Form2 on the Form Closing Event Handler add

formclosed = true

also in the Form2 after

InitializeComponent(); 

add

formclosed = false;

In Form1 create a timer.

In the timer1.Tick event handler say

    private void timer1_Tick(object sender, EventArgs e)
    {
       if(formclosed == true)
        {
            Application.Restart();
        }
    }

This will restart the application and refresh everything ... I also had my text saved to the Properties.Settings.Default so everytime the application started the default settings would show.


I created an initial version of a Progress control using a BackgroundWorker. The Progress control computed and displayed nice things like Estimated Duration, Estimated Time to Completion. Each statistic was displayed by custom control based on a Label control. The Progress control worked in my production code.

Then I made some changes to the Progress control. I switched from basing my custom statistics controls from Label to Panel. I successfully ran all my tests (using NUnit). Then, I created a Test Windows Forms app. The Progress control successfully worked in the Test Windows Forms app.

When I ran my production Windows app with the updated Progress control it didn't display the statistics.

I tried Thread.Sleep(N), Form.Refresh(). None of those worked to update the statistics.

I eventually called Control.Invalidate() on each Statistic control. The Invalidate caused OnPaint to be called and the control happily updated the display with its specific statistic.

For Each Stat_Obj As Control in Statistics_Controls
    Stat_Obj.Invalidate()
Next 


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();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜