How to add cancellation support to async UI tasks? - wpf

I am looking into adding cancellation support to awaitable extension methods in the AsyncUI and WinRT XAML Toolkit libraries. It seems like the base Task class does not have a built-in Cancel() method or event and relies on cancellation tokens that my tasks would need to check for periodically. I think does not make sense in my UI scenarios, since my tasks are generated using the TaskCompletionSource and I would like to cancel the tasks on the UI thread in response to events instead of running a loop on a thread pool thread and checking for a token.
My tasks currently complete when a UI event happens - like a Storyboard completing or a button being clicked, so if I want to support cancellation - I need to respond to a cancellation request by unsubscribing from events and doing other cleanup like stopping an animation .
I am thinking of creating some sort of CancellableUITask base class instead of using the TaskCompletionSource, so I could expose a Cancel() method that the consumer of my task could call (e.g. to stop an awaited animation in response to a button click) and something like a virtual OnCancellationRequested() method that my storyboard awaiter task could override to perform cleanup.
Does it make sense? What would be the best way to accomplish what I am describing here?

Your tasks would not need to check periodically for cancellation - that is just the most common way of doing things. If your tasks are event-driven, then you can register a handler against the CancellationToken that will be called when it is cancelled. See CancellationToken.Register.
So your code might look something like this:
public Task DoSomethingAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var tcs = new TaskCompletionSource();
if (cancellationToken.CanBeCanceled)
{
// register for notification of cancellation
cancellationToken.Register(...);
}
// set up other handlers and what-not
return tcs.Task;
}

Related

SynchronizationContext and Async/Await in WinForms

I need help understanding why following Test deadlocks? I suspect it has something to do with WindowsFormsSynchronizationContext that is set as Current SyncronizationContext during WinForm control creation.
[TestMethod]
public async Task myTest()
{
//During this winForms control creation, WindowsFormsSynchronizationContext is set as Current SyncronizationContext
new SomeWinformControl();
//This statement deadlocks
await Task.Run(() => { });
}
(MSTest using .NET 4.5.2)
Here is more context to #HansPassant's answer:
It has everything to do with Application.Run(), invariably missing in
a unit test. It is the dispatcher loop that is implemented by Run()
that keeps async code going.
From https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
Your user interface also has a scheduler: the message pump. A
dedicated thread sits in a loop, monitoring a queue of messages and
processing each; that loop typically processes messages like mouse
events or keyboard events or paint events, but in many frameworks you
can also explicitly hand it work to do, e.g. the Control.BeginInvoke
method in Windows Forms, or the Dispatcher.BeginInvoke method in WPF.

wpf - is it a harmfull to call database from the xaml code-behind?

