WPF with UI threading issues - TaskFactory, CollectionView issues - syntax nightmare - wpf

See bottom of post for my pseudo solution.
Once again I'm completely and utterly stuck on this. I've burned hours trying to understand - and yes I can get a single collectionviewsource to work beautifully with nothing about threading on the code behind.
Imagine my shock when I found merely adding two collectionviewsources on the page causes threading issues. I've spent a few hours last night reading Async in C#5 and the MSDN stuff however I get into work today and I can't decipher how to make this happen.
The code below is the last attempt I've made before whining for help as I've burnt, possibly, a bit too much work time on attempting to understand how to do this. I understand that I need one collectionviewsource to complete before starting the other, so I tried Await Task.ContinueWith etc to try and chain one after the other.
Lining up both sets of tasks in the threads correctly seems to be quite tricky, or I'm still misunderstanding something fundemental.
If anyone can advise how they would asynchronously populate a few controls on a WPF UI I would be very grateful.
The application itself is a throwaway application, linked to an Access database that I'm using to try and become fluent enough in threading to implement it in our proper code base. I'm long way off that!
Updated with more complete code samples and the adjustments made according to answers:
Private Async Sub MainWindowLoaded(sender As Object, e As RoutedEventArgs) Handles MyBase.Loaded
InitializeComponent()
Dim personSetViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("personSetViewSource"), System.Windows.Data.CollectionViewSource)
Dim contactSetViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("contactSetViewSource"), System.Windows.Data.CollectionViewSource)
Dim personList = Await Task.Run(Function() personSet.personList)
personSetViewSource.Source = personList
Dim contactList = Await Task.Run(Function() contactSet.contactList)
contactSetViewSource.Source = contactList
End Sub`
The ObservableCollectionEx class:
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
using (BlockReentrancy())
{
NotifyCollectionChangedEventHandler collectionChanged = this.CollectionChanged;
if (collectionChanged != null)
foreach (NotifyCollectionChangedEventHandler nh in collectionChanged.GetInvocationList())
{
DispatcherObject dispObj = nh.Target as DispatcherObject;
if (dispObj != null)
{
Dispatcher dispatcher = dispObj.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
NotifyCollectionChangedEventHandler nh1 = nh;
dispatcher.BeginInvoke(
(Action) (() => nh1.Invoke(this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
nh.Invoke(this, e);
}
}
}
}
Please note, I can't translate this class to VB due to requiring an Event Override.
Another variation I've tried, but falls foul of thread ownership again. The two collectionviews thing isn't yielding to a solution: I don't know if it's because the underlying collection isn't good for it or whether in reality it wasn't meant to work that way. I get close but no cigar.
Dim CarePlanList = Task.Run(Function() CarePlanSet.CarePlanList)
Dim rcpdList = Task.Run(Function() rcpdSet.rcpdList)
Dim tasks() As Task = {CarePlanList, rcpdList}
Dim t = New TaskFactory
Await t.ContinueWhenAll(tasks, Sub()
carePlanSetViewSource.Source = CarePlanList
rcpdSetViewSource.Source = rcpdList
End Sub)
I found a way to do it, based on a combination of feedback and research this morning. Building the two collectionviews asynchronously itself is somewhat impractical given the STAThread model of WPF. However, merely ensuring one HAS completed and shifting some of the async out of one entity class has made this plausible.
Instead I fire off the first task, who's underlying class does build it's data with its own Async method. Then test to see if it has completed before allowing the second collectionview to be fired off. This way I don't need to worry about context or dispatcherobjects. The second collection does not use any async.
Dim personList = Task(Of List(Of person)).Run(Function() personSet.personList)
Dim contactList = Task(Of ObservableCollectionEx(Of contact)).Run(Function() contactSet.contactList)
contactSetViewSource.Source = contactList.Result
If contactList.IsCompleted Then personSetViewSource.Source = personList.Result
This is an experimental project for concept research really. As it happens, the idea I'd want two such lists built this way isn't as useful as all that but I do see where being able to compose a data heavy interface asynchronously could be handy.

Your two code samples each have issues that jump out right away.
In the first you are awaiting task1, I assume with more code following, but all task1 is doing is starting what is basically a fire and forget operation back to the UI thread (Dispatcher.BeginInvoke), therefore not really producing anything asynchronous to await.
In the second, the primary issue seems to be that you are doing a lot of setup of Task instances and chaining them with continuations but never starting the action2 Task, which appears to be the root of the whole chain, hence getting no activity at all. This is similar to what you get with a BackgroundWorker that never has RunWorkerAsync called.
To get this working properly and avoid making your head spin any more I would suggest starting by writing this whole block without any async and verifying that everything loads as expected, but with the UI lockup you want to avoid. Async/Await is designed to be added into code like that with minimal structural changes. Using Task.Run along with async and await you can then make the code asynchronous.
Here's some pseudocode for the basic pattern, without async to start:
PersonSetList = LoadData1()
CVS1.Source = PersonSetList
ContactList = LoadData2()
CVS2.Source = ContactList
and now adding async:
PersonSetList = await Task.Run(LoadData1())
CVS1.Source = PersonSetList
ContactList = await Task.Run(LoadData2())
CVS2.Source = ContactList
What this will now do is start a task to load the person data and immediately return from your WindowLoaded method, allow the UI to continue rendering. When that data is loaded it will continue to the next line on the original thread and push the data into the UI (which may itself slow down the UI while rendering). After that it will do the same for the Contact data. Notice that Dispatcher isn't needed explicitly because await is returning back to the UI thread for you to complete its continuation.

Related

WPF Application freezes for 20 seconds

I have a WPF (MVVM with Prism) application
There are quite a lot of factors that can affect this problem, but I will try to boil it down.
Hopefully I can at least get some tips how to trouble shoot this.
I have a user control containing a datagrid and a typical Search-button. The grid is initially empty and on SearchCommand. The user control uses a class "AccountServiceGateway" (_accountSG below) to make a request to the server, and then fills datasource of the grid with the result. Pretty standard.
VM: Binding command to handler in ctor
...
SearchCommand = new DelegateCommand(async () => await SearchOnServer(new AccountFilterDTO()));
VM, Button handler implementation
private async Task<bool> SearchOnServer(AccountFilterDTO filter)
{
var searchAccountResults = await _accountSG.SearchAccounts(filter);
//AccountSearchResultList is an observable collection that is datasource for the grid
AccountSearchResultList = new ObservableCollection<AccountSearchResultDTO>(searchAccountResults);
}
// Account Service gateway, making a web request
protected async Task<T> GetFromUrl<T>(string urlPart)
{
...
var response = await _httpClient.GetAsync(url);
resStr = await response.Content.ReadAsStringAsync();
//convert to T and return
}
EDIT
When I replace the implementation of GetFromUrl() with
await Task.Delay(5000)
return [hardcoded list of T]
everything works ok (although the hardcoded list is only 5 items)
END EDIT
Now to my problem. Getting an answer from the server taks about 1-2secs, as expected. I can follow the code until my datasource is filled. But then the GUI freezes for roughly (10-)20 seconds before anything is displayed, then efter the freeze, everything is as expected.
Other things to notice is that user control is in a Prism-region, within a Telerik RadTabbedWindow, so if this looks ok ith might be something else.
So my main question is, why does it hang for 20 seconds, I suspect there is some threading problem, but if it where a deadlock, wouldnt it hang forever? Any way to trouble shoot this?
Has the view changed in some way so that list virtualisation has been effectively turned off?
eg. Parent ScrollerViewer been added

Update DataContext from background thread

I fetch data for a wpf window in a backgroundthread like this [framework 4.0 with async/await]:
async void refresh()
{
// returns object of type Instances
DataContext = await Task.Factory.StartNew(() => serviceagent.GetInstances());
var instances = DataContext as Instances;
await Task.Factory.StartNew(() => serviceagent.GetGroups(instances));
// * problem here * instances.Groups is filled but UI not updated
}
When I include the actions of GetGroups in GetInstances the UI shows the groups.
When I update in a seperate action the DataContext includes the groups correclty but the UI doesn't show them.
In the GetGroups() method I inlcuded NotifyCollectionChangedAction.Reset for the ObservableCollection of groups and this doesn't help.
Extra strange is that I call NotifyCollectionChangedAction.Reset on the list only once, but is executed three times, while the list has ten items?!
I can solve the issue by writing:
DataContext = await Task.Factory.StartNew(() => serviceagent.GetGroups(instances));
But is this the regular way for updating DataContxt and UI via a backgound process?
Actually I only want to update the existing DataContext without setting it again?
EDIT: serviceagent.GetGroups(instances) in more detail:
public void GetGroups(Instances instances)
{
// web call
instances.Admin = service.GetAdmin();
// set groups for binding in UI
instances.Groups = new ViewModelCollection<Groep>(instances.Admin.Groups);
// this code has no effect
instances.Groups.RaiseCollectionChanged();
}
Here ViewModelCollection<T> inherits from ObservableCollection<T> and I added the method:
public void RaiseCollectionChanged()
{
var handler = CollectionChanged;
if (handler != null)
{
Trace.WriteLine("collection changed");
var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
handler(this, e);
}
}
There's a few points that stand out in the async portion of your code:
I explain why we should avoid async void in my MSDN article. In summary, void is an unnatural return type for async methods, so it has some quirks, particularly around exception handling.
We should prefer TaskEx.Run over StartNew for asynchronous tasks, as I explain on my blog.
While not exactly required, it's a good idea to follow the guidelines in the Task-based Asynchronous Pattern; following those naming conventions (etc) will help other developers to maintain the code.
Based on these, I also recommend my intro to async blog post.
On to the actual problem...
Updating data-bound code from background threads is always tricky. I recommend that you treat your ViewModel data as though it were part of the UI (it is a "logical UI", so to speak). So it's fine to retrieve data on a background thread, but updating the actual VM values should be done on the UI thread.
These changes make your code look more like this:
async Task RefreshAsync()
{
var instances = await TaskEx.Run(() => serviceagent.GetInstances());
DataContext = instances;
var groupResults = await TaskEx.Run(() => serviceagent.GetGroups(instances));
instances.Admin = groupResults.Admin;
instances.Groups = new ObservableCollection<Group>(groupResults.Groups);
}
public GroupsResult GetGroups(Instances instances)
{
return new GroupsResult
{
Admin = service.GetAdmin(),
Groups = Admin.Groups.ToArray(),
};
}
The next thing you need to check is whether Instances implements INotifyPropertyChanged. You don't need to raise a Reset collection changed event when setting Groups; since Groups is a property on Instances, it's the responsibility of Instances to raise INotifyPropertyChanged.PropertyChanged.
Alternatively, you could just set DataContext last:
async Task RefreshAsync()
{
var instances = await TaskEx.Run(() => serviceagent.GetInstances());
var groupResults = await TaskEx.Run(() => serviceagent.GetGroups(instances));
instances.Admin = groupResults.Admin;
instances.Groups = new ObservableCollection<Group>(groupResults.Admin.Groups);
DataContext = instances;
}
Seems there's a bit of confusion on what DataContext is. DataContext is not some special object that you have to update. It's a reference to the object or objects that you want to bind to your UI. Whenever you make changest to these objects, the UI get's notified (if you implement the proper interfaces).
So, unless you explicitly change the DataContext, your UI can't guess that now you want to show a different set of objects.
In fact, in your code, there is no reason to set the DataContext twice. Just set it with the final set of objects you want to display. In fact, since you work on the same data, there is no reason to use two tasks:
async Task refresh()
{
// returns object of type Instances
DataContext=await Task.Factory.StartNew(() => {
var instances = serviceagent.GetInstances();
return serviceagent.GetGroups(instances);
});
}
NOTE:
You should neer use the async void signature. It is used only for fire-and-forget event handlers, where you don't care whether they succeed or fail. The reason is that an async void method can't be awaited so no-one can know whether it succeeded or not.
I discovered that RaiseCollectionChanged has no influence on the property Groups where the DataContext is bound to. I simply have to notify: instances.RaisePropertyChanged("Groups");.

Replacing methods that use backgroundworker to async / tpl (.NET 4.0)

My questions are many. Since I saw. NET 4.5, I was very impressed. Unfortunately all my projects are .NET 4.0 and I am not thinking about migrating. So I would like to simplify my code.
Currently, most of my code that usually take enough time to freeze the screen, I do the following:
BackgroundWorker bd = new BackgroundWorker();
bd.DoWork += (a, r) =>
{
r.Result = ProcessMethod(r.Argument);
};
bd.RunWorkerCompleted += (a, r) =>
{
UpdateView(r.Result);
};
bd.RunWorkerAsync(args);
Honestly, I'm tired of it. And that becomes a big problem when there is a logic complex user interaction.
I wonder, how to simplify this logic? (Remember that I'm with. Net 4.0) I noticed a few things by google, but not found anything easy to implement and suitable for my needs.
I thought this solution below:
var foo = args as Foo;
var result = AsyncHelper.CustomInvoke<Foo>(ProcessMethod, foo);
UpdateView(result);
public static class AsyncHelper
{
public static T CustomInvoke<T>(Func<T, T> func, T param) where T : class
{
T result = null;
DispatcherFrame frame = new DispatcherFrame();
Task.Factory.StartNew(() =>
{
result = func(param);
frame.Continue = false;
});
Dispatcher.PushFrame(frame);
return result;
}
}
I am not sure about the impact is on manipulating the dispatcher frame.
But I know That it would work very well, for example, I could use it in all the events of controls without bothering to freeze the screen.
My knowledge about generic types, covariance, contravariance is limited, maybe this code can be improved.
I thought of other things using Task.Factory.StartNew and Dispatcher.Invoke, but nothing that seems interesting and simple to use. Can anyone give me some light?
You should just use the Task Parallel Library (TPL). The key is specifying the TaskScheduler for the current SynchronizationContext for any continuations in which you update the UI. For example:
Task.Factory.StartNew(() =>
{
return ProcessMethod(yourArgument);
})
.ContinueWith(antecedent =>
{
UpdateView(antecedent.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
Aside from some exception handling when accessing the antecedent's Result property, that's all there is too it. By using FromCurrentSynchronizationContext() the ambient SynchronizationContext that comes from WPF (i.e. the DispatcherSynchronizationContext) will be used to execute the continuation. This is the same as calling Dispatcher.[Begin]Invoke, but you are completely abstracted from it.
If you wanted to get even "cleaner", if you control ProcessMethod I would actually rewrite that to return a Task and let it own how that gets spun up (can still use StartNew internally). That way you abstract the caller from the async execution decisions that ProcessMethod might want to make on its own and instead they only have to worry about chaining on a continuation to wait for the result.
UPDATE 5/22/2013
It should be noted that with the advent of .NET 4.5 and the async language support in C# this prescribed technique is outdated and you can simply rely on those features to execute a specific task using await Task.Run and then execution after that will take place on the Dispatcher thread again automagically. So something like this:
MyResultType processingResult = await Task.Run(() =>
{
return ProcessMethod(yourArgument);
});
UpdateView(processingResult);
How about encapsulating the code that is always the same in a reusable component? You could create a Freezable which implements ICommand, exposes a property of Type DoWorkEventHandler and a Result property. On ICommand.Executed, it would create a BackgroundWorker and wire up the delegates for DoWork and Completed, using the value of the DoWorkEventHandler as event handler, and handling Completed in a way that it sets its own Result property to the result returned in the event.
You'd configure the component in XAML, using a converter to bind the DoWorkEventHandler property to a method on the ViewModel (I assume you've got one), and bind your View to the component's Result property, so it gets updated automatically when Result does a change notification.
The advantages of this solution are: it is reusable, and it works with XAML only, so no more glue code in your ViewModel just for handling BackgroundWorkers. If you don't need your background process to report progress, it could even be unaware that it runs on a background thread, so you can decide in the XAML whether you want to call a method synchronously or asynchronously.
A few months have passed, but could this help you?
Using async/await without .NET Framework 4.5

Force redraw before long running operations

When you have a button, and do something like:
Private Function Button_OnClick
Button.Enabled = False
[LONG OPERATION]
End Function
Then the button will not be grayed, because the long operation prevents the UI thread from repainting the control. I know the right design is to start a background thread / dispatcher, but sometimes that's too much hassle for a simple operation.
So how do I force the button to redraw in disabled state? I tried .UpdateLayout() on the Button, but it didn't have any effects. I also tried System.Windows.Forms.DoEvents() which normally works when using WinForms, but it also had no effect.
The following code will do what you're looking for. However I would not use it. Use the BackgroundWorker class for long time operations. It's easy to use and very stable.
Here the code:
public static void ProcessUITasks() {
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(delegate(object parameter) {
frame.Continue = false;
return null;
}), null);
Dispatcher.PushFrame(frame);
}
Here you will find a sample on how to use the BackgroundWorker.
InvalidateVisual(); #HCL is right... don't do this
Like you say, it is better to start use a background thread / dispatcher and keep the UI thread unblocked. Consider looking at the Reactive Extensions library from Microsoft for high level asynchronous ui programming
In Windows.Forms, you can Button.Refresh().
In Windows.Forms or WPF, you can yield to the message pump to let it redraw. Async/Await were designed to allow you to do this without the nastiness of HCL's answer.
Private Async Function Button_OnClick
Button.Enabled = False
Await Task.Yield
[LONG OPERATION]
End Function

Managing async service calls using Silverlight and Reactive Extensions

So I'm reading up on Rx and having a difficult time grokking it. I have a Silverlight app that needs to make say 6 calls to a specific service asynchronously. In the old days, we'd handle this by making the calls and querying the userState/token to match the response with the request since they're not guaranteed to return in the order we called them. However, I suspect Rx handles this in a far more elegant manner. But I can't get it to work. Here's what I have so far...
myCollection.Add(new myObject(1));
myCollection.Add(new myObject(2));
myCollection.Add(new myObject(3));
myCollection.Add(new myObject(4));
myCollection.Add(new myObject(5));
myCollection.Add(new myObject(6));
foreach (var myItem in myCollection)
{
var myObservable = Observable.FromEventPattern<MyServiceMethodCompletedEventArgs>
(
f => myServiceClient.MyServiceMethodCompleted += f,
f => myServiceClient.MyServiceMethodCompleted -= f
).Take(1).ObserveOn(SynchronizationContext.Current);
myObservable.Subscribe
(
s =>
{
if (s.EventArgs.Error == null)
{
myItem.MyProperty = s.EventArgs.Result;
}
}
);
myServiceClient.MyServiceMethodAsync(myItem);
}
I hope you can see what I'm trying to achieve here...
What I end up with is all of myObject's being set to the result of the first call that returns.
I'm sure it's something silly but I haven't been able to figure it out yet.
Thanks :)
Consider trying the Observable.FromAsyncPattern instead of Observable.FromEventPattern. There is a trick to using FromAsyncPattern in Silverlight (and the phone) because the BeginInvoke/EndInvoke pair are not exposed directly by the service proxy. However, if you use the interface for the service proxy rather than the service proxy itself, you can access the begin/end pattern:
IMyService svc = new myServiceClient();
var svcObservable = Observable.FromAsyncPattern<T, MyServiceResultArgs>
(svc.BeginMyServiceMethod, svc.EndMyServiceMethod);
Now, you can switch from using foreach (an anti-pattern with LINQ) to making your myCollection into an observable and SelectMany between the myCollection and the service request as follows:
var requestResult = from myItem in myCollection.ToObservable()
from result in svcObservable(myItem)
select new {myItem, result};
requestResult.Subscribe(result => result.myItem.myProperty = result.result);
One additional word of caution: If you use the FromAsyncPattern in silverlight this way, the result will come back on a background thread. You will need to take care teo delegate back to the dispatcher.
If you want to see this in action, check out the last 20 minutes or so of my Mix presentation at http://channel9.msdn.com/events/MIX/MIX11/EXT08.

Resources