Unreliable GUI update in multithreaded C# program - c#

I have a program that runs a series of methods in other threads within one window and let's the user know what's going on using a status bar. The status bar updates are in the main thread which set's the status bar and then refreshes the GUI. There are several blocks of code in series each looking something like this:
Thread do1Thread = new Thread(Class.Method);
do1Thread.Start();
// inform user
this.status.Text = "Doing stuff 1...";
// update GUI
Utility.RefreshGUI();
// join thread
do1Thread.Join();
Sometimes the status bar does indeed update but often is stays on the first status until the end when it displays the last status. Occasionally is sticks on "Ready." which is the default.
Note that two of the blocks take a few seconds so there should be time for it to update. Also, the program is written in C# (Mono) using GTK# for the GUI.
How can I ensure that that the GUI updates to reflect the change?

The problem is that the Join() call blocks the UI thread which blocks all window messages.
Can you use a BackgroundWorker and execute whatever code you have after the Join in the RunWorkerCompleted call?

You need to dispatch Update message to UI thread, call invoke instead of direct property
this.status.BeginInvoke((Action)(() => this.status.Text = "Something happen"));

The best way I have found to update a control in a primary thread is to set a delegate for updating and invoke that from other threads.

You have to use observe and observable pattern.
EDITED:
It is really better divide logic and view parts of code.
Here is an example in real world how to use. Pattern

Could you check whether you are using a StatusStrip control?
If so, your code looks like setting directly the Text of Stautus Strip Control
this.status.Text = "Doing stuff 1...";
So it wont reflect in the Status Strip as Text. You have to place a toolstriplabel and need to set its text in this case.
Please check the post here

Related

Really impossible to run animated GIF in async threads?

I want to run long lasting methods, even containing time consuming dlls, while a Spinner GIF is rotating.
OK, the most accepted solution is to run these methods in a BackgroundWorker while the GIF is shown in the main thread and I have already done, successfully, it but… but I am still curious to know if it is really impossible to do do the other way round !
This would simplify the calls to those methods, especially if they have arguments and return values, avoid the use of Invoke if they contain Labels to show the working progress, etc…
I have spent quite a lot of time browsing the Web but all the suggested solutions don’t work for me: as soon as the program calls my methods the Spinner stops rotating and resume working only when the methods end.
Ciao and thanks for any suggestion.
DONE!!
Thanks to the Camilo Terevinto sentence “You cannot do UI work on a non-UI thread” I asked myself if it was not possible to create the PictureBox that holds my spinning GIF in another thread and…
I created a new borderless Form (named frmSpinner) with inside a PictureBox running the Spinner.gif.
Then I used, in the main Form with the long running methods, a BackgroundWorker that, in the DoWork event, has a frmSpinner.Show().
Now the Spinner rotates endless without interrupting and resuming.
I have still to solve the problem how to place the Spinner Form on the right position on the main Form but, with the help of the PointToScreen method, it shouldn’t be to difficult.
Ciao and thanks for the suggestion.
Dealing explicitly with threads in a WPF/c# project is not canonical, unless you have something very specific to do, using Tasks is the modern way.
You can have a background task, started with for instance:
Task.Run(() =>
{
// blocking methods running outside UI thread
var newPropValue = //... update a UI property
Application.Current.Dispatcher.Invoke(() =>
{
//Running in UI thread.
PropValue = newPropValue;
});
});
With this pattern you can remove all the heavy/blocking work from the UI thread and have a flowing UI experience.

Properly use Background Worker in a WPF application

i'm stuck in using properly the background worker, the app needs to communicate with the database, so it takes a bit longer and the UI freezes for a while, i need to create a function that does the database things and wait until the work is finished, for this time, i want to display a kind of window that inform the user about the app state (Loading, busy, Downloading).
My code
i didn't write any code yet, but here're what i need:
//instructions
InitializeComponent();
//do this in background and wait until it finnishes
GetEntitiesFromDatabase();
entitiesListView.ItemSource = someList; (GetEntitiesFromDatabase will initialize this list)
//....
How can i proceed, i know that this question might be already asked in the forum but i'm desperate by searching for an answer, if this can be done other way please help me, thanks in advance.
If you are going to directly manipulate the UI like that, you need to use Dispatcher.BeginInvoke when you aren't on the UI thread (read, in the BackgroundWorker).
Dispatcher.BeginInvoke(new Action(() =>
{
entitiesListView.ItemSource = someList;
}), null);
You could also bind that items source to your view model (a much better idea) and the framework will marshal the change to the bound property for you.
Basically, either use Dispatcher.BeginInvoke or switch to MVVM (which WPF was meant to use anyways).

ToolStripStatusLabel not behaving like other typelike controls

