Adding text to RichTextBox Async #C / WPF - c#

What I am trying to achieve is to add text after every operation to a RichTextBox.
The problem is, that these operations take some time and instead of viewing the appended text after every operation finishes, I view them all at the end of the routine.
Semi-Pseudo code:
RichTextBox richTextBox = new RichTextBox()
if (Operation1())
{
richTextBox.AppendText("Operation1 finished");
if (Operation2())
{
richTextBox.AppendText("Operation2 finished");
if (Operation3())
{
richTextBox.AppendText("Operation3 finished");
}
}
}
The problem is that I view the appended text of operation 1 & 2 after the operation 3 is finished.
I read somewhere that I need to use something called BackgroundWorker???

Using BackgroundWorker, you would just put the background work into DoWork, and the update into RunWorkerCompleted:
var bw1 = new BackgroundWorker();
var bw2 = new BackgroundWorker();
var bw3 = new BackgroundWorker();
bw1.DoWork += (sender, args) => args.Result = Operation1();
bw2.DoWork += (sender, args) => args.Result = Operation2();
bw3.DoWork += (sender, args) => args.Result = Operation2();
bw1.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation1 ended\n");
bw2.RunWorkerAsync();
}
};
bw2.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation2 ended\n");
bw3.RunWorkerAsync();
}
};
bw3.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation3 ended\n");
}
};
bw1.RunWorkerAsync();
You'll notice that this runs afoul of "DRY". You could always consider abstracting the tasks for each step using something like:
var operations = new Func<bool>[] { Operation1, Operation2, Operation3, };
var workers = new BackgroundWorker[operations.Length];
for (int i = 0; i < operations.Length; i++)
{
int locali = i; // avoid modified closure
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => args.Result = operations[locali]();
bw.RunWorkerCompleted += (sender, args) =>
{
txt.Text = string.Format("Operation{0} ended\n", locali+1);
if (locali < operations.Length - 1)
workers[locali + 1].RunWorkerAsync();
};
workers[locali] = bw;
}
workers[0].RunWorkerAsync();
You could do the above 3 times, or use ReportProgress to run all tasks in one background thread, and periodically report progress.

The way that WPF (and most other UI frameworks work) is that there is a UI thread, which handles all the UI events (such as button clicking) and UI drawing.
The UI can't draw things if it's busy doing other things. What's happening is this:
You click a button
The UI thread gets a button click message, and invokes your click handler function
Now, the UI can't redraw or perform any other updates until your click handler function finishes.
Your Operation1 function finishes, and you append to the RichTextBox
The UI can't update because it's still stuck running your code
Your Operation2 function finishes, and you append to the RichTextBox
The UI can't update because it's still stuck running your code
Your Operation3 function finishes, and you append to the RichTextBox
Your function finishes, and now the UI thread is free, and it can finally process the updates and redraw itself.
This is why you see a pause and then all 3 updates together.
What you need to do is make the code that takes a long time run on a different thread so that the UI thread can remain free to redraw and update when you'd like it to. This sample program works for me - it requires .NET 4.5 to compile and run
using System.Threading.Tasks;
...
// note we need to declare the method async as well
public async void Button1_Click(object sender, EventArgs args)
{
if (await Task.Run(new Func<bool>(Operation1)))
{
richTextBox.AppendText("Operation1 finished");
if (await Task.Run(new Func<bool>(Operation2)))
{
richTextBox.AppendText("Operation2 finished");
if (await Task.Run(new Func<bool>(Operation3)))
{
richTextBox.AppendText("Operation3 finished");
}
}
}
}
What happens here is that we use the C# magical async feature, and the order of operations goes like this:
You click a button
The UI thread gets a button click message, and invokes your click handler function
Instead of calling Operation1 directly, we pass it to Task.Run. This helper function will run your Operation1 method on a thread pool thread.
We use the magic await keyword to wait for the thread pool to finish executing operation1. What this does behind the scenes is something morally equivalent to this:
suspend the current function - and thus free up the UI thread to re-draw itself
resume when the thing we're waiting for completes
Because we're running the long operations in the thread pool now, the UI thread can draw it's updates when it wants to, and you'll see the messages get added as you'd expect.
There are some potential drawbacks to this though:
Because your Operation1 method is Not running on the UI thread, if it needs to access any UI related data (for example, if it wants to read some text from a textbox, etc), it can no longer do this. You have to do all the UI stuff first, and pass it as a parameter to the Operation1 method
It's generally not a good idea to put things that take a long time (more than say 100ms) into the thread pool, as the thread pool can be used for other things (like network operations, etc) and often needs to have some free capacity for this. If your app is just a simple GUI app though, this is unlikely to affect you.
If it is a problem for you, you can use the await Task.Factory.StartNew<bool>(_ => Operation1(), null, TaskCreationOptions.LongRunning))) instead and each task will run in it's own thread and not use the thread pool any more. It's a bit uglier though :-)

