C#: Can I minimize app in BackgroundWorker running & update UI - c#

I have a backgroundWorker thread running. At the end I update the UI, minimize the app & change the icon.
I tried to minimize the app in middle, but then the UI wasn't updated, nor the icon was changed. Thread was executed but this part wasn't done.
Can't the UI get updated when the app is minimized ? If can, where I may be going wrong.
UPDATED :
#Daniel, YesI am aware of it. And when the task is done I call ProgressChanged is called and finally RunWorkerCompleted is also called. BUT when I minimize the app in middle, the components like Label's aren't changed. Button text is changed, but it hangs/stucks - button doesn't work any longer. MY point is : If app is minimized during executing DoWork, then when ProgresChanged & RunCompleted is called or not. If called then why certain components aren't changed. NOTE: If I don't minimize in middle, then everything works perfectly fine.

You can only update the UI from the UI thread. The ProgressChanged and RunWorkerCompleted events are run on the UI thread, the DoWork event is not. This means, if you want to update your UI from a BackgroundWorker while it is running, you need to do it in the ProgressChanged event handler.

Are you using WPF or Winforms ?
In any case, if you want to do something with the UI, you will have to come back into the UI thread.
To do so, in WPF, you can use the dispatcher object, and invoke on it, so the method will be executed by the UI thread, and not by your worker thread.
In Winforms, you will have to maintain a reference to some UI control, and invoke on this control.
Consider the following code snippet (WPF) :
if (!myCheckBox.Dispatcher.CheckAccess())
{
myCheckBox.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
myCheckBox.IsChecked = true;
}
));
}
else
{
myCheckBox.IsChecked = true;
}
Here is another code snippet I use in Winforms, to raise my events from my UI thread
Friend Shared Sub RaiseUiEvent(ByVal hnd As EventHandler, ByVal sender As Object, ByVal e As EventArgs)
Dim uiRef = GlobalManager.GetInstance().UI
Try
uiRef.BeginInvoke(hnd, sender, e)
Catch ex As InvalidOperationException
''if UiRef is null, then we simply drop the event
Logger.AddTextToLog(ex)
Debug.WriteLine("Invalid operation in raiseUiEvent")
Catch ex As NullReferenceException
Logger.AddTextToLog(ex)
Debug.WriteLine("Null Reference in RaiseUiEvents")
End Try
End Sub

Related

BackgroundWorker process needs to raise custom events to be handled on the main (UI) thread

I have a parser class for large CSV files. The parse method's work of reading through the large files line by line is done in a backgroundWorker. The percentage complete information is passed to the UI thread using the backgroundWorker.ReportProgress method, so that a progress bar on my form can do its thing. That's working fine.
However, I would also like to raise a custom event that sends back to the UI (WPF) a list of fieldnames taken from the first line of the CSV file, so that they can be placed in a dropdown list. I would also like to inform the user via an event if the parser should encounter malformed lines or other roadblocks.
Can my parser process executing in the background simply raise an event? Or must the SynchronizationContext.Current be passed from the main UI thread to my parser class, which would then use the Post method?
If you absolutely must schedule work on the UI thread from within your DoWork handler, the simplest way to do this is by using Dispatcher.Invoke:
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
var worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
// Suppose we've done some processing and need to notify the UI.
// We're on the background thread, so we can't manipulate the UI directly.
this.Dispatcher.Invoke(new Action(() =>
{
// This will actually run on the UI thread.
this.Label.Content = "hello from the background thread.";
}));
};
worker.RunWorkerAsync();
}
}
There seems to be a lot of confusion about events and their magical ability to do thread synchronization, so allow me to rant for a bit.
Events are glorified multicast delegates. When you raise an event, each delegate in its invocation list is called, on the thread which raised the event. Therefore, if you create an event in your custom parser class just to have it raised from the DoWork handler, the handlers for the event will still execute on the background thread, and you will still need to find a way to switch to the UI synchronization context - either by performing some Invoke/SynchronizationContext.Post/Send magic inside the handler of the new event, OR by Invoking/Posting/Sending the actual event raise logic.
The reason that handlers for out-of-the-box events such as RunWorkerCompleted and ProgressChanged run on the UI thread is that these events are actually raised on the UI thread by the BackgroundWorker for your convenience. And yes, you can produce similar behaviour by capturing the SynchronizationContext and then Posting/Sending the event raise logic in your custom parser class to it. Alternatively, if you choose to raise the event on the background thread, you can always use Dispatcher.Invoke inside its handler in your WPF component.

Gui busy pop up

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;

Start long process after modal dialog popup

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.

How to call Application.Exit on an non UI Thread

I have a UI design question.
I would like to exit the application when it encounter an exception on a non-UI thread.
Basically, the event goes like this:
Main Form -> ShowDialog of sub WinForm (MainThread)-> Starts a background thread (WorkerThread) -> Exception occurs -> Show an ErrorForm (WorkerThread)
When the user click Exit button on the ErrorForm, i want to exit the entire application. However, doing the following call doesn't work.
Invoker.Invoke((Action)(() => { Application.Exit(); }), null);
The Invoker reference to the main form SynchronizedContext. However, since the MainThread is still waiting for the subWinForm to return its control, it probably can't handle the Application.Exit().
What would be a better design to handle exception that is thrown by a background worker thread?
Cancel the background worker and send an argument to the BackgroundWorker RunWorkerCompletedEvent to identify there is an exception. After that call the Application.Exit() from there would be fine.
I know that invoking like this works in Silverlight:
Dispatcher.BeginInvoke(() => Application.Exit());
Or if there's no Dispatcher for your WinForms classes:
Invoker.BeginInvoke(() => Application.Exit());
You had a lot of extra unnecessary code ((Action), null, unnecessary brackets and parentheses). I don't think it would have stopped it from working correctly, but in any case, it's easier to read like this.