Im having some trouble with a ToolStripStatusLabel in Winforms application. To better explain i have some code here
bottomLbl.Text = "Adding file(s) to list...";
this.Text = "Adding file(s) to list...";
listAllFiles(carrier, type, chkListBox, withDestSystem, listBox, cmbBox);
bottomLbl.Text = "Done!";
this.Text = "Done";
What i dont get is, that this.Text does change to "Adding files.." but not bottomLbl even though i set it to do so before this.text. Both controls get the "Done!" text after the listAllFiles function has been run.
But is there something special i have to do on a ToolStripStatusLabel ?
You need to refresh the form before calling the function
bottomLbl.Text = "Adding file(s) to list...";
this.Text = "Adding file(s) to list...";
this.Refresh();
listAllFiles(carrier, type, chkListBox, withDestSystem, listBox, cmbBox);
bottomLbl.Text = "Done!";
this.Text = "Done";
It looks to me like you're setting up a condition where we can't reliably predict which of the UI updates will be displayed to the screen before the final UI updates get displayed.
You're doing some UI updates (e.g. label1.Text = ...) before performing a time-consuming process (listAllFiles()). After that completes, you're doing some more (final) UI updates.
One or more of the "early" UI updates gets queued to be displayed as soon as the main thread (the UI thread) has some time to refresh the window.
What we don't necessarily know is which types of controls (ToolStripStatusLabel versus Label, for instance) internally consider the setting of their Text property to be something that can't wait to be displayed.
It sort of sounds like one of those types will allow its Text property to be updated without waiting until it gets displayed (that's a non-blocking call) while the other type will ensure that the change gets displayed before proceeding (that's a blocking call).
Even if you determine what their behavior is through testing, it could change in the future or in different environments.
Optionally, you could customize one or more of those types to specify whether to block or not. You could do this by either calling Refresh() or Invalidate(). But I recommend an alternative approach:
Keep the load of tasks performed in the UI thread to a minimum. This way, the UI is more responsive to the user and you can have more confidence that your UI updates will be displayed quickly.
Generally, this means we should perform all time-consuming work in a different thread, asynchronously, and we can update the UI with the status of that work as frequently as we like. To accomplish this, I recommend looking into the BackgroundWorker type. Its purpose is to perform time-consuming work asynchronously while maintaining a responsive UI.

How to clear the text inside a TextBox when it is being passed down in a function?

Here is what my code looks like:
private void exportToExcelButton_Click(object sender, EventArgs e)
{
txtBox.Clear();
txtBox.AppendText("Beginning Export...");
ExportExcel(txtBox);
txtBox.AppendText("Export complete...");
}
The problem I am having is that whenever the button is clicked (to execute the function above), only part of the current text in the TextBox (System.Windows.Forms.TextBox) is cleared, and replaced with the first line: "Beginning Export ...".
However once the function ExportExcel(txtBox) is done executing, then the entire text is replaced by the new one generated in ExportExcel(txtBox).
Inside ExportExcel(txtBox); I have several txtBox.AppendText() statements explaining to the user the actions being made.
I have tried clearing the text with txtBox.Text = String.Empty; and txtBox.Text = "";and neither have worked.
Let me know if anything needs to be clarified, thanks.
Looks like you're blocking the GUI thread, preventing the text box from redrawing itself. This is one reason why you shouldn't perform long-running tasks on the GUI thread. Use a background thread instead. That way you leave the GUI thread free to perform important operations like drawing, responding to mouse clicks, etc.
Have you tried the textBox.Refresh , before calling txtBox.AppendText("Beginning Export...").
The method invalidates the control.
On the other hand, if you use a background thread, then you should update the UI only by overriding the Progress Changed event. Background threads are not meant for updating user interfaces. Try searching for Worker threads and UI threads. They correlate to MFC, but the concept is the same.
Also keep in mind the cross thread calls.
I agree with dvnrrs. However if you are unable to do this, try calling txtBox.Refresh();after adding each line of text.
There is another method called Application.DoEvents(); that has a similar behavior, but its use is not recommended since it sort of short-circuits the normal application flow and can cause your application to fail unexpectedly or do strange things.

Stop a method and wait for user input

My program's written in C# and has a GUI in WPF. When one button is clicked, the method StopAndGo() is invoked.
The problem is that I want this method to stop at certain points, wait for user input, and then continue.
I guess this could be done with multithreading, but since I've never studied that topic, I want to be sure that it could be done, and whether it would be hard to accomplish, before studying it.
Can you give me some guidelines on what needs to be done?
Thanks.
Well first and foremost, I don't think multi-threading is the way to go here. If you want the method to stop at certain points you should code it as such to display popup boxes, text boxes, or any other area to receive the input. So you will need multiple methods to handle this and inside the main method you can call other methods to create these waiting points.
A very simple way to do this is to use nested message pumps via DispatcherFrame
http://www.deanchalk.me.uk/post/WPF-Modal-Controls-Via-DispatcherFrame-%28Nested-Message-Pumps%29.aspx
Updated link:
http://deanchalk.com/wpf-modal-controls-via-dispatcherframe-nested-message-pumps/
Use ManualResetEvent class. Start a thread that executes StopAndGo(); Inside of the context of that thread call
this.manualResetEvent.WaitOne(TimeSpan.Infinite,false);
when your user is done with the input, call
this.manualResetEvent.Set();
that will let the other thread know to proceed with stopANdgo
The reason to go with Multi-threading when your method has a background nature, and you do not want to lock the UI while that method executes.
For waiting scenarios you probably need to go with synchronization objects, like ManualResetEvent.
If your code can't proceed without user input, modal dialogs are a good way to avoid multiple threads.
DialogBox dialogBox = new DialogBox();
// Show window modally
// NOTE: Returns only when window is closed
Nullable<bool> dialogResult = dialogBox.ShowDialog();
From https://msdn.microsoft.com/en-us/library/system.windows.window.showdialog(v=vs.110).aspx

Categories