Related

Run NSAlert app-modal without blocking application

I have an application hosting a finder extension. This extension requests data from the host application.
While running a worker thread, i need to ask the user for feedback via the NSAlert modal. Invoking the main thread and displaying the modal blocks the entire application, thus preventing the finder extension from requesting data.
My application structure is as follows:
1 application thread
1 thread for listening to a CFMessagePort
1 thread for running a file watcher
1 thread performing background work
as well as a running finder extension as client application.
Creating the worker thread
myWorker = new BackgroundWorker();
myWorker.WorkerSupportsCancellation = true;
myWorker.DoWork += new DoWorkEventHandler(delegate(Object o, DoWorkEventArgs args) {
// Time consuming task
// which may require user feedback
});
Inside the worker, invoke an event that is later handled in the os-specific part of the application.
System.Threading.AutoResetEvent waitHandle = new System.Threading.AutoResetEvent(false);
controller.ConflictOccured ? .Invoke(this, new ConflictEventArgs {
Type = ConflictType.LocalFolderDeleted, OnResolve = (userChoice) => {
// Do something else
waitHandle.Set();
}
}
});
// Wait until conflict event has been handled
waitHandle.WaitOne();
Handling the event, running the modal.
private void HandleMyConflict(object sender, ConflictEventArgs eventArgs) {
// Invoking the UI thread
NSApplication.SharedApplication.BeginInvokeOnMainThread(() => {
var conflictAlert = new NSAlert() {
AlertStyle = NSAlertStyle.Warning, MessageText = "Foo", InformativeText = "Bar",
};
conflictAlert.AddButton("Foo");
conflictAlert.AddButton("Bar");
// Run the actual modal - this blocks everything
nint userResponse = conflictAlert.RunModal();
// Determine the user choice
// Resolve event by invoking callback
eventArgs.OnResolve(choice);
});
}
I would expect the modal to be blocking the main application thread since its handling the event, but not the watcher and listener thread. The latter being in an entirely different ThreadPool.

BeginInvoke callback function

I have a c# .NET winforms app making this async call:
simpleDelegate.BeginInvoke(null, null);
My function is being called by the delegate and that all works great. The problem is, after the function finishes on the worker thread, I need the main thread to update some controls on my winform. If the worker thread tries to update these controls, .NET freaks out. But I need the main thread to remain responsive to user actions, and then call my function UpdateFormAfterServerCall() ONLY AFTER the worker thread finishes calling the async function.
I would greatly appreciate if you can give me a concise code sample, rather than abstractly explain how to do this. I've read a hundred explanations already, and am just having trouble wiring it together correctly.
Note: Before the BeginInvoke I have:
simpleDelegate = new MethodInvoker(CallServer);
From different thread if you want to update GUI which is owned by another thread use MethodInvoker
if(control.InvokeRequired)
control.Invoke( (MethodInvoker) ( ()=> updating_function() ) );
else
updating_function();
You could use a BackgroundWorker:
BackgroundWorker bw = new BackgroundWorker();
string result = null;
bw.DoWork += (s, e) =>
{
// Executes on background thread.
// UI remains responsive to user activity during this time.
result = CallServer();
};
bw.RunWorkerCompleted += (s, e) =>
{
// Executes on UI thread upon completion.
resultTextBox.Text = result;
};
bw.RunWorkerAsync();
The Control class (Form is a Control as well) has an Invoke method, you can call this from any thread to execute code on the GUI thread.
In addition, Control has a convenient InvokeRequired property that informs you whether you are on the GUI thread already. You could for instance create the following method in your form:
public class MyForm
{
// ...
public void UpdateMe()
{
if (InvokeRequired)
{
Invoke(new Action(UpdateMe));
return;
}
// Code to update the control, guaranteed to be on the GUI thread
}
}
Here is the code sample [what you want exactly] -
http://www.yoda.arachsys.com/csharp/threads/winforms.shtml
& you can read about all flavours of async here -
http://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.100).aspx

