C# Changing main thread's button text from another thread - c#

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;
}

Related

Window closing not working because of a race condition

Here's my code:
private void OpenLoadingWindow()
{
loadingWindow = new LoadingView();
loadingWindow.Closed += new EventHandler(LoadingWindow_Closed);
_go = true;
loadingWindow.ShowDialog();
}
public void OpenLoadingWindowInNewThread()
{
thread = new Thread(x => OpenLoadingWindow());
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
lock (_locker)
{
Monitor.Pulse(_locker);
}
}
public void CloseLoadingWindow()
{
lock (_locker)
while (!_go)
Monitor.Wait (_locker);
if (loadingWindow != null)
{
loadingWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
{
_go = false;
loadingWindow.Close();
loadingWindow = null;
}));
}
}
In code I first call OpenLoadingWindowInNewThread() and after that I call CloseLoadingWindow(). However, the first time the code is executed it works fine. But after that, the code in CloseLoadingWindow(), in BeginInvoke doesnt get executed. What am I doing wrong?
What I want to achieve is this: open the loading window, execute some code. After the code is execited I call the closing method, I want to close the loading window.
The main problem here is that you are creating a second thread for UI. Don't do that.
Unfortunately, you didn't provide a good code example. So for the sake of an answer, let's assume you're doing something like this:
void button1_Click(object sender, EventArgs e)
{
DoLoadingWork();
}
void DoLoadingWork()
{
OpenLoadingWindowInNewThread();
LoadingWork();
CloseLoadingWindow();
}
I.e. some event happened in your UI, and now you have to do some work. You implemented this by calling the methods you've shown in your question, processing the work in your UI thread and creating a second thread to show the dialog.
This is the wrong way to approach this. Instead, you should keep all of your UI in the same thread, and do the work in a different thread. That would look more like this:
void DoLoadingWork()
{
using (LoadingView form = new LoadingView())
{
form.Shown += async (sender, e) =>
{
await Task.Run(() => LoadingWork());
form.Close();
};
form.ShowDialog();
}
}
This version does the following:
Creates your status dialog in the UI thread
Subscribes to the Shown event, to ensure the dialog is visible before anything else happens
Shows the dialog
Once the dialog is shown, a new thread is started to execute the LoadingWork() method
When the LoadingWork() method completes, the dialog is closed, allowing the dialog to be disposed and the DoLoadingWork() method to return.
Note that even if you have to interact with the UI from the code that does the processing, or if you need a way to interrupt the processing, the above is still the correct way to do things. Those other aspects of the requirements can easily be implemented, using standard idioms for dealing with them.
Without an actual example of what that processing might be, and how the UI interaction and/or interruption works, it's impossible to say exactly how that part would be implemented. But it would generally involve using Invoke() for UI interaction (or even better, refactoring the processing so that it uses async/await, with UI interaction occurring between await statements for the individual pieces of the work) and a flag or CancellationToken to deal with interrupting the thread.
If your processing does in fact interact with the UI, and you did in fact run it in the UI thread, then it's likely you've got calls to methods like Refresh() or Application.DoEvents() interspersed. These methods are practically never required, and IMHO are always a sign that the code has been implemented incorrectly. As an added benefit of changing your implementation to put the right code in the right thread, you won't have to use any of those methods to interact with the UI (instead, you'll use Invoke()).

Thread accesing .NET control issue