BackgroundWorker OnWorkCompleted throws cross-thread exception

I have a simple UserControl for database paging, that uses a controller to perform the actual DAL calls. I use a BackgroundWorker to perform the heavy lifting, and on the OnWorkCompleted event I re-enable some buttons, change a TextBox.Text property and raise an event for the parent form.
Form A holds my UserControl. When I click on some button that opens form B, even if I don't do anything "there" and just close it, and try to bring in the next page from my database, the OnWorkCompleted gets called on the worker thread (and not my Main thread), and throws a cross-thread exception.
At the moment I added a check for InvokeRequired at the handler there, but isn't the whole point of OnWorkCompleted is to be called on the Main thread? Why wouldn't it work as expected?
EDIT:
I have managed to narrow down the problem to arcgis and BackgroundWorker. I have the following solution wich adds a Command to arcmap, that opens a simple Form1 with two buttons.
The first button runs a BackgroundWorker that sleeps for 500ms and updates a counter.
In the RunWorkerCompleted method it checks for InvokeRequired, and updates the title to show whethever the method was originaly running inside the main thread or the worker thread.
The second button just opens Form2, which contains nothing.
At first, all the calls to RunWorkerCompletedare are made inside the main thread (As expected - thats the whold point of the RunWorkerComplete method, At least by what I understand from the MSDN on BackgroundWorker)
After opening and closing Form2, the RunWorkerCompleted is always being called on the worker thread. I want to add that I can just leave this solution to the problem as is (check for InvokeRequired in the RunWorkerCompleted method), but I want to understand why it is happening against my expectations. In my "real" code I'd like to always know that the RunWorkerCompleted method is being called on the main thread.
I managed to pin point the problem at the form.Show(); command in my BackgroundTesterBtn - if I use ShowDialog() instead, I get no problem (RunWorkerCompleted always runs on the main thread). I do need to use Show() in my ArcMap project, so that the user will not be bound to the form.
I also tried to reproduce the bug on a normal WinForms project. I added a simple project that just opens the first form without ArcMap, but in that case I couldn't reproduce the bug - the RunWorkerCompleted ran on the main thread, whether I used Show() or ShowDialog(), before and after opening Form2. I tried adding a third form to act as a main form before my Form1, but it didn't change the outcome.
Here is my simple sln (VS2005sp1) - it requires
ESRI.ArcGIS.ADF(9.2.4.1420)
ESRI.ArcGIS.ArcMapUI(9.2.3.1380)
ESRI.ArcGIS.SystemUI (9.2.3.1380)
Isn't the whole point of OnWorkCompleted is to be called on the Main thread? Why wouldn't it work as expected?
No, it's not.
You can't just go running any old thing on any old thread. Threads are not polite objects that you can simply say "run this, please".
A better mental model of a thread is a freight train. Once it's going, it's off on it's own track. You can't change it's course or stop it. If you want to influence it, you either have to wait til it gets to the next train station (eg: have it manually check for some events), or derail it (Thread.Abort and CrossThread exceptions have much the same consequences as derailing a train... beware!).
Winforms controls sort of support this behaviour (They have Control.BeginInvoke which lets you run any function on the UI thread), but that only works because they have a special hook into the windows UI message pump and write some special handlers. To go with the above analogy, their train checks in at the station and looks for new directions periodically, and you can use that facility to post it your own directions.
The BackgroundWorker is designed to be general purpose (it can't be tied to the windows GUI) so it can't use the windows Control.BeginInvoke features. It has to assume that your main thread is an unstoppable 'train' doing it's own thing, so the completed event has to run in the worker thread or not at all.
However, as you're using winforms, in your OnWorkCompleted handler, you can get the Window to execute another callback using the BeginInvoke functionality I mentioned above. Like this:
// Assume we're running in a windows forms button click so we have access to the
// form object in the "this" variable.
void OnButton_Click(object sender, EventArgs e )
var b = new BackgroundWorker();
b.DoWork += ... blah blah
// attach an anonymous function to the completed event.
// when this function fires in the worker thread, it will ask the form (this)
// to execute the WorkCompleteCallback on the UI thread.
// when the form has some spare time, it will run your function, and
// you can do all the stuff that you want
b.RunWorkerCompleted += (s, e) { this.BeginInvoke(WorkCompleteCallback); }
b.RunWorkerAsync(); // GO!
}
void WorkCompleteCallback()
{
Button.Enabled = false;
//other stuff that only works in the UI thread
}
Also, don't forget this:
Your RunWorkerCompleted event handler should always check the Error and Cancelled properties before accessing the Result property. If an exception was raised or if the operation was canceled, accessing the Result property raises an exception.
It looks like a bug:
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=116930
http://thedatafarm.com/devlifeblog/archive/2005/12/21/39532.aspx
So I suggest using the bullet-proof (pseudocode):
if(control.InvokeRequired)
control.Invoke(Action);
else
Action()
The BackgroundWorker checks whether the delegate instance, points to a class which supports the interface ISynchronizeInvoke. Your DAL layer probably does not implement that interface. Normally, you would use the BackgroundWorker on a Form, which does support that interface.
In case you want to use the BackgroundWorker from the DAL layer and want to update the UI from there, you have three options:
you'd stay calling the Invoke method
implement the interface ISynchronizeInvoke on the DAL class, and redirect the calls manually (it's only three methods and a property)
before invoking the BackgroundWorker (so, on the UI thread), to call SynchronizationContext.Current and to save the content instance in an instance variable. The SynchronizationContext will then give you the Send method, which will exactly do what Invoke does.
The best approach to avoid issues with cross-threading in GUI is to use SynchronizationContext.

Categories