I am supposed to work on a wpf legacy application( and desktop app is a new beast for me).
I have read that consumming task should not be launched on the ui thread : but I find this following code in the code behind of a view :
bool isSearching = true;
try
{
Task<ProductSearchResult>.Factory
.StartNew(() => DBCatalogService.Search( search.Criteria, search.CriteriaPage, search.CriteriaResultByPage)
.ContinueWith(res => LoadResult(res, search.Criteria, search.CriteriaPage, search.CriteriaResultByPage),
TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(s => isSearching = false);
}
catch
{
...
}
I am wondering it will not cause any trouble.
I know that it's sounds weird to call the database directly from the view code behind, but I just want to know if it could freeze the ui thread or something like this.
Thank you for your advice on this matter.
My question is : does the sample code that I provided would block the UI thread and have to be considered harmfull or not ?
The call to the DBCatalogService.Search method will not block the UI thread since it is being invoked on a background thread using the task parallel library (TPL).
The call to the LoadResult method will however be executed on the UI thread once the task that calls the Search method has completed.
This is fine though since this method probably sets some properties of some UI elements based on the result of the search and you must do this on the UI thread. This is because WPF controls have thread affinity, meaning that a control can only be accessed on the thread on which it was originally created.
So no, the sample code you have provided should not be considered "harmfull" in terms of UI responsiveness assuming that the LoadResult doesn't perform any strange and potentially long-running operations.
If you block the UI thread (dispatcher thread) with a long-running operation such as a synchronous DB request, your application will be unresponsive until the thread is unblocked.
You can avoid this by either:
Doing the blocking/synchronous operation on another thread
Making the operation non-blocking/asynchronous
Both of the above
Using async/await can make your code read much like the synchronous form, but with asynchronous behaviour. It should be much clearer than the code sample you give in the question. However you need an async form of your search.
If you do use another thread, remember to dispatch back onto the UI thread if you have to update UI properties.

SQLDependency - does not fire while being busy

Is it normal behavior that a SQLDependency onchange event is not fired if its thread is too busy?
private void NotificationOnChanged(...)
{
// get database results
// simulate long process
Thread.Sleep(10000);
}
During the sleep i add a new row and the notification is lost after the sleep expires.
Must I spawn a new single thread to do the work and use a flag to detect if new notifications arrived to restart the it?
This behavior is an artifact of the implementation on the ADO.net notification listener. See http://rusanu.com/2008/01/04/sqldependencyonchange-callback-timing/.
The SqlDependency internal thread that posts the WAITFOR(RECEIVE) is not going to post another one until the callback returns. So you have to do as little processing as possible and return in the OnChange event. Definetely nothing blocking.
Alternatively you can use the lower level SqlNotificationRequest that lets you manage everyting, including the notification handling. But you'll have to manage everything. See http://technet.microsoft.com/en-us/library/ms190270(v=sql.105).aspx

Long term operation progress window

in WPF application I load a list of business objects by WCF to a listbox.
I want to load it in another thread and display a progressbar window.
But how? I need to call WCF service in the second thread and return its return value to the first (UI) thread. And this is the point I don't know. How to return?
Or what should be the correct workflow of calling for data at background and displaying of progress?
Now I:
- show the progress window
- create a new thread and call the WCF service
- return values <-- I don't know
- close window
But - is this workflow right?
I don't want to use a backgroundworker because I can call many different WCF services, not only one I could write into the DoWork method.
I have a WCFClient class in which I call all services I need. Here is a one of the methods (all methods are same only calls different services):
public class Client
{
public List<BusinessDto> GetBusinesses(int userID)
{
OnConnecting();
ServiceClient client = null;
BusinessDto[] dtos = null;
try
{
client = new ServiceClient();
dtos = client.GetBusinesses(userID);
}
catch
{
MessageBox.Show(Properties.Resources.ConnectionNotSucessful, Properties.Resources.ApplicationName, MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
if (client != null) client.Close();
OnClosing();
}
return dtos.ToList();
}
}
I'm catching an Onconnecting and OnClosing events of WCFClient and opening and closing a new window with progressbar.
But I do not call WCF services in a new thread, because I don't know how.
Thanks, I'm trying do that unsuccessfuly for a two days.
I don't want to use a backgroundworker because I can call many different WCF services, not only one I could write into the DoWork method.
Well, first, you can decide which of many DoWork methods you want to call at the time you prepare the BackgroundWorker. But you can also (and this is probably more maintainable) write a DoWork method of arbitrary complexity. You can write a DoWork method that takes an argument of type Client, and have it call a member method on that object, for instance. There's absolutely nothing limiting about this method.
The way I'd implement a progress bar window:
Implement a Task class that exposes three methods: SetUp, Execute, and TearDown, as well as a Status property, which is a struct containing int PercentComplete and string Message. Implement INotifyPropertyChanged.
Implement a protected UpdateStatus method that updates Status and raises PropertyChanged.
Build a modal window that implements a ShowDialog(Task t) method. In that method, call t.SetUp(), then create a BackgroundWorker.
Handle t.PropertyChanged and have the handler raise the BackgroundWorker's ProgressChanged event.
Have the BackgroundWorker's ProgressChanged event handler use t.Status to update the UI,
Have the BackgroundWorker's DoWork event handler call t.Execute().
Have its its RunWorkerCompleted event handler both handle exceptions (do not neglect this!) and call t.TearDown().
Build and test Task subclasses as needed.
Whenever you need to display a progress bar window, instantiate the appropriate Task, set whatever properties it needs, and then call ProgressBarWindow.ShowDialog(t). This will display the window and block and wait while the background process runs, updating the UI as it goes; it will return after t.TearDown() gets called.
BackgroundWorker is your friend. It does the thread marshalling for you, leaving you to worry about only doing the actual work.

Updating UI from a different thread

I know this question has been asked before, but I feel it wasn't asked correctly.
I have an intensive operation and I'd like the UI to remain responsive. I've read a few posts that say Background worker is the best way to go, however, I think this assumes you have the source code to the intensive task.
I have a library that I have no source code for, the only way I can check on the progress is to attach to events that get fired and get information that way.
The example I saw on the MSDN site assumed one would have the source.
I know how to get progress (which is a percentage value) by attaching to events, but how do I get that value back to the UI?
The following answer is based on my gut feeling and have not actually done it a test with third party libs.
Call your third party lib code as usual you call in a simple background (not BackGroundWorker) thread.
Attach the library components' events to normal event handlers in your code (meant to update UI).
In the event handler code should look like this:
private void EventHandler(object sender, DirtyEventArgs e)
{
if (myControl.InvokeRequired)
myControl.Invoke(new MethodInvoker(MethodToUpdateUI), e);
else
MethodToUpdateUI(e);
}
private void MethodToUpdateUI(object obj)
{
// Update UI
}
Attach to the progress events in the third party component and call ReportProgress on the BackgroundWorker. Have your UI attach to the BackgroundWorker.ProgressChanged event to update the UI.
You can have the desired effect by using a second thread and a thread safe queue.
You can create a second thread that will listen for the events.
When a new event happens it pushes the event information to a queue (thread safe- synchronized).
Using a Timer (Windows.Forms.Timer) that will check that queue every x time and in case new events exist can update the UI.
Because the timer runs in the main thread it can safely update the UI and if you make it light weight it will not block it long enough to be noticed.
A similar discussion is on this Q. If you can't or don't want to use the BackgroundWorker for whatever reason you can use your own thread and marshal events from it back onto your UI thread.
private void Initialise() {
MyLibClass myLibClass = new MyLibClass();
myLibClass.SomeEvent += SomeEventHandler;
ThreadPool.QueueUserWorkItem(myLibClass.StartLongTask);
}
private void SomeEventHandler(EventArgs e) {
if (this.Dispatcher.Thread != Thread.CurrentThread) {
this.Dispatcher.Invoke(delegate { DoStuffOnUIThread(e); });
}
else {
DoStuffOnUIThread(e);
}
}

Resources