I have a method which updates a richtextbox when it executes. I have to call this method a few times, without blocking the GUI. Once a call ended and the richtextbox populated, i have to block the flow to show some info to the user ( the next calls to the method should not start ). I have done this with a MessageBox.Show().
Here appears another problem, i have to be able to abort the program at any given time but the modal messagebox doesn't let me do that. Maybe you will tell me to get another form and simulate the messagebox, but hey, i still need to block the flow if the modal box will be replaced.
Yes, this is a multi-threading issue. There are many ways you can achieve this. Here is an explanation of what multi-threading is. What is multi-threading?
Also, here's my favorite tutorial on multi-threading: albahari.com
Threads have their own stack and operate independently. Here's one example of how to spawn a thread:
Thread _thread = new Thread(() => { MessageBox.Show("Hello world!", "Spawned in a different thread"); });
_thread.Start();
To invoke your richtextbox from another thread, one way to do it is to create a method inside of your form that appends text to your textbox by invoking it. Here is an example of how to do it:
public void AddText(string Text)
{
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(AddText); // Delegate
this.Invoke(d, new object[] { text });
}
else { this.textBox1.AppendText(text); }
}
Here is a msdn reference to what you're trying to do:
http://msdn.microsoft.com/en-us/library/ms171728.aspx
Related
Background
I am a person who is very new to doing threading/concurrent operations in C#, and am struggling with cross-threaded GUI applications. Right now I am experiencing an exception being thrown at run-time that I am not sure how to deal with. First, I will provide a background of what's going on, and then I will describe what I have tried to debug this.
I have a task that does a bunch of calculations.
readingDumpTask = new Task(() =>
{
this.myDumpEntries = BiteDump.GetEntriesFromFile(fileName);
});
When this task is completed, I want to do two things at once. I implement this process, in the following way...
// display dump information
displayDumpTask = readingDumpTask.ContinueWith(
delegate
{
Parallel.Invoke
(
() => this.DisplayEntries(),
() => this.DisplayDump()
);
}
, TaskScheduler.FromCurrentSynchronizationContext());
The two methods that are being delegated, work in the following way...
Method #1
private void DisplayEntries()
{
UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayEntries);
if (this.InvokeRequired)
{
this.BeginInvoke(myDel);
}
// fills some data into labels on the GUI
}
Method #2
private void DisplayDump()
{
UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayDump);
if (this.InvokeRequired)
{
this.BeginInvoke(myDel);
}
// fills some data into a RTB on the GUI
}
I would like to make it clear that none of the controls being used by method #1, are ever touched by method #2.
Problem Statement
The problem here, is that I am getting an InvalidOperationException as soon as I try to access the controls from inside of the DisplayDump function. I only ever get this exception when I have clicked on the Dump tab as shown below.
I think that this has something to do with the fact that the DisplayDump method is accessing the text-box sitting inside the Dump tab. Now, when I am on the Navigation tab, I never experience any problems and everything works safe/sound.
Attempted Debugging
I have only just this morning discovered how to even see the Parallel Tasks Debug Window let alone really know how to use it to solve my problems. So, I have removed one of the method calls from the Parallel.Invoke block and I see that it doesn't matter which tab I am on, I will never get the exception in this case (DisplayDump always works). However, as soon as I have both actions inside of the Parallel statement, the problem occurs. So... So far all I know is that it has something to do with the Parallel.Invoke.
After you call BeginInvoke, your method should return. If you fall through without a return, then the GUI will be accessed on a non-UI thread and you will get an InvalidOperationException.
private void DisplayEntries()
{
UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayEntries);
if (this.InvokeRequired)
{
this.BeginInvoke(myDel);
return; // ** change here
}
// fills some data into labels on the GUI
}
I have a windows form (C#.NET) with a statusLabel that I can not seem to get to update in the middle of a process in event handler methods. My code looks like this...
void Process_Completed(object sender, EventArgs e)
{
string t = "Process is finished!";
this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t });
}
void Process_Started(object sender, EventArgs e)
{
string t = "Process has begun";
this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t });
}
private delegate void StatusLabelUpdator(string text);
private void updateStatusLabel(string text)
{
StatusLabel1.Text = text;
statusStrip1.Invalidate();
statusStrip1.Refresh();
statusStrip1.Update();
}
When I run the code, once the process starts, the Process_Started method is triggered, and a couple seconds later the Process_Completed method is triggered. For some reason I can not get the status label to ever display "Process has begun". It only ever displays "Process is finished!". As you can see I have tried invalidating, refreshing and updating the status strip which contains the status label but no success. I can't call update/refresh/invalidate on the statuslabel itself because those methods are not available to it. What am I doing wrong?
ADDED INFO:
The "process" is started by a button click on the form which calls a method in a separate class that looks like this:
public void DoSomeProcess()
{
TriggerProcessStarted();
System.Threading.Thread.Sleep(2000); // For testing..
TriggerProcessComplete();
}
and inside the TriggerProcessxxxx methods I trigger the events using this code...
var EventListeners = EH.GetInvocationList(); //EH is the appropriate EventHandler
if (EventListeners != null)
{
for (int index = 0; index < EventListeners.Count(); index++)
{
var methodToInvoke = (EventHandler)EventListeners[index];
methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, new object[] { });
}
}
Finally, I have added Application.DoEvents() to the updateStatusLabel method but it did not help. I am still getting the same result. Here is my update method.
private void updateStatusLabel(string text)
{
StatusLabel1.Text = text;
statusStrip1.Refresh();
Application.DoEvents();
}
So I guess the "processing" is taking place on the UI thread but eventhandler is invoked on it's own thread which then invokes the control update back on the UI thread. Is this a dumb way of doing things? Note: The class that contains the DoSomeProcess() method is in a separate .NET ClassLibrary that i am referencing.
If you're doing your processing on the UI thread, it won't be able to do anything else (like redraw updated labels) while the processing is running. So for instance, if the processing is happening because the user clicked a button and is triggered by the button click handler (without explicitly placing it on another thread), it's running on the UI thread. Even though you update the label's text, it doesn't get drawn until it receives a paint message, at which point it's probably busy doing your processing.
The answer is to do long-running processing on a separate thread. The hack (IMHO) is to use Application.DoEvents to let the UI thread do some UI stuff during your processing. If you put one of those after updating the label and before you start your processing, odds are pretty high the label will get repainted. But then, during the processing, no further paint events can get processed (leading to half-drawn windows when someone moves another app window over your app and back, etc.). Hence my calling it a hack (even though, er, um, I've been known to do it :-) ).
Edit Update based on your edits:
Re
So I guess the "processing" is taking place on the UI thread but eventhandler is invoked on it's own thread...
I'm assuming DoSomeProcess is triggered from the UI thread (e.g., in direct response to a button click or similar). If so, then yes, your processing is definitely on the UI thread. Because TriggerProcessStarted triggers your callback asynchronously via BeginInvoke, you have no idea when it will run, but in any case your code then immediately launches into processing, never yielding, so no one else is going to be able to grab that thread. Since that's the UI thread, the call to the delegate will block on the Invoke call setting the label's text, whereupon it has to wait for the UI thread (which is busy processing). (And that's assuming it's scheduled on a different thread; I couldn't 100% convince myself either way, because Microsoft has two different BeginInvokes -- which IIRC one of the designers has acknowledged was a Really Dumb Idea -- and it's been a while since I fought with this stuff.)
If you make the TriggerProcessStarted calls to your callbacks synchronous, you should be okay. But ideally, schedule the processing (if it's not doing UI) on its own thread instead.
I have a class called Form1, which has a button in it. Now in that class I made another thread.
If I try to change the button in any way from the new thread, I get the cross-thread error/exception
new Thread(delegate ()
{
while (!DL.HasExited)
{
Thread.Sleep(500);
}
File.Delete(folderBrowserDialog1.SelectedPath + #"\Steam\steamcmd.zip");
//The code below this note is the problem
button1.Text = "START DOWNLOADING";
button1.Enabled = true;
}).Start();
I need to have the code in the new Thread, because I don't want to make my program freeze when it reaches the while loop.
So how can I change the button text from a different thread?
you cannot access ui element properties from a different thread. Use beginInvoke
button1.BeginInvoke( new MethodInvoker(() =>
{
button1.Text = "START DOWNLOADING";
button1.Enabled = true;
}));
Use Invoke(). This function takes a delegate (you can pass an anonymous function too). Something like:
Invoke(() => {
button1.Text = "START DOWNLOADING";
button1.Enabled = true;});
Threads are not meant to mess with each other's memory space - thus, your attempt to simply change the text will fail. However, there are 3 solutions I could think of:
Using invoke()
As others mentioned a few seconds before I did, you could use invoke to change the text in another form. However, if you'd like any more communication between the threads, this would be inefficient.
Using shared resources
In order to converse between threads, you can use a common resource that will be used for their communication. A very basic example of this is writing from the thread to a text file the text you want the button to display, and reading it each few seconds from the other thread and checking for changes. There are some better ways to do this, this is just an example. If you'd like me to show you an example of this method, ask for it and I'll gladly provide it.
Using processes instead of threads
Proccesses and threads both allow the multytasking you need, however, processes can interfere with each other. There are some more diffrences you should read about before making this decision, which leaves it up for you - which do you think is more fit for each one of these, a process or a thread? I'll happily provide an example of the usage of proccesses if you'd like me too, as well.
Good luck :)
The simplest way to achieve what you want is to use the BeginInvoke function on the control:
public delegate void InvokeDelegate();
new Thread(delegate ()
{
while (!DL.HasExited)
{
Thread.Sleep(500);
}
File.Delete(folderBrowserDialog1.SelectedPath + #"\Steam\steamcmd.zip");
//The code below this note is the problem
button1.BeginInvoke(new InvokeDelegate(InvokeMethod));
}).Start();
public void InvokeMethod()
{
button1.Text = "START DOWNLOADING";
button1.Enabled = true;
}
I'm using the following method to show a modeless Message Box.
public void ShowMessageBox(string Message)
{
var thread = new Thread(
() =>
{
MessageBox.Show(Message);
});
thread.Start();
}
The "() => {...}" is something I've never seen before. What is the name for this code pattern?
Also, thread.Start starts the thread, and it automatically closes once the "()=>{...}" method completes (when the Message Box is OK'ed), right? If so, can you please point me to some official documentation saying that the thread closes automatically?
Thanks!
It's the lambda operator, and read as "goes to". MSDN has a good intro: Lambda Expressions (C# Programming Guide)
One concern with your example is that you're spinning up a new thread to update the UI, the UI is intrinsically single-threaded, so background updates are generally the wrong thing to do (unless you're manually/explicitly checking InvokeRequired and calling Invoke() as needed.
Regarding the UI threading...
In WinForms every Form or Control is created on a particular thread (the "UI Thread"), and you can think of that thread as owning that control (not exactly correct, but a good way to conceptualize it). Updating the UI from that thread is safe, updating the UI from another thread runs the risk of collisions and corruption and all the usual risks of parallel/async programming.
...So... how do you safely update the UI from a background thread without blocking the UI? In short--you can't--the best you can do is block it for the bare minimum required to update the UI. This is where InvokeRequired and Invoke() come in...
Here's a sample: you should be able to drop this into the code-behind of a new form with a button and textbox.
To use:
Try commenting out either the call to SetTextAsyncSafe() or SetTextAsyncSafe() -- running both could confuse you since they won't necessarily execute in the order they're called (they're running async, remember?).
Then set a breakpoint on SetText(). You should see the "safe" call will actually call the method twice--the first call will detect InvokeRequired and will call the method a 2nd time for the correct thread by Invoke()'ing to it.
You should see an Exception thrown when SetTextAsyncUnsafe() actually gets to the textBox1.Text = value; statements. The exception will be an InvalidOperationException with a message stating "Cross-thread operation not valid" -- you can google this term for more details.
The code:
private void button1_Click(object sender, EventArgs e)
{
SetTextAsyncSafe("This update was made from the UI Thread by using Invoke()");
SetTextAsyncUnsafe("This update was made directly from the background thread and can cause problems");
}
private void SetTextAsyncUnsafe(string value)
{
new Thread(() => SetText(value, false)).Start();
}
private void SetTextAsyncSafe(string value)
{
new Thread(() => SetText(value, true)).Start();
}
private void SetText(string value, bool checkInvokeRequired)
{
if (checkInvokeRequired)
{
if (InvokeRequired)
{
Invoke(new Action(() => SetText(value, checkInvokeRequired)));
return; // early exit
}
}
textBox1.Text = value;
}
That is a Lambda. In this case, you're using it to create a new anonymous method that will be run when the new Thread is started.
It's the (near) equivalent of:
public void ShowMessageBox(string Message)
{
var thread = new Thread(ShowBox);
thread.Start(Message);
}
public void ShowBox(object message)
{
MessageBox.Show(message.ToString());
}
This is called a Lambda Expression. You can read more here.
Lambda expression, C# version 3 feature.
Don't use this code. A message box needs a parent window, something it can make sure to be on top of. It can normally find a parent by itself by iterating the windows that were created on the same thread. Not in this case though, there are no other windows, it has to pick the desktop window as the parent.
That will go wrong badly when the user is working in an app window or switches focus to another app, the message box disappears behind the foreground window. There is no obvious way for the user to tell that it is there, she'll just loses sight of it. It could be hours, if not days, before she finds it back. That thread is meanwhile consuming resources badly, you would probably never consider it if you knew that this message box requires a megabyte of memory. In extreme cases, you'll crash the program with OOM.
The common alternative in Windows UI programming is a balloon tooltip provided by a NotifyIcon. Or your own form with the TopMost property set to True so it cannot easily get lost. Also allows you to control the position, important for "non-modal" notifications that should not get in the way. Set that form's ShowWithoutActivation property to true in the form constructor so it doesn't steal the focus.
Its a statement lambda.
Yes, thread is active as long as this anonymous method is running. Since after MessageBox.Show() there is no other statements, thread will exit, and this must be true... if you are in doubt add this before start:
thread.Name = "LALALA";
And then debug your app. When the message box apear, pause execution, go to Threads View and you will see LALALA running. Click OK and pause again, there should be no "LALALA"... =)
I have recently started programming in WPF and bumped into the following problem. I don't understand how to use the Dispatcher.Invoke() method. I have experience in threading and I have made a few simple Windows Forms programs where I just used the
Control.CheckForIllegalCrossThreadCalls = false;
Yes I know that is pretty lame but these were simple monitoring applications.
The fact is now I am making a WPF application which retrieves data in the background, I start off a new thread to make the call to retrieve the data (from a webserver), now I want to display it on my WPF form. The thing is, I cannot set any control from this thread. Not even a label or anything. How can this be resolved?
Answer comments:
#Jalfp:
So I use this Dispatcher method in the 'new tread' when I get the data? Or should I make a background worker retrieve the data, put it into a field and start a new thread that waits till this field is filled and call the dispatcher to show the retrieved data into the controls?
The first thing is to understand that, the Dispatcher is not designed to run long blocking operation (such as retrieving data from a WebServer...). You can use the Dispatcher when you want to run an operation that will be executed on the UI thread (such as updating the value of a progress bar).
What you can do is to retrieve your data in a background worker and use the ReportProgress method to propagate changes in the UI thread.
If you really need to use the Dispatcher directly, it's pretty simple:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => this.progressBar.Value = 50));
japf has answer it correctly. Just in case if you are looking at multi-line actions, you can write as below.
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
this.progressBar.Value = 50;
}));
Information for other users who want to know about performance:
If your code NEED to be written for high performance, you can first check if the invoke is required by using CheckAccess flag.
if(Application.Current.Dispatcher.CheckAccess())
{
this.progressBar.Value = 50;
}
else
{
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
this.progressBar.Value = 50;
}));
}
Note that method CheckAccess() is hidden from Visual Studio 2015 so just write it without expecting intellisense to show it up. Note that CheckAccess has overhead on performance (overhead in few nanoseconds). It's only better when you want to save that microsecond required to perform the 'invoke' at any cost. Also, there is always option to create two methods (on with invoke, and other without) when calling method is sure if it's in UI Thread or not. It's only rarest of rare case when you should be looking at this aspect of dispatcher.
When a thread is executing and you want to execute the main UI thread which is blocked by current thread, then use the below:
current thread:
Dispatcher.CurrentDispatcher.Invoke(MethodName,
new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.
Main UI thread:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background, new Action(() => MethodName(parameter)));
The #japf answer above is working fine and in my case I wanted to change the mouse cursor from a Spinning Wheel back to the normal Arrow once the CEF Browser finished loading the page. In case it can help someone, here is the code:
private void Browser_LoadingStateChanged(object sender, CefSharp.LoadingStateChangedEventArgs e) {
if (!e.IsLoading) {
// set the cursor back to arrow
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => Mouse.OverrideCursor = Cursors.Arrow));
}
}