GUI not updating c# winforms in the same thread - c#

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!");

Related

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.

Class Method not running in another thread despite background worker?

On a button press I call a background worker to change the colour of some text, and in the DoWork method it creates a new object and executes one of its methods. Here is the code:
private void StartProcessButton_Click(object sender, EventArgs e)
{
FirstTimeBackgroundWorker.RunWorkerAsync(GenKeyLabel.ForeColor = Color.DodgerBlue);
}
And the DoWork method...
private void FirstTimeBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
KeyFile k = new KeyFile();
k.CreateDummyFile(DLetter);
}
[The CreateDummyFile effectively does a bunch of file processing, such as copying,deleting and creating files (up to 4.0MB). Throughout I call the ReportProgress method and change some GUI elements on the form, however as it does it in one chunk I cant see the constant progress or the GUI elements change]
Now it does what its supposed to inside the CreateDummyFile method, however it executes it as if it wasn't in another thread (like when you press a button to do something and the form would freeze, and then just show the final result). Any ideas why this is? What I am doing wrong?
There is a lot of code in that class that gets executed so I can't just place it inside the DoWork method.
EDIT: Removed all of the ReportProgress's and it still only changes one of my GUI elements. (All I do is change the font.forecolour. Apart from this I am changing the text on a status strip...
You need to add the FirstTimeBackgroundWorker_DoWork as a handler of DoWork event on BackgroundWorker:
FirstTimeBackgroundWorker.DoWork += FirstTimeBackgroundWorker_DoWork;
Also make sure if you're calling back the UI to update it to do so on the UI thread, not from the background one.

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.

I can only close a form once, InvalidOperation Exception Invoke or BeginInvoke cannot be called on a control until the window handle has been created

Hi
I'm opening a form like this from my main form when the user makes a selection of a menu item.
private void commToolStripMenuItem_Click(object sender, EventArgs e)
{
Command_Form Command_Form1 = new Command_Form();
Command_Form1.ShowDialog();
// Command_Form1.Dispose(); this didn't help
}
Inside the form "Command_Form1"
I close it like this when the user clicks on the close button
private void Close_button_Click(object sender, EventArgs e)
{
this.Close(); //I get the exception here
}
This process works fine once but on the second closing of the form
(Which I hope is a completely different/new instance of the form) I get the error in the title of this post.
This is the output in the debug window.
A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
All the topics that list this error go on about not trying to do anything to a form that has not been displayed but this happens when I click on a button in the form.
It would seem to me that pretty much ensures the form has been displayed if I'm able to click its button.
The other posts I've found that list this type of error go on about making thread safe calls so I tried this as an experiment but it didn't make any difference.
private void Close_button_Click(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
CloseCallback d = new CloseCallback(Close_button_Click);
Invoke(d, new object[] { sender, e });
}
else
{
this.Close();
I have multiple threads in my application but they are created by the controls I'm using not by me explicitly.
I am passing data from a serial port to/from the form by Marshling the received/sent data via a delegate[s].
It makes sense that the serial port would run on a different thread than the form but why would a button click on a form be in a diffrent thread than the form????
The whole thread thing is very confuzing
How do I figure out what threads originated where and what is going on in the threads that I didn't create explicitly?
Why would I need to invoke the form's close method via a delegate?
Heck is there anything I can do in this multi threading environment that is thread safe How in do I know if what I'm doing is unsafe/safe if I don't know what/where/why/who/when is creating threads?
My guess is your close() call is not throwing that exception, but something that happens after close(). Have you stepped into the code with the debugger to see when it is fired?
As to when you need to invoke...There is only one thread allowed to make changes and access dynamic properties on the GUI, call it the GUI thread. The GUI thread is responsible for updating layout, firing events like buttons, etc. If you ever access the GUI from another thread (like a timer event) you need to use invoke() to queue your function to be run on the GUI thread. BeginInvoke will also queue the function but is asynchronous (will only queue the function to be run on GUI thread, but will not wait for it to finish).
Close_button_click will only be called by your gui thread when the button click event fires(unless you explicitly call it somewhere else in your code behind, not recommended!), so invokeRequired=false in your code above, and the invoke code is never executed.

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

Categories

Resources