Cannot update Silverlight UI from asynchronous method completed callback

I have inherited some code that queries a DB over a WCF service and then employs a callback when it's done. I am trying to add some code to that callback to update the UI as the data is processed. I'm finding that I cannot get the UI to update during that callback:
client.GetDataAsync();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);
void GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
// Loop through the data
// ...
textBlock1.Text= "test1";
Dispatcher.BeginInvoke(() => textBlock1.Text= "test2" );
var thread = new Thread(() =>
{
// textBlock1.Text= "test3"; (this throws a cross-thread access exception)
Dispatcher.BeginInvoke(() =>
{
textBlock1.Text= "test4";
});
}
thread.Start();
// ...
Debug.WriteLine("done");
}
None of these things update the UI until (apparently) the entire callback is completed. This post:
What thread calls the completed event handler on silverlight WCF calls?
suggests that the callback is running on the main UI thread so that the BeginInvoke call should be unnecessary. Even if I add various delays in the above code, it still doesn't work. Is this possible? Is there a better way to do this?
(This is a follow-up question to this: Multiple asynchronous UI updates in Silverlight)
degorolls is right in suggesting the TPL, your code would look like below (except without the comments)(Also, exceptions MUST be handled in the TPL, so that might make it not worth it, but I dont think it should).
The first methods would remain the same, and yes in event-based async programming thread-safety is taken care of (ie: you always return to the same thread you called out from)
I also noticed that the text output is all doing = instead of +=, but that is probably more of a problem of typing into overflow
So, test1 and test2 will print out at the same time, however everything being spit out from the TPL code should print as it comes in.
UI code should not be doing anything that requires too much time, though...only updating the UI. So, do think of this as a point to refactor?
Let me know if this helps or if I missed what you were looking for.
client.GetDataAsync();
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);
void GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
// Loop through the data
// ...
textBlock1.Text= "test1";
//////Dispatcher should not be needed here as this IS on the main UI thread
Dispatcher.BeginInvoke(() => textBlock1.Text= "test2" );
//////Everything that happens here should NOT be on the main UI thread, thus the cross-thread access exception
//////You can do Dispatcher.CheckAccess to determine if you need to invoke or not
//////Notice the newCopyOfDataToBeWritten. This is a closure,
//////so using the same referenced object will result in errant data as it loops
//////Also, doing it this way does not guarantee any order that this will be written out
//////This will utilize the parallel fully, but there are ways to force the order
var task = Task.Factory.StartNew(()=>
{
Dispatcher.BeginInvoke(()=>textBlock1.Text += newCopyOfDataToBeWritten)
}
);
// ...
///////I assume this is the end of the loop?
Debug.WriteLine("done");
}
....
the below dummied-down code based on what you posted seems to work for me
var outsideThread = new Thread(()=>
{
for(int i = 0; i < 20; i++)
{
//This code will show all at once since it is on the main thread,
//which is still running
//If you want this to display one at a time also, then you need
//to use threads and callbacks like below, also
Dispatcher.BeginInvoke(()=>{textBlock1.Text += "outer" + i;});
int newI = i;
var thread = new Thread(() =>
{
System.Threading.Thread.Sleep(1000 * newI);
Dispatcher.BeginInvoke(() =>
{
//This will display as it comes in
textBlock1.Text += "inner" + newI;
});
});
thread.Start();
}
});
outsideThread.Start();

How to asynchronously wait for x seconds and execute something then?

