0

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?

Razick
  • 744
  • 2
  • 9
  • 28
  • 5
    Does the code block you put above happen on the UI thread? If so doing a `.Join()` on the UI thread is your problem. – Scott Chamberlain Dec 18 '13 at 15:22
  • Hi everyone, I have a bad cold, I'll try some of these suggestions when I'm recovered. I appreciate all the help. – Razick Dec 19 '13 at 23:24

5 Answers5

2

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?

Rob Prouse
  • 22,161
  • 4
  • 69
  • 89
  • 1
    I'm a little unsure of why using `Join()` is a problem here. Join causes the current thread to wait for the thread on which it was called to finish, correct? So since the next GUI update does not occur until *after* the join (there are only two asynchronous threads, the GUI and the process thread), it shouldn't be blocking the GUI at that point. Am I misunderstanding something? – Razick Dec 18 '13 at 15:32
  • BTW in GTK# there is not any BackgroundWorker – Adriano Repetti Dec 18 '13 at 15:35
  • 3
    @Razick You are misunderstanding, you may have changed the data values the GUI uses to display but if the function never returns to the message pump it never gets an opportunity to repaint the screen with the new information. You must return control to the message pump instead of blocking with `Join` so the paint events can happen. You can then signal that your task is done with a `Event` and have the event start the next step instead of blocking on a `Join`. – Scott Chamberlain Dec 18 '13 at 15:36
  • As Scott said, you are changing the values of the UI controls, but blocking the message pump, so the paint messages are not being picked up by the controls, so they are not updating. If BackgroundWorker isn't available, an Event will work as Scott said, just make sure that any UI changes that you make in the Event invoke back to the UI thread. – Rob Prouse Dec 18 '13 at 16:05
1

You need to dispatch Update message to UI thread, call invoke instead of direct property

this.status.BeginInvoke((Action)(() => this.status.Text = "Something happen"));
Arsen Mkrtchyan
  • 49,896
  • 32
  • 148
  • 184
  • I get an error when running this: `Error CS1061: Type Gtk.Label' does not contain a definition for BeginInvoke' and no extension method BeginInvoke' of type Gtk.Label' could be found (are you missing a using directive or an assembly reference?)` – Razick Dec 18 '13 at 15:35
  • 2
    @Razick take a look to [this post](http://stackoverflow.com/questions/2548200/how-do-i-access-gui-gtk-from-multi-threads). – Adriano Repetti Dec 18 '13 at 15:36
  • 1
    @Razick, I have added conversion to Action sorry, BeginInvoke method takes Delegate, so direct conversion is required..try with conversion please. Also please note, that if the code you provide is in UI thread, Join will block it and no update will happen – Arsen Mkrtchyan Dec 18 '13 at 15:38
  • I tried it with Action, and yes it is in the UI thread so that must be the problem. – Razick Dec 18 '13 at 15:40
  • The first thread has to finish before the second thread begins because the second thread uses data from the first. – Razick Dec 18 '13 at 15:50
  • than you can start second thread on the end of first one, but I don't see even any reason to create a new thread, because if the first thread have been finished, it can do the work of second one, so just do both parts in one threads... – Arsen Mkrtchyan Dec 18 '13 at 15:54
1

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.

Dean.DePue
  • 1,013
  • 1
  • 21
  • 45
0

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

Musketyr
  • 745
  • 1
  • 16
  • 37
-1

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

Prathap
  • 1
  • 1
  • The problem is that he's blocking the UI thread, not that he's not properly changing the text. – Servy Dec 18 '13 at 16:21