I have a strange issue. I wonder whether it's a standard behavior of the .NET components, and how to handle this.
Our app is using galasoft mvvm light. I have a form with a tree view, which is getting the data via an asynchronous call. And why that asynchronous task is running, we're showing a progress bar to the user. I'm using ObservableCollection as a collection for my tree structure. Now the problem:
This piece of code gives us the info:
public Task<ObservableCollection<FillingTreeNode>> GetTreeStructureAsync(SyncSettings settings)
{
SearchRequest request = BuildRequest();
return searchService.SearchRecordsAsync(request).ContinueWithConversion(
records => new ObservableCollection<FillingTreeNode>(records
.Select(cabinet => new FillingTreeNode
{
IsChecked = false,
DisplayName = cabinet.Fields[Fields.CabinetName].Value,
Node = cabinet.AsFillingNode(FillingNodeType.Cabinet),
NumberOfNodes = SendXmlRequest(record),
Children = new ObservableCollection<FillingTreeNode>(GetChildren (record));
}
}
This is the task extension to convert the result to some new type:
public static Task<TNew> ContinueWithConversion<TOld, TNew>(this Task<TOld> task, Func<TOld, TNew> conversionAction)
{
return task.ContinueWith(completedTask => conversionAction(task.Result));
}
Now the issue. The data is loaded from the server, the UI (the progress bar) says that the data is loaded, and only after that SendXmlRequest(record) (which is a bit long to wait) begins to work! But i expect that it's already done. The user sees nothing until those functions are finished working
Do you know what is the cause of the problem? Can that be the behavior of the Observable collection? How can i fix it?
Thank in advance.
Related
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
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");.
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
I would like to track the progress of a download taking place on a separate thread. I know that System.Net.WebClient has a DownloadStringAsync method, but it doesn't work directly with the new TPL types (TaskFactory, Task, etc.).
Can progress be tracked using the HttpRequest and HttpResponse classes?
What's the best class for tracking progress? The less overhead the better.
Are there times when the size of the response is unknown, aka, progress can't be tracked?
What's the best way to synchronize with the UI thread whenever progress is made?
Most examples show Tasks updating the UI only after the entire task is complete. These examples use continuations taking a UI synchronization context that avoids needing to work with a Dispatcher directly.
The idea is to show a grid view (in WPF) with all the downloads with progress bars. I am going to adding new rows and updating progress bars all the time. I'm trying to avoid turning this code into a mess.
DownloadStringAsync and the other event methods work very well with TPL in .NET 4.0 (check for EAP and TPL). In general, TPL does support event async programming through the TaskCompletionSource. The Begin/EndXXX model (APM) is supported through the Task.FromAsync method. You can find a detailed description TPL and Traditional .NET Asynchronous Programming.
The ParallelExtensionExtras library has a set of WebClient extensions methods like DownloadStringTask that return a task which completes when the appropriate event is fired.
The following code will create a Task that will complete when download finishes:
public Task<string> DownloadStringTask(WebClient client,Uri uri)
{
var tcs = new TaskCompletionSource<string>();
client.DownloadStringCompleted += (o, a) => tcs.SetResult(a.Result);
client.DownloadStringAsync(uri);
return tcs.Task;
}
As for updating the UI, you can easily use the DownloadProgressChanged event to provide feedback,eg:
using (var client = new WebClient())
{
client.DownloadProgressChanged += (o, a) => Console.WriteLine("{0}",a.ProgressPercentage);
var task = DownloadStringTask(client,new Uri("http://www.stackoverflow.com"));
var write=task.ContinueWith(t => Console.WriteLine("Got {0} chars", t.Result.Length));
write.Wait();
Console.ReadKey();
}
If you use data binding to provide the progress values to your progress bars, you can just update the progress value properties. If you update the progress bars directly (not a good idea), you will have to marshal the call to the UI thread using the progress bar's dispatcher, eg. like this
void UpdateProgress(int percent)
{
if (progressBar1.CheckAccess())
progressBar1.Value = percent;
else
{
progressBar1.Dispatcher.Invoke(new Action(()=>UpdateProgress(percent)));
}
}
....
client.DownloadProgressChanged += (o, a) => UpdateProgress(a.ProgressPercentage);
I'm trying to implement my first application using the MVVM pattern. I've manged to get most things working, but now I'm facing a problem with the following (IMHO pretty common) scenario:
Pressing a Button (View) shall invoke a Method (Model). Using a ICommand (ViewModel) this is pretty easy. But what to do if a time consuming operation has to be executed?
My current solution required me to implement a WorkQueue class containing WorkQueueItems. The WorkQueue has a Thread associated with it which executes the WorkQueueItems. Each WorkQueueItem has a Name, a Status and a Progress which is updated during execution.
Each Window has its own WorkQueue - visualized as StatusBar.
My problem: How can a ViewModel find the appropriate WorkQueue? Do I have to pass the WorkQueue to each ViewModel I create (this would be really be annoying)? Or are there other mechanism I could use?
I'm not really familiar with RoutedCommands - tough the basic concept seems to go into this direction. What'd love to see is a solution where I can bind a WorkQueueItem to a Command/Event which then bubbles up to the containing Window where it is added to the Window's WorkQueue.
I also considered making WorkQueue a Singleton - but this only works if I only have one Window at a time.
With the later .Net Frameworks (4.0+) and WPF you can utilize the System.Threading.Tasks library to provide a lot of this work under the hood.
If say your Command on your needs to update a property on your View Model, but it has to wait for the information, you simply start a task to perform the IO:
this.FindDataCommand = new RelayCommand<string>(
/* ICommand.Execute */
value =>
{
Task.Factory
.StartNew<IEnumerable<Foo>>(() => FindData(value))
.ContinueWith(
task =>
{
this.foundData.Clear();
this.foundData.AddRange(task.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
},
/* ICommand.CanExecute */
value => !String.IsNullOrWhitespace(value));
Breaking this down into manageable parts, we're starting a new task which calls some method IEnumerable<Foo> FindData(string). This is the plain old boring synchronous code you've always written. Likely it already exists on your view model!
Next we tell the framework to start a new task when that one finishes using ContinueWith, but to do it on the WPF Dispatcher instead. This allows you to avoid the hassles of cross-thread problems with UI elements.
You can extend this for monitoring with a helper class:
public class TaskManager
{
private static ConcurrentDictionary<Dispatcher, TaskManager> _map
= new ConcurrentDictionary<Dispatcher, TaskManager>();
public ObservableCollection<WorkItem> Running
{
get;
private set;
}
public TaskManager()
{
this.Running = new ObservableCollection<WorkItem>();
}
public static TaskManager Get(Dispatcher dispatcher)
{
return _map.GetOrAdd(dispatcher, new TaskManager());
}
// ...
Using this class in XAML would be along the lines of adding its instance to your Window's ViewModel:
public TaskManager CurrentTaskManager
{
get { return TaskManager.Get(Dispatcher.CurrentDispatcher); }
}
// <StatusBarItem Content="{Binding CurrentTaskManager.Running.Count}" />
You would then add a method to your TaskManager to handle the adding of tasks to and from the Running collection:
public Task<TResult> StartNew<TResult>(Func<TResult> work)
{
var task = Task.Factory
.StartNew<TResult>(work);
// build our view model
var workItem = new WorkItem(task);
this.Running.Add(workItem);
// Pass the result back using ContinueWith
return task.ContinueWith(
t => { this.Running.Remove(workItem); return t.Result; },
TaskScheduler.FromCurrentSynchronizationContext());
}
Now we simply change our FindDataCommand implementation:
TaskManager.Get(Dispatcher.CurrentDispatcher)
.StartNew<IEnumerable<Foo>>(() => FindData(value))
.ContinueWith(
task =>
{
this.foundData.Clear();
this.foundData.AddRange(task.Result);
},
TaskScheduler.FromCurrentSynchronizationContext());
The WorkItem class could expose the properties on the Task class to the UI, or it could be extended to encapsulate a CancellationToken to support cancellation in the future.
I'm not sure I got the question right, but I feel that using buil in Dispatcher would solve your problem and you do not need implementing WorkQueue manually since Dispatcher implements such a queue for you and able dispatching "worker items" to the UI/any thred using predefined set of priorities. You can execute an operation either synchronously or asynchronously using Dispatcher.Invoke() or Dispatcher.BeginInvoke()
Useful links:
MSDN Magazine: WPF Threads, Build More Responsive Apps With The Dispatcher