I know there is Thread.Sleep and System.Windows.Forms.Timer and Monitor.Wait in C# and Windows Forms. I just can't seem to be able to figure out how to wait for X seconds and then do something else - without locking the thread.
I have a form with a button. On button click a timer shall start and wait for 5 seconds. After these 5 seconds some other control on the form is colored green. When using Thread.Sleep, the whole application would become unresponsive for 5 seconds - so how do I just "do something after 5 seconds"?
(transcribed from Ben as comment)
just use System.Windows.Forms.Timer. Set the timer for 5 seconds, and handle the Tick event. When the event fires, do the thing.
...and disable the timer (IsEnabled=false) before doing your work in oder to suppress a second.
The Tick event may be executed on another thread that cannot modify your gui, you can catch this:
private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
private void StartAsyncTimedWork()
{
myTimer.Interval = 5000;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Start();
}
private void myTimer_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
/* Not on UI thread, reenter there... */
this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
}
else
{
lock (myTimer)
{
/* only work when this is no reentry while we are already working */
if (this.myTimer.Enabled)
{
this.myTimer.Stop();
this.doMyDelayedWork();
this.myTimer.Start(); /* optionally restart for periodic work */
}
}
}
}
Just for completeness: with async/await, one can delay execute something very easy (one shot, never repeat the invocation):
private async Task delayedWork()
{
await Task.Delay(5000);
this.doMyDelayedWork();
}
//This could be a button click event handler or the like */
private void StartAsyncTimedWork()
{
Task ignoredAwaitableResult = this.delayedWork();
}
For more, see "async and await" in MSDN.
more completeness:
Depending on your Framework, there is a good chance you will have DispatcherTimer class that can handle the invocation internally (WPF-variants). (finde details in ms docs)
Have you tried
public static Task Delay(
int millisecondsDelay
)
You can use like this:
await Task.Delay(5000);
reference: https://msdn.microsoft.com/en-us/library/hh194873(v=vs.110).aspx
You can start an asynchronous task that performs your action:
Task.Factory.StartNew(()=>
{
Thread.Sleep(5000);
form.Invoke(new Action(()=>DoSomething()));
});
[EDIT]
To pass the interval in you simply have to store it in a variable:
int interval = 5000;
Task.Factory.StartNew(()=>
{
Thread.Sleep(interval);
form.Invoke(new Action(()=>DoSomething()));
});
[/EDIT]
You can wait UI thread the way you want it to work.
Task.Factory.StartNew(async() =>
{
await Task.Delay(2000);
// it only works in WPF
Application.Current.Dispatcher.Invoke(() =>
{
// Do something on the UI thread.
});
});
if you're using .Net Framework 4.5 or higher version, you can use Task.Run instead of Task.Factory.StartNew just like below.
int millisecondsDelay = 2000;
Task.Run(async() =>
{
await Task.Delay(millisecondsDelay);
// it only works in WPF
Application.Current.Dispatcher.Invoke(() =>
{
// Do something on the UI thread.
});
});
You are looking at it wrong.
Click the button, it kicks off a timer with an interval of x seconds. When those are up it's eventhandler executes the task.
So what don't you want to happen.
While the x seconds are elapsing.?
While The task is executing?
If for instance it's you don't want the button to be clicked until delay and task are done. Disable it in the button click handler, and enable it on task completion.
If all you want is a five second delay prior to the task, then you should pass the start delay to the task and let it take care of it.
your application hangs because you are invoking the 5 second sleep/wait on the main UI thread. put the sleep/wait/whatever action in a separate thread (actually System.Windows.Forms.Timer should do that for you) and when it completes invoke the action that turns some control green. remember to check InvokeRequired. here's a short sample (SetText can be called from another thread, if it is the call will instead be invoked on the main UI thread where the textbox is on):
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
I took the sample from here (well worth a read!).
#eFloh in the post marked as answer said:
The Tick event may be executed on another thread that cannot modify
your gui, you can catch this ...
That is not what the docs say.
You are using a System.Windows.Forms.Timer in your example code.
That is a Forms.Timer.
According to the C# docs the Timer events are raised on the UI thread.
This Windows timer is designed for a single-threaded environment where
UI threads are used to perform processing. It requires that the user
code have a UI message pump available and always operate from the same
thread ...
Also see stackoverflow post here

Interacting between two threads