I have two threads that look like this
pbDB_running = true; // start progress bar
Thread connectDb = new Thread(new ThreadStart(ConnectToDb));
Thread runProgress = new Thread(new ThreadStart(RunpbDB));
connectDb.Start();
runProgress.Start();
connectDb.Join(); //wait untill the connection is done
pbDB_running = false; //stop the progress bar
as you probably might have guessed, ConnectToDb is used to make a connection to a database, while runpbDB is making a progress bar run on the interface. The progress bar (pbDB) is a Windows.Forms control created with drag and drop on the design view.
The runProgress thread is running RunpbDB() wich looks like this :
private void RunpbDB()
{
while (pbDB_running)
{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;
}
pbDB.Value = 0;
}
When the two threads start I get the following exception inside RunpbDB() :
Cross-thread operation not valid: Control 'pbDB' accessed from a thread other than the thread it was created on.
What can I do to overcome this situation?
Have you thought about using a BackgroundWorker? This might make your life a lot easier. You could set two up, one for your database call and the other for your progress bar. Just listen for the background workers ProgressChanged and RunWorkerCompleted events.
More information on MSDN
Use Control.Invoke method to remedy this problem. The whole your solution will became
private void RunpbDB()
{
while (pbDB_running)
{
Invoke((Action)(()=>{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;}));
}
Invoke((Action)(()=>{pbDB.Value = 0;});
}
You can use something along the lines of pbDB.InvokeRequired and if so, call pbDB.Invoke to perform your action back on the UI thread.
You do not need the check if you know it will always be done on a separate thread than the UI thread.
Here is a link to some code on this and other ways to accomplish this.
You could also use a BackgroundWorker
This is a security imposed by microsoft for its .NET technology. It basically happens when you access a winforms element from a separate thread, i.e. not in the main thread where the GUI winforms is running. The solution is to create a delegate for your RunpbDB method. See the solution here Best Way to Invoke Any Cross-Threaded Code?. in here too: How to update the GUI from another thread in C#?
Just make your life easier and use a BackgroundWorker for this if you don't have access to .NET 4.0. If you can use 4.0+, use the TPL. And if you can use 4.5, you can use the new async/await functionality. There are tons of examples here on Stack Overflow. Here is a link from Stephen Cleary comparing them.
Cross thread operation call when UI thread is involved was discouraged by VS team in 2.0(before that it was possible for security reasons. There are two ways to overcome this issue. Easy way is to set the static property
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls
to false which then disables this check globally from your application. But this solution is not advised by any one and not even by me since it again opens the security holes.
Another ways is, in the method first check if UI control needs Invoke,If so then use control's invoke method to invoke the current method again and then return. Code can better clear what I want to say so
private void RunpbDB()
{
if (pbDB.InvokeRequired)
{
pbDB.Invoke(new Action(RunpbDB));
return;
}
while (pbDB_running)
{
if (pbDB.Value == 100) pbDB.Value = 0;
else pbDB.Value += 1;
}
pbDB.Value = 0;
}

How to block the flow, but keep the GUI responsive

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

correct method of threading

I am very new to WPF. And just started learning threading.
Here is my scenario:
I've created a program with a button named START. When start button is clicked it starts to do some complex task in different thread. Just before beginning the complex task it also creates a UI elements in another STA thread (technically i don't know what i am saying).
Here is a sample code:
// button click event
private void button1_Click(object sender, RoutedEventArgs e)
{
System.Threading.Thread myThread = new System.Threading.Thread(
() => buttonUpdate("Hello "));
myThread.Start();
}
private void buttonUpdate(string text)
{
System.Threading.Thread myThread = new System.Threading.Thread(createUI);
myThread.SetApartmentState(System.Threading.ApartmentState.STA);
// set the current thread to background so that it's existant will totally
// depend upon existance of main thread.
myThread.IsBackground = true;
myThread.Start();
// Please don't read this loop it's just for my entertainment!
for (int i = 0; i < 1000; i++)
{
System.Threading.Thread.Sleep(100);
button1.updateControl(new Action(
() => button1.Content = text + i.ToString()));
if (i == 100)
break;
}
// close main window after the value of "i" reaches 100;
this.updateControl(new Action(()=>this.Close()));
}
// method to create UI in STA thread. This thread will be set to run
// as background thread.
private void createUI()
{
// Create Grids and other UI component here
}
The above code succesfully does what i want to do. But do you think it's the correct way? so far i don't have any problem here.
EDIT: OOps I forgot to mention this class:
public static class ControlException
{
public static void updateControl(this Control control, Action code)
{
if (!control.Dispatcher.CheckAccess())
control.Dispatcher.BeginInvoke(code);
else
code.Invoke();
}
}
If you are using .NET 4.0 you might want to consider using the Task class from the Task parallel library. Read into it since you say you are new to threading. It's much more flexible to use.
Also I think that this link could be very helpful to you:
http://www.albahari.com/threading/
There seems to be no good reason to use 2 threads.
You should be able to execute the createUI() on the main thread. That'll be complicated enough when it becomes time to fill those controls.
Only one thread can interact with the UI. If you are going to add a control to a page or windows then you must use the thread that created the page or window. The typical scenario is to use threading to create expensive data or object in the background and then on the callback (running on the primary thread) retrieve the result and bind appropriate data to the UI. Look at using BackgroundWorker as it takes care of a lot of the threading detail for you. http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx Why do you want to create UI objects on another thead?

Change WPF controls from a non-main thread using Dispatcher.Invoke

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

Categories