I have a gui. When I press a button my gui interacts with a software. It takes some seconds. During this time I want a dialog box, pop up or some thing like that to appear infront of my gui which tells the user to wait (with a message). When interaction of gui with software finishes the pop up automatically closes and user can again normally interact with gui.
Is there any way to do that ?
The trick is to spin off a thread so as not to tie up the UI thread. This is typically achieved via a BackgroundWorker.
There's a walkthrough for setting all this up on codeplex. The loading form closes when the backgroundworker is complete.
Here is briefly how it can be done using the BackgroundWorker component.
Put a BackgroundWorker onto your Form, then in the button's Click handler show your popup indicator Form above the current form, and start your worker with its RunWorkerAsync method. Handle the workers DoWork event, and it the handler, run the long running task. Also handle the worker's completed event (not sure now how it's called exactly), and in that hide your popup form. You can track the operation result in the DoWork event eventargs (Result property), and also you can catch any exceptions during the long running task with the completed event eventarg's Error peroperty. The operation progress can be reported in the DoWork handler with the worker's ReportProgress method, and it can be catched in the GUI with the worker's corresponding event.
You could also set mouse cursor to wait before long running operation
this.Cursor = Cursors.WaitCursor;
and than back to normal, then it's finished
this.Cursor = Cursors.Default;
Related
I have a button and a Windows.Forms.Timers timer1. Both of them have some operations on database. Could couse any problem on Main thread if timer1 tick fired when button click is already operating ?
What is the Main thread response in this case ?
Both of them operates seperately or button click operation blocked by timer1 tick ?
If you aren't implementing any async operations, this cannot happen. When an event is raised on the UI thread, whether a Form Timer or a Button Press, the UI is locked for the duration (as the thread handling the rendering is busy doing something else). In the event where you click a button while a timer_tick is running, the UI won't begin processing the button_clicked event until the timer_tick event returns.
If you are implementing any await/async operations or running any background workers, then things get a little more complicated because the timer_tick event could be awaiting a result from a background thread and begin processing the button_clicked event before that has returned. However, unless you are implementing threading yourself this cannot happen.
This question already has answers here:
Async Progress Bar Update
(3 answers)
Closed 3 years ago.
I have a form (called Form1) and I have created a status strip (called toolStripProgressBar1), with a label and a progress bar. I have a function that is called when you press a button and gets some data and processes it, which works well.
I want to provide the user with some information as to its progress so I want to set the label and progress bar but I can't get it to set
private static async Task GetSurvey(string surid)
{
Form1.toolStripProgressBar1.Value = 10;
It will be a lot easier if you don't do it as static; doing the work in a method that has access to the form instance variables would make this quite trivial. Be aware though that your progress bar is (== should be) updated by the windowing thread, and most things like button click event handlers are actioned by that same thread. This means that you click your button and the thread that created the progressbar gets busy doing your long running code...
Now, you're running your task async and this is good, just make sure that you await any lengthy operations so that the windowing thread can hand off to a background process at that point and go back to its main job (updating the UI) otherwise you won't see your progress bar updating very effectively. Don't try to update form controls from code that is run on a different thread to the main windowing thread.
If you're not clear on how async/await works, visualise it that upon encountering await within some method block, the thread that is executing the code will create a background process to complete the await'd section, and it itself will travel back up the call stack til it reaches the first method not marked async and proceed from there. In practical terms in a windows forms app you want all your code that "does stuff" in response to a button click to be marked async so you can release the windowing thread out of your code context entirely and back to doing its job of keeping the UI responding, whenever you use await
An alternative way of doing this might be tonise a BackgroundWorker - you attach a DoWork event handler that does the work, and as part of this code it should regularly call the ReportProgress method, passing in an int percentage completion. The ProgressChanged event handler on the BGWorker is used to set the percentage bar/Uu elements -critically you need to know that DoWork runs on a thread that is NOT the windowing thread (and windows controls must not be accessed from a thread other than the thread that created them, usually the windowing thread) so we don't access the progress bar directly from DoWork- instead we call ReportProgress and that causes the ProgressChanged event to fire, and the BGWorker deliberately arranges things so the code in that event handler IS run on the windowing thread(actually the thread that creates the worker but this should be the same)
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 a main form that contains an edit control that occupies the entire form. There is another worker thread that constantly writes log messages to this edit control. Now I want to show a dialog box with just a cancel button while the main UI's edit control is displaying stuff. The problem is that the cancel dialog is non-responsive while the updates are happening behind it and I cannot click on the cancel button. Any idea on how to resolve it. I was thinking of creating another UI thread and show the cancel button from it. Any other alternatives?
EDIT#1
I should clarify that I already use a worker thread to do the work.
DisplayLogs() is in a seperate thread.
DisplayLogs() is called from other threads.
LogMessage is the delegate that points to the method UpdateMessage in main UI.
The control used is a TextBox. I have tried other controls like listview,
richtextboxsand, etc. still the same result.
Worker Thread
void DisplayLogs()
{
lock (this)
{
while (logQueue.Count > 0)
{
string logMessagemessage = logQueue.Dequeue();
LogMessage(string.Concat(logMessagemessage, Environment.NewLine));
}
}
}
Main UI
public void UpdateMessage( string message)
{
if (!txtLog.IsHandleCreated)
{
return;
}
if (txtLog.InvokeRequired)
txtLog.BeginInvoke( new UpdateLogDelegate( UpdateLog), message);
else
txtLog.AppendText(message);
}
The main solution is to offload the expensive code onto a background worker and leave your UI thread responsive for UI actions. Your form can then simply show a modal dialog or something.
MSDN - How to use a Background Worker
In this situation it's necessary to move the majority of the work to a new thread, and clear up the UI thread for cancel messages etc.
You are going about this backwards. The main thread should, in theory, always be available to accept user input. Anything that may block for extended periods of time (heavy computation, database access, network access) should be done in a background thread. The idea is to have the edit control's data being computed and populated by a background thread (BackgroundWorker objects work nicely here) so that the main thread is always available if the user clicks on the cancel button.
Your problem is that the your UI thread is ALWAYS busy. I am saying this assuming that the number of items in logQueue is quite large. The while loop doesn't quit till the queue is empty. So it keeps hitting the UI thread with request for updates.
Also the if (txtLog.InvokeRequired) is kind of pointless because you are always calling the method from a worker thread.
So, since a .net WinForm application has only a single UI, which in your case is too busy to process other notifications, the new window appears stuck (because the paint messages are stuck in the message queue and cannot be processed as it is already flooded with the text box update messages)
You could stick an Application.DoEvents inside your loop which will give the message loop some time to process the pending notifications. However this is kind of a hack, in my opinion, as the UI behavior is sometimes not predictable. It may lead to things like stuttering while moving a dialog, delayed responses to click events etc.
Another point, MessageBox.Show or a Form.ShowDialog (if this is what you are using for the cancel button) is a blocking call. The thread on which you show it WILL hang till you dismiss the dialog. Try Form.Show and set the parent property to the main form.
Another alternative is to add a timer and process only X notifications per Y seconds. This will give the UI thread some breathing room for performing other activities.
Is there any way to load window inside the background worker thread without using showdialog()? the background worker only terminate only after getting some input from the window. Here the issue is window shown but the button and other controls are not rendered even we don't have control over any of the window.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// acquire form
Acquire aq = new Acquire(Handle);
aq.Show();
do
{
// waiting for image
} while (!aq.isImageReady);
// doing Image operation
}
You can show the dialog before you start the background worker, and declare a volatile var that you can change/check in the background worker and keep it running until it will have your desired value, that will be achieved once the dialog is closed.
The problem might be because you're using the background thread to show the window instead of the main UI thread of the process. Winform controls either throw exceptions or behave incorrectly if they are not used on the proper thread. In this case, the problem might be that your main window is running on the main UI thread while this additional dialog window is being created and shown by a different thread.
Try raising an event from the background thread to let the UI know that it requires input from the user. The UI can then handle displaying the dialog and responding to the user's input by passing the data back to the background thread.
In order to prevent the background thread from proceeding any further, create a System.Threading.AutoResetEvent object (a WaitHandle) and call the WaitOne method on that object immediately after raising the event to notify the UI to show the dialog. When the UI responds to the user input by passing the data back to the background thread, that code can call the Set method on the AutoResetEvent object, allowing the background thread to proceed.