I am working on a winform application, and my goal is to make a label on my form visible to the user, and three seconds later make the label invisible. The issue here is timing out three seconds. I honestly do not know if this was the correct solution to my problem, but I was able to make this work by creating a new thread, and having the new thread Sleep for three seconds (System.Threading.Thread.Sleep(3000)).
I can't use System.Threading.Thread.Sleep(3000) because this freezes my GUI for 3 seconds!
private void someVoid()
{
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
Thread sleepThreadStart = new Thread(new ThreadStart(newThread_restProgram));
sleepThreadStart.Start();
// Once three seconds has passed / thread has finished: lbl_authenticationProcess.Visible = false;
}
private void newThread_restProgram()
{
System.Threading.Thread.Sleep(3000);
}
So, back to my original question. How can I determine (from my main thread) when the new thread has completed, meaning three seconds has passed?
I am open to new ideas as well as I'm sure there are many.
Right now, you are blocking the entire UI thread in order to hide a label after 3 seconds. If that's what you want, then just user Thread.Sleep(3000) from within the form. If not, though, then you're best off using a Timer:
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = 3000;
timer.Tick += (s, e) => { this.lbl_authenticationProcess.Visible = false; timer.Stop(); }
timer.Start();
After 3 seconds, the label will disappear. While you're waiting for that, though, a user can still interact with your application.
Note that you must use the Forms version of Timer, since its Tick event is raised on the UI thread, allowing direct access to the control. Other timers can work, but interaction with the control would have to be Invoke/BeginInvoked.
Did you try to use Timer
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 3000;
t.Start();
t.Tick += new EventHandler(t_Tick);
void t_Tick(object sender, EventArgs e)
{
label.Visible = false;
}
You really don't need to synchronize anything. You just need a new thread, with a reference to your label. Your code is actually pretty close:
private void someVoid()
{
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
lbl_authenticationProcess.Visible = true;
Thread sleepThreadStart = new Thread(new ThreadStart(newThread_restProgram));
sleepThreadStart.Start();
}
private void newThread_restProgram()
{
System.Threading.Thread.Sleep(3000);
if (lbl_authenticationProcess.InvokeRequired) {
lbl_authenticationProcess.Invoke(new SimpleCallBack(makeInvisible));
} else {
makeInvisible();
}
}
private void makeInvisible()
{
lbl_authenticationProcess.Visible = false;
}
So, when someVoid() is called, the message on the label is set, the label is made visible. Then a new thread is started with the newThread_restProgram() as the body. The new thread will sleep for 3 seconds (allowing other parts of the program to run), then the sleep ends and the label is made invisible. The new thread ends automatically because it's body method returns.
You can make a method like so:
public void SetLbl(string txt)
{
Invoke((Action)(lbl_authenticationProcess.Text = txt));
}
And you would be able to call it from the second thread, but it invokes on the main thread.
If you're using .NET 3.5 or older, it's kinda a pain:
private void YourMethod()
{
someLabel.BeginInvoke(() =>
{
someLabel.Text = "Something Else";
Thread thread = new Thread(() =>
{
Thread.Sleep(3000);
someLabel.BeginInvoke(() => { someLabel.Visible = false; });
});
thread.Start();
});
}
That should stop you from blocking the UI.
If you're using .NET 4+:
Task.Factory.StartNew(() =>
{
someLabel.BeginInvoke(() => { someLabel.Text = "Something" });
}).ContinueWith(() =>
{
Thread.Sleep(3000);
someLabel.BeginInvoke(() => { someLabel.Visible = false; });
});
If you are willing to download the Async CTP then you could use this really elegant solution which requires the new async and await keywords.1
private void async YourButton_Click(object sender, EventArgs args)
{
// Do authentication stuff here.
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
await Task.Delay(3000); // TaskEx.Delay in CTP
lbl_authenticationProcess.Visible = false;
}
1Note that the Async CTP uses TaskEx instead of Task.
You can use an AutoResetEvent for your thread synchronization. You set the event to signalled when your secondary thread has woken from it's sleep, so that it can notify your main thread.
That means though that your main thread waits for the other thread to complete.
On that note, you can use SecondThread.Join() to wait for it to complete in your main thread.
You do either of the above, but you don't need to do both.
As suggested in the comments, having a UI thread sleep is not generally a good idea, as it causes unresponsiveness for the user.
However if you do that, you might as well just sleep your main thread and get rid of the extraneous need of the second thread.
I'm not exactly sure this is the right way to do it, but to answer your question, you have to use the Join() function.
public void CallingThread()
{
Thread t = new Thread(myWorkerThread);
t.Join();
}
public void WorkerThread()
{
//Do some stuff
}
You can also add a timeout as parameter to the function, but you don't need that here.

Categories