Reputation: 49
I have a GUI in c# where you can open log files. The program have 2 main functions: setTextMessageTextBox() which updates the GUI when needed (after each operation, giving the appropriate messages)
private static void setTextMessageTextBox(){
MyProject.mainForm update = new MyProject.mainForm();
//messageTextBox.Text is set by default to "Open the file".
const string msg_0 = "Open the file";
const string msg_1 = "Press button to start reading";
const string msg_2 = "Please wait, Reading in progress";
const string msg_3 = "Reading finished";
string textCurrent = update.messageTextBox.Text;
switch (textCurrent){
case msg_0 :
update.messageTextBox.Text = msg_1;
break;
case msg_1 :
update.messageTextBox.Text = msg_2;
break;
case msg_2 :
update.messageTextBox.Text = msg_3;
break;
case msg_3 :
//msg_4 is a global variable with the result
update.messageTextBox.Text = msg_4;
break;
}//switch
}//method setMessageBoxTest
Then, i have the analyze() method which does all the work (the log is very big so it takes up to 1 min to finish).
private void analyze(){
//reading log
//cutting log
//calls other classes to take what pieces are needed
//calls other classes to decode pieces
//saves the final result to msg_4
}
Finally I have the button AnalyzeButton which triggers:
private void analyzeButton_Click(object sender, EventsArgs e){
setTextMessageTextBox();
analyze();
setTextMessageTextBox();
}//analyzeButton
The result is that the GUI freezes and the textBox is updated only after the analyze function finishes.
I tried:
MethodInvoker startAnalyze = new MethodInvoker(analyze);
startAnalyze.Invoke();
but the GUI still Freezes
I tried to invoke the setTextBox
MethodInvoker setMessage = new MethodInvoker(setMessageTextBox);
setMessage.Invoke();
the GUI still Freezes
I tried to use both with method invoker but the Invalid Operation Exception is thrown.
Finally I used the begin_invoke instead of Invoke to analyze:
MethodInvoker beginning = new MethodInvoker (analyze);
beginning.BeginInvoke(null, null)
The GUI doesn't freeze anymore but still the textBox is not updated.
I have read about BackgroundWorker but maybe I am missing something and it didn't work at all. Anybody has a clue of what I am missing or if there is a better way to solve? Thank you for your time.
Update: @SriramSakthivel I moved the creation of the new instance to a global variable level, but the result still the same. The GUI doesnot freeze but the textBox is not updated. It is updated just before I press the button . ex . The text changes from "Open the file" to Press button to start reading but after I press the button the textBox remains as it is. Having the new instance as global I tested in 2 ways: calling the setMessageTextBox, and assigning the new message manually before and after the appropriate action. ex. ' update.messageTextBox.Text = "Analyzing"; update.messageTextBox.Refresh(); analyze(); ' I tried with and without .Refresh() but still nothing changes.
Upvotes: 0
Views: 477
Reputation: 51284
First of all, it doesn't make sense to create a new instance of your form on each call to setTextMessageTextBox()
. I am pretty sure you don't want to have a bunch of windows popping up for each log message (and you aren't even showing these new forms, actually).
Next, GUI elements must be updated from the UI thread only. If you are doing a long running operation on a UI thread, and setting progress properties of different controls, they will not be repainted until the job is done, because setting these properties (like Text
) only invalidates them and basically tells the runtime to redraw them the next time the UI thread has nothing else to do.
In other words, you must use a background worker to do this, whether it's a BackgroundWorker
, a Task
, or a plain Thread
. This background thread should then call the progress update method, which will make sure that the update action is dispatched to the UI thread (which will now be free to repaint it also):
class MyForm : Form
{
public void ShowMessage(string msg)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<string>(ShowMessage, msg));
return;
}
this.messageTextBox.Text = msg;
}
}
And then call it from this Form
instance only.
Upvotes: 1