Why is Form.Refresh() not working? - c#

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?

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

Related

Cross thread exception from minimized winform app

I have a FileSystemWatcher that is waiting for files to appear in a folder, which then triggers an insert to an ObservableCollection. When the form is open on the screen the update happens successfully. But when the form is minimized I get a cross-thread exception? I know I can just check for context, but I want to know why this behaviour is happening so I can ensure it doesn't come up again in other places.
I've tried this with a new blank winform app, and it errors 100% of the time whether it is minimised or not, so I'm unsure what I'm doing wrong or how to diagnose the issue. The main app is thousands of lines long, with many custom libraries so I can't post all of it here.
Called by the watcher
private void CheckForFiles(object source, FileSystemEventArgs e)
{
WaitingFiles.Add(e.FullPath);
}
Which triggers this
private async void WaitingFiles_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
CurrentProcessStatusLabel.Text = "Checking download for despatch file";
}
When I minimize the window I run the following code;
Hide();
SysTrayIcon.Visible = true;
Unless a SynchronizingObject is set, events are raised on a background thread, and background threads are not allowed to update the UI. I'm not sure why it works unless the application is minimized, but a good start is to ensure you are using correct synchronization to avoid issues like this.
If you want to update the UI you should either set the SynchronizingObject to one of the controls in your UI, or manually schedule any UI updates to the UI thread.

Can I update a control text value if the control is on a tabPage that isn't visible

I have a C# program using WinForms in a Windows Desktop application. It includes a TabControl, and each of 6 tabs has text boxes and so forth. Some of these controls display values that are obtained by the application in background threads.
On occasion, values will be obtained that are to be displayed on controls on a tab that is not visible at that time. When I test it, these values don't show up when I change back to that tab.
My question is: Is there something that prevents Windows.Forms from updating the value on the controls that are not visible at the time of update? They aren't disabled, this happens whether or not they were previously rendered, I'm not coming up with any other reason they wouldn't show up. Can anyone confirm that or tell me I have to look elsewhere? Or even give me another place to look?
I am going to throw up an answer here because of this statement:
Some of these controls display values that are obtained by the application in background threads.
I am going to go out on a limb here and assume you are trying to set the text box's text by using the background thread they were obtained on. If this is the case, you can not modify anything that deals with the UI on any other thread other than the UI thread.
Here is an example of what you do not want to do:
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
textBox1.Text = "Don't do this";
}).Start();
}
That can have undefined behavior, or even crash your application. What you should do is this:
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
Invoke(new Action(() =>
{
textBox1.Text = "Hello!";
}));
}).Start();
}
Controls in WinForms (the main form in this case) have a method named Invoke that will run the delegate on the controls UI thread.
To answer your question:
Is there something that prevents Windows.Forms from updating the value on the controls that are not visible at the time of update?
No. Even if they were disabled, you can still set the controls properties. It doesn't matter if they are hidden either.
If, for whatever reason, this doesn't fix your problem, please just let me know in the comments and I will remove this answer.

GUI not updating c# winforms in the same thread

