Say I'm showing the user a form, and using a BackgroundWorker to do some work behind the scenes.
When the user clicks OK, I cannot continue until the BackgroundWorker has completed.
If it hasn't finished when the user clicks Ok, I want to show a WaitCursor until it has, and then continue.
What's the best way to implement this?
I know I could use a plain old Thread, and then do Thread.Join, but I like BackgroundWorker.
Can it be done nicely?
You could use this code, instead of BW
var resetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(5000);//LongRunning task, async call
resetEvent.Set();
});
resetEvent.WaitOne();// blocking call, when user clicks OK
Ideally, you should disable all the relevant controls on the form, then re-enable them when the BackgroundWorker completes. That way you won't get a locked UI - and you can have a cancel button etc.
If you wanted to actually block until it had finished, you could just run the task synchronously to start with.
You could check is the BackgroundWorker is running with a check in the btnOK_Click event handler to see if bw.IsBusy is true. If so change the cursor and set a boolean flag that indicates that OK handler is waiting. When BackgroundWorker finishes its job check if ok had been waiting, if so, set the flag to false change the cursor and do the job of the OK button. :-)
I'd just use boolean flags and a lock.
object waitlock = new object();
bool isBackgroundWorkerCompleted = false;
bool isOKButtonPressed = false;
private void onPress(...)
{
lock(waitlock)
{
isOKButtonPressed = true;
if (isBackgroundWorkerCompleted) DoNextStep();
else cursor.Wait(); //psuedo-code - use whatever the actual call is...
}
}
Background thread does the same trick (just remember to BeginInvoke back to the main UI thread)
Related
I have a problem with my UI not updating while executing a Command.
I've got an indeterminate ProgressBar which has it's visibility bound to the IsBusyIndicator-property in the ViewModel. A Command should now excecute a method and show the ProgressBar while computing, as shown in the codesnippett below. However, this doesn't work as I'd expect. The property is set to the correct value but the UI doesn't update to show the ProgressBar.
It works fine if I just set the IsBusyIndicator to true and do nothing else in the Command, so INotifyPropertyChanged and the Binding are working correctly.
void CommandExecute()
{
IsBusyIndicator = true;
// Do stuff that takes long
IsBusyIndicator = false;
}
It seems to me that the UI waits for the Command to finish before it updates the View. Is there a way to force the UI to update right away?
Thanks in advance, may the force be with you.
You can try the following:
private async void CommandExecute()
{
IsBusyIndicator = true;
await Task.Run(() => DoWork());
IsBusyIndicator = false;
}
This makes DoWork() run on a separate thread. Generally, async void methods are to be avoided, but they're OK if they're handling a UI event.
As others said here, it doesn't work since you're probably doing your work on the UI thread.
Here are the technical stuff:
When PropertyChanged of INotifyPropertyChanged is raised, the binding is scheduling a handler by invoking it in the dispatcher with BeginInvoke, not immediately.
Since the UI thread is still running your long operation, handling the property change remains waiting on the dispatcher queue.
That's why the change happens only when the command is done.
I have a program that run in a loop. it's this
private void ReadCamAuto_Click(object sender, EventArgs e)
{
this.serialPort1.DataReceived -= new System.IO.Ports.SerialDataReceivedEventHandler(this.DataReceivedHandler);
RunReadCamAuto = true;
while (RunReadCamAuto)
{
serialPort1.WriteLine("2,2,2,2");
CreatePic(4, 4);
}
this.serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(this.DataReceivedHandler);
}
but the problem is then I'm in the While loop I cant press any other Button in my program so it is not possible to stop. I have no idea how to stop it ?
I tried to press a button who set the RunReadCamAuto to false and Console.ReadKey()
I'm Using:
c# Form App
MS VS 2010
You cannot expect user interface to work while the main thread is busy doing some work in a loop. Use BackgroundWorker object to fire desired loop in it - it's automatically invoked on other thread and from your main thread you can easily send the message to stop its work.
You need to assign your method to BackgroundWorker's DoWork event and then from your form just call myBackgroundWorker.RunWorkerAsync(). Then by calling myBackgroundWorker.CancelAsync() you will change its CancellationPending property to true, which you can be constantly checking in your loop to break execution.
Please see here.
In my c# winform app I have a button which when clicked, it performs some calculations which can tke some time.
I also have a label.visible = false which I would like to label.visible = true right after the button click so that the user can see the app working away.
The thing is even when label.visible = true is the first thing in the button1_Click(object sender, EventArgs e) method, it only toggles to visible at the very end, after the calculation is performed.
How can I show the label right after the button_click?
If you need your application to remain responsive whilst your app is processing, take a look at Background Worker
You can force your form to update & process any background messages by calling:
Application.DoEvents();
Just after you have changed your label - although this is probably a bit of a hacky solution.
Your calculation is being performed on the UI thread. This means that it is blocking the UI from refresshing after you have set the visibility of the label.
You should consider doing the calculation on another thread using a Task. This will allow the UI to be responsive during what it seems is a long operation that can run in the background.
e.g.
var taskCalc = Task.Factory.StartNew(() => //Do Calculation );
There are several ways to tackle this.
One way (the simplest) is to call
this.Refresh();
right after you set the Label to visible.
Another is to do your calculations in a background thread. The easiest way to do that is to use a BackgroundWorker. Then your main thread can just continue serving the UI (refreshing the form, responding to buttons etc.) while the background worker thread performs the computation.
See http://www.dotnetperls.com/backgroundworker for more on background worker threads.
Like others have said: You are performing your work on the UI Thread. I am showing you another valid way of achieving what you need. You can move your work to a separate thread using an anonymous delegate.
(new System.Threading.Thread(() =>
{
dowork(); // What ever work you need put here.
})).Start();
I have a modal dialog with a cancel button only which pops up when the user clicks on a button. Aftre the modal dialog pops up, I would like to start a long process which monitors external event. If the event happens, then the dialog will be closed automatically. The user can cancel the monitoring process by clicking the cancel button.
I assigned the process start to the Shown event
private void ProceedForm_Shown(object sender, System.EventArgs e)
{
controller.StartSwiping();
}
The process itself is a loop
public void StartSwiping()
{
Status status;
do
{
status = CallForFeedback();
} while (status == Status.Pending);
form.DialogResult = DialogResult.OK;
form.Close();
}
The process starts fine, but the dialog does not pop up, so the user can non cancel the process. I also tried to assign the start to the Load event, but nothing changed.
Is there any way to Show the dialog and after that start the process?
Thanks
Your problem is that you are doing everything in the UI thread. You need to put you status monitoring loop in a separate thread so that the UI thread can remain responsive.
There are several ways you can do this, but one easy place to start is with the BackgroundWorker class
Use a Task to do your LongRunning events:
CancellationTokenSource _cancelationTokenSource = new CancellationTokenSource();
new Task(() =>
{
//Do LongRunning task
}, _cancelationTokenSource.Token, TaskCreationOptions.LongRunning).Start();
Use the _cancelationTokenSource to cancel the task when needed.
I would move the long running code onto a background thread as you are blocking the UI thread, which is why the UI never displays.
Use a background worker class for the controller functionality http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
When the work is completed on the background worker (i.e. the event is received) then you can use the following mechanism to callback onto the UI thread:
http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
Note: the article says you can turn off the crossthreadexception this would be considered bad practice, instead handle it the correct way using the InvokeRequired check and then invoke method on the windows form.
Others have suggested using a BackgroundWorker, or some other sort of background thread. While in many cases this is appropriate here, there is likely an even better solution. You're not just doing some long running task, you're waiting for something to happen. Rather than constantly polling...whatever it is, you should be using events. There should be an event that is triggered when you are done, and you should subscribe to that event to do whatever you need to do (i.e. close the dialog) when the correct conditions are met.
I have the following code, Some times ArchiveFiles() take more time to complete its execution.
When user clicks exit option more than one time from context menu, Application becomes non responding if ArchiveFiles() takes more time. How can I show a wait message when he clicks the exit option again?
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
ArchiveFiles();
Application.Exit();
}
Thanks
Bhaskar
You can use BackgroundWorker.
Using BackgroundWorker, you will be able to execute time consuming tasks such as the one you have on another thread so that the UI doesn't freeze. You will be able to report the progress of that task, then report it accomplishement and executing what ever logic you need after its completion.
Handle the BackgroundWorker.DoWork event to start the operation that performs the potentially time-consuming work.
Handle BackgroundWorker.ProgressChanged event to report the progress of an asynchronous operation to the user.
Finaly, handle BackgroundWorker.RunWorkerCompleted event to implement whatever logic you want to be implemented after the task has been completed.
Refer to the following:
BackgroundWorker Component Overview
C# BackgroundWorker Tutorial
Create a new WaitingForm and put an image control on the form and use the below .gif in that image control which which automatically animate. Then use the code below:
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
using (var wait = new WaitingForm())
{
wait.Show();
ArchiveFiles();
Application.Exit();
}
}
This may not be the best solution but it is the quickest. If you want the form to be a dialog, use wait.ShowDialog(); instead and carry ArchiveFiles(); Application.Exit();
functions inside it (if that is a probability).
You'd have to run ArchiveFiles asynchronously, by either using a thread or task, or making some delegate and using its BeginInvoke method.
You should use then some synchronization object like ManualResetEvent so the main thread don't continue executing, calling Application.Exit(). Or you could use some event to know when the operation finishes and then call Application.Exit() (or call it inside ArchiveFiles), but I find that to be worse.