private void buttonSave_Click(object sender, EventArgs e)
{
textBox1.Text = "DATA is being copied.";
//my own function to cpy files, working properly
copyDirectory(sourceFolderPath, destFolderPath);
}
Copying takes 3 seconds, but i cannot see TextBox with text="DATA is being copied. before it goes into the copyDirectory function", it only updates the text box after finishing copy, what is the Problem? i am not using another thread in copying.
This is because of how Windows Forms handles events. Windows Forms is executing all events synchronously. Which means that when a button is clicked, all code in all attached events are executed, before anything else happens.
This means that until the copying finishes (that is, the method returns), the textbox will not be visibly updated.
There are several fixes for this. One fix is to have the button click start a timer, that executes 100 milliseconds later. And then when the timer executes, perform the copying.
Another (my preferred) would be to execute the copyDirectory method inside a Task:
Task.Factory.StartNew(() => copyDirectory(sourceFolderPath, destFolderPath))
Note: This means that the code runs on a different thread, so if you want to update the textbox to say something like "Completed!" when it's finished, you'll need to do this
Invoke(new Action(() => textbox.Text = "Completed!");

Best Practices on BackgroundWorker

I am making my first ever C# GUI. It is using a BackgroundWorker to run a computationally heavy simulation. Periodically, the simulation reports (through the ProgressChanged event) a sizable body of information that needs to be displayed in the GUI.
The GUI is divided into controls and I want each control to know how to 'update itself'.
In my research, I found lots of ways I might be able to do this.
I could think of was to have the method bougnd to ProgressChanged call an update method on each part of the GUI.
I could Invalidate the GUI and bind each controls update method to the Validating event.
I could bind each control's update method to the ProgressChanged event.
I could have each control implement the INotifyPropertyChanged interface (but I'm skeptical that this would work)
Of all of these options, which one is best practices for updating the entire GUI upon the ProgressChanged event? (or am I out in left field?)
From comment:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Simulation simulation = new Simulation();
while(true)
{
simulation.RunIteration();
backgroundWorker.ReportProgress(-1, simulation);
}
}
You should not have to do anything special.
Your ProgressChanged handler can 'unpack' the data and set the relevant properties of controls. Invalidation and repainting is automatic and it runs on the GUI thread.
You should only be careful with updating 'too often'. How often is too often depends on the volume of data, the handler should be finished well before the next update. If not, build in some throttling mechanism.
ProgressBar is the method I recommend. If the data you want to send to the GUI is text then you could have a rich textbox or just a regular textbox and pass text to it when the progress bar is changed:
public void SomeThread_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
/* Update progress bar */
ProgressBar.Value = (e.ProgressPercentage);
if (e.UserState != null)
{
TextBox.Text += (string)e.UserState;
}
}
For other forms of data you could include an If statement which does X when the progress bar is given a certain value.
But ithout knowing more about what you're specifically trying to do I'm afraid I can't provide any more help than that.

No Load Complete event for windows form

In windows form on click on Next I need to display other form and start some processing. Am coding in .Net C#
The problem is before the form is complete visible, the method gets triggered and the processing starts and the UI looks like it crashed. Processing started even before controls are loaded. and once processing is done all controls are visible.
The actual output must be all controls should be loaded and then processing must start.
I need to call the method to start processing after the form (user control) is visible and is loaded completely.
I tried this and this, but no luck.
Added code:
private void FeatureRemovalControl_Load(object sender, EventArgs e)
{
pictureBox2.Image = Properties.Resources.line;
prgbar.Value = 0;
//code to load images and some other stuff
StratProcess();
}
You're calling StartProcess (which seems to block until it's finished) from your UI thread. Because WinForms repaints occur on that thread, nothing is painted, and it appears that your process has hung. You should look at using a BackgroundWorker, or other way to call StartProcess asynchronously.
Best way, if you ask me, would be to start processing asynchronously, so that you maintain full control of the UI and process at the same time.
http://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.71).aspx
Try calling that method at the end of the FormLoad event, the control should have finished loading by then. If it hasn't that you may need perform some checks and possibly create a custom event that fires when you're happy that it is ready.
Another solution is to have a button that the user must press to trigger the processing, which they will only be able to click once everything has loaded
EDIT: The reason it look's like it's happening is because you're starting the process in one of the control's load method, which I assume is not the last control to load, so it's starts processing before the other controls are given a chance to load. Make StratProcess method public and call it in the FormLoad method of the parent form instead, like so:
private void ParentForm_Load(object sender, EventArgs e)
{
FeatureRemovalControl.StratProcess(); // Should it be called StartProcess instead?
}
Beware though this is still doing the processing on the UI thread, so the screen may appear to 'hang' whilst this is happening so I advise you move it to a background thread as others have suggested.

Categories

Resources