Accessing ViewModel properties from separate thread - wpf

In my wpf application, a time consuming operation in my viewmodel is called using a separate thread. This function, however, accesses several properties in the viewmodel that are bound to objects in the view. I tried accessing them directly and I see no complaints on them being owned by the UI thread. I'm interested in knowing the consequences of using them directly between threads.

You are free to use your ViewModel from any thread - including reading and writing. The one main exception is dealing with collections - data bound collections must be written to on the user interface thread, as the binding doesn't automatically marshal to the UI thread (like simple bindings do).
However, you still should consider having proper synchronization in place for any writes. Normal thread synchronization issues will occur, as the ViewModel is just another class.
That being said, typically, you'll want to handle synchronization slightly differently than you would in many cases. Locks don't typically work on a ViewModel, as the WPF data binding will not lock the objects. As such, you should normally use Dispatcher.Invoke/BeginInvoke to marshal calls back to the user interface thread as needed when synchronization is required in the ViewModel.

There are no consequences aside from your usual thread safety concerns. The only thing that usually is problematic with VM properities are ObservableCollections which do have thread affinity.

If you use extend your ObservableCollection with this, you can update from a separate thread:
/// <summary>
/// Source: New Things I Learned
/// Title: Have worker thread update ObservableCollection that is bound to a ListCollectionView
/// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx
/// Note: Improved for clarity and the following of proper coding standards.
/// </summary>
/// <param name="e"></param>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
// Use BlockReentrancy
using (BlockReentrancy())
{
var eventHandler = CollectionChanged;
// Only proceed if handler exists.
if (eventHandler != null)
{
Delegate[] delegates = eventHandler.GetInvocationList();
// Walk thru invocation list
foreach (NotifyCollectionChangedEventHandler handler in delegates)
{
var currentDispatcher = handler.Target as DispatcherObject;
// If the subscriber is a DispatcherObject and different thread
if ((currentDispatcher != null) &&
(currentDispatcher.CheckAccess() == false))
{
// Invoke handler in the target dispatcher's thread
currentDispatcher.Dispatcher.Invoke(
DispatcherPriority.DataBind, handler, this, e);
}
else
{
handler(this, e);
}
}
}
}
}
/// <summary>
/// Overridden NotifyCollectionChangedEventHandler event.
/// </summary>
public override event NotifyCollectionChangedEventHandler CollectionChanged;

Related

C#: ObservableCollection - why virtual “CollectionChanged” event?

Why CollectionChanged event is virtual in ObservableCollection? We have virtual OnCollectionChanged method, which should be enough to override event call right?
I don't see any usage of this, and also virtual events are evil. ungainly usage of virtual events can bring a lot of logical issues, but however virtual events exists even in framework.
Is this just bad design or anyone use this in real-word?
We can debate about base classes and design, but here's a not direct/scholastic answer, but more of an example. I personally find it great that I could extend ObservableCollection and override OnCollectionChanged. ObservableCollection is very chatty, every time you add/remove items it bombards the UI thread with property changed messages and slows it down (in the datagrid, for example, every binding in it to be updated). So, as far as I know many people extend the ObservableCollection to suppress such notifications until they are done adding items. Just because WPF controls DataGrids/ListViews etc.. respond to CollectionChanged this works.
here's the usage, I refresh my data and instead of adding one item at a time, I populate a List then I reset the ObservableCollection with it just once which speeds up UI responsiveness enormously:
private void OnExecuteRefreshCompleted(IEnumerable<MyObject> result)
{
UiUtilities.OnUi(() => { _myObservableCollectionField.Reset(result, true);
});
here's my extended class:
public class ObservableCollectionExtended<T> : ObservableCollection<T>
{
private bool _suppressNotification;
//without virtual , I couldn't have done this override
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
public void Clear(bool suppressNotificationUntillComplete)
{
_suppressNotification = suppressNotificationUntillComplete;
Clear();
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ClearItems(bool suppressNotificationUntillComplete)
{
_suppressNotification = suppressNotificationUntillComplete;
base.ClearItems();
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void AddRange(IEnumerable<T> list, bool suppressNotificationUntillComplete)
{
if (list == null)
throw new ArgumentNullException("list");
_suppressNotification = suppressNotificationUntillComplete;
foreach (T item in list)
Add(item);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// clears old items, and new ones
/// </summary>
/// <param name="list"></param>
/// <param name="suppressNotificationUntillComplete"></param>
public void Reset(IEnumerable<T> list, bool suppressNotificationUntillComplete)
{
if (list == null)
throw new ArgumentNullException("list");
_suppressNotification = suppressNotificationUntillComplete;
Clear();
foreach (T item in list)
Add(item);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
I am afraid denis didn't answer the quesiton "Why CollectionChanged event is virtual?" but rather the question "Why OnCollectionChanged() method is virtual?". The first question is more properly answered by Jon. You may be interested in different handling of subscribers like I did some time ago.
Let's have two different scenarios:
First scenario:
I want to raise CollectionChanged event and be sure that any delegate that is invoked within this event (list of delegates), doesn't interrupt the invoking of the following delegates in case of an exception. In other words, an event is made of a list of delegates. If I have let's say 10 subscribers (delegates) and the 3rd delegate raises an exception, I may continue invoking the rest of delegates. Standard implementation interrupts invoking.
Second scenario:
I may want to let some subscribers be prioritized (they receive the event earlier) even if they do the subscription later than others. In "add" event I can move some specific subscribers lower or higher in my custom list of delegates which is later used to raise the event..

MVVM and collections of VMs

A common senario: A model with a collection of item models.
E.g a House with a collection of People.
How to structure this correctly for MVVM - particulary with regard to updating the Model and ViewModel collections with additions and deletes?
Model House contains a collection of model People (normally a List<People>).
View model HouseVM contains the House object that it wraps and an ObservableCollection of view model PeopleVM (ObservableCollection<PeopleVM>). Note that we end up here with the HouseVM holding two collections (that require syncing):
1. HouseVM.House.List<People>
2. HouseVM.ObservableCollection<PeopleVM>
When the House is updated with new People (add) or People leave (remove) that event must now be handled in both collections the Model House People collection AND the the VM HouseVM PeopleVM ObservableCollection.
Is this structure correct MVVM?
Is there anyway to avoid having to do the double update for Adds and Removes?
Your general approach is perfectly fine MVVM, having a ViewModel exposing a collection of other ViewModels is a very common scenario, which I use all over the place. I would not recommend exposing items directly in a ViewModel, like nicodemus13 said, as you end up with your view binding to models without ViewModels in between for your collection's items. So, the answer to your first question is: Yes, this is valid MVVM.
The problem you are addressing in your second question is the synchronization between the list of people models in your house model and the list of people ViewModels in your house ViewModel. You have to do this manually. So, no there is no way to avoid this.
What you can do: Implement a custom ObservableCollection<T>, ViewModelCollection<T>, which pushes it's changes to an underlying collection. To get two way synching, make the model's collection an ObservableCollection<> too and register to the CollectionChanged event in your ViewModelCollection.
This is my implementation. It uses a ViewModelFactory service and so on, but just have a look at the general principal. I hope it helps...
/// <summary>
/// Observable collection of ViewModels that pushes changes to a related collection of models
/// </summary>
/// <typeparam name="TViewModel">Type of ViewModels in collection</typeparam>
/// <typeparam name="TModel">Type of models in underlying collection</typeparam>
public class VmCollection<TViewModel, TModel> : ObservableCollection<TViewModel>
where TViewModel : class, IViewModel
where TModel : class
{
private readonly object _context;
private readonly ICollection<TModel> _models;
private bool _synchDisabled;
private readonly IViewModelProvider _viewModelProvider;
/// <summary>
/// Constructor
/// </summary>
/// <param name="models">List of models to synch with</param>
/// <param name="viewModelProvider"></param>
/// <param name="context"></param>
/// <param name="autoFetch">
/// Determines whether the collection of ViewModels should be
/// fetched from the model collection on construction
/// </param>
public VmCollection(ICollection<TModel> models, IViewModelProvider viewModelProvider, object context = null, bool autoFetch = true)
{
_models = models;
_context = context;
_viewModelProvider = viewModelProvider;
// Register change handling for synchronization
// from ViewModels to Models
CollectionChanged += ViewModelCollectionChanged;
// If model collection is observable register change
// handling for synchronization from Models to ViewModels
if (models is ObservableCollection<TModel>)
{
var observableModels = models as ObservableCollection<TModel>;
observableModels.CollectionChanged += ModelCollectionChanged;
}
// Fecth ViewModels
if (autoFetch) FetchFromModels();
}
/// <summary>
/// CollectionChanged event of the ViewModelCollection
/// </summary>
public override sealed event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
/// <summary>
/// Load VM collection from model collection
/// </summary>
public void FetchFromModels()
{
// Deactivate change pushing
_synchDisabled = true;
// Clear collection
Clear();
// Create and add new VM for each model
foreach (var model in _models)
AddForModel(model);
// Reactivate change pushing
_synchDisabled = false;
}
private void ViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Return if synchronization is internally disabled
if (_synchDisabled) return;
// Disable synchronization
_synchDisabled = true;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var m in e.NewItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Add(m);
break;
case NotifyCollectionChangedAction.Remove:
foreach (var m in e.OldItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Remove(m);
break;
case NotifyCollectionChangedAction.Reset:
_models.Clear();
foreach (var m in e.NewItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Add(m);
break;
}
//Enable synchronization
_synchDisabled = false;
}
private void ModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_synchDisabled) return;
_synchDisabled = true;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var m in e.NewItems.OfType<TModel>())
this.AddIfNotNull(CreateViewModel(m));
break;
case NotifyCollectionChangedAction.Remove:
foreach (var m in e.OldItems.OfType<TModel>())
this.RemoveIfContains(GetViewModelOfModel(m));
break;
case NotifyCollectionChangedAction.Reset:
Clear();
FetchFromModels();
break;
}
_synchDisabled = false;
}
private TViewModel CreateViewModel(TModel model)
{
return _viewModelProvider.GetFor<TViewModel>(model, _context);
}
private TViewModel GetViewModelOfModel(TModel model)
{
return Items.OfType<IViewModel<TModel>>().FirstOrDefault(v => v.IsViewModelOf(model)) as TViewModel;
}
/// <summary>
/// Adds a new ViewModel for the specified Model instance
/// </summary>
/// <param name="model">Model to create ViewModel for</param>
public void AddForModel(TModel model)
{
Add(CreateViewModel(model));
}
/// <summary>
/// Adds a new ViewModel with a new model instance of the specified type,
/// which is the ModelType or derived from the Model type
/// </summary>
/// <typeparam name="TSpecificModel">Type of Model to add ViewModel for</typeparam>
public void AddNew<TSpecificModel>() where TSpecificModel : TModel, new()
{
var m = new TSpecificModel();
Add(CreateViewModel(m));
}
}
In this situation I simply make the model expose ObservableCollections rather than Lists. There's no particular reason why it shouldn't. The ObservableCollection is in the System.Collections.ObjectModel namespace of the System assembly, so there's no unreasonable extra dependencies, you almost certainly have System anyway. List is in mscorlib, but that's as much a historical artefact as anything.
This simplifies the model-viewmodel interactions massively, I can't see a reason not to do it, using Lists on the model just creates lots of unpleasant boiler-plate code. You are interested in the events, after all.
Also, why is your HouseVM wrapping an ObservableCollection<PeopleVM>, rather than ObservableCollection<People>? VMs are for binding to views, so I would think that whatever is binding to your ObservableCollection<PeopleVM> is actually interested in People, otherwise you're binding-within-a-binding, or is there a specific reason why this is useful? I wouldn't generally have a VM expose other VMs, but maybe that's just me.
Edit about libraries/WCF
I don't see why having a model in a library, or even exposed by a WCF-server should affect whether they raise events or not, it seems perfectly valid to me (obviously the WCF-service won't expose the events directly). If you don't like this, I think you're stuck with having to chain multiple updates, though I wonder if you're actually just manually doing the same work as the event would do in an ObservableCollection, unless I've misunderstood some of it.
Personally, like I said, I'd keep the VMs simple, and have them expose the minimum and not expose other VMs. It can take some redesign and make certain parts a bit of a pain (e.g. Converters, however, you end up with a simple, easy-to-manage design with some simple-to-handle irritations on the edges.
It seems to me that your current route is going to end up very complex rather quickly and, most importantly, awkward to follow... However, YMMV, it's just my experience :)
Perhaps moving some of the logic to explicit services might help?

Unsubscribe EventAggregator events in ViewModels

I start using WPF with PRISM and MVVM. One problem I am facing is that I can’t find a good place / best practice to unsubscribe EventAggregator events formerly subscribed in the ViewModel. The following solution - calling Unsubscribe in the destructor – is much too late. It is just running with the next garbage collection.
public class ViewModel : ViewModelBase
{
public ViewModel()
{
var eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
eventAggregator.GetEvent<SeriesSelectionChangedEvent>().Subscribe(OnSeriesSelectionChanged);
}
~ViewModel()
{
var eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
eventAggregator.GetEvent<SeriesSelectionChangedEvent>().Unsubscribe(OnSeriesSelectionChanged);
}
void OnSeriesSelectionChanged(SeriesSelectionChangedEventArgs e)
{
}
}
It's up to you! If your application can notify ViewModel when it is no longer needed, so you should unsubscribe there.
For example, in our project we have IViewDisposeService. If view (or its model) needs deterministic finalization, it registers itself in IViewDisposeService when showing. Then the Core use the same service to notify registered views when they've been removed from regions.
Another way is to use commands. Your model expose command which must be invoked by a view when it is closing. ViewModel may use command handler to unsubscribe.
By the way, if you worry that EventAggregator will hold your ViewModel, it's not a problem, because Prism's EventAggregator use weak references.
Well sometime back, I also faced the same issue. Here's what we did (WPF App).
Create a new base class - DisposableUserControl : UserControl, IDisposable. This will contain the logic of disposing an user control. Code added in the end.
Replace all user control in your application with DisposableUserControl. like < app: DisposableUserControl .... > < / app.DisposableUserControl>
Add an OnDispose method (Virtual) in ViewModelBase which is called in Dispose () method of VM.Each ViewModel of your app should override this OnDispose method in which you'll unsubscribe your events. Something like-
OnDispose() { base.Dispose(); UnsubscribeEvent (abcEventSubscribername); }
Code
/// <summary>
/// Falg used to avoid calling dispose multiple times on same user control
/// </summary>
private bool isDisposed;
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// If disposing equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed. If disposing equals false, the method has been called by the
/// runtime from inside the finalizer and you should not reference
/// other objects, only unmanaged resources can be disposed.
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!this.isDisposed)
{
this.isDisposed = true;
if (disposing)
{
UtilityFunctions.DisposeChildDisposableUserControls(this);
if (this.DataContext != null && this.DataContext is IDisposable)
{
var parent = LogicalTreeHelper.GetParent(this);
if (parent == null || ((parent as FrameworkElement).DataContext != this.DataContext))
{
(this.DataContext as IDisposable).Dispose();
}
BindingOperations.ClearAllBindings(this);
this.DataContext = null;
}
}
}
}
You could have the View notify the ViewModel when it is unloaded (or in case of a Window when it is closed). Then in the Unloaded/Closed handler in the ViewModel you could unsubscribe. That is the way I do it in my application.

Why does my busy indicator work when I click but not for the default button?

Hard question to describe in one line so here's the detail.
I asked a question before about showing a busy indicator in a WPF window whilst doing some long-running action. The original question is here.
I followed some advice from a comment that suggested a kind of "DoEvents" style workaround. I know this isn't ideal but I only have a few hours to add busy indicators to about 50 windows and they all have lots of code on the UI thread that I do not have the time to modify to use background threads (I expect to be up all night sorting it without that additional headache).
So, after I set the "Busy" property in my viewmodels I run the following DoEvents style code:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Now, when I click the button that starts the long-running process, the wait indicator appears as I need it to and all is well. However, the button in question is the default button in the window. If I press Enter, the same command binding is used and I can see in debug mode that the code is visited in exactly the same way. However, the busy indicator doesn't show.
Can anyone suggest a workaround that will allow me to get some sleep tonight?
If you accept to use the "standard" Windows waiting cursor, you can simply set the Cursor property of your Window to Cursors.Wait when starting an operation and then to null at the end of the long-running operation (or use the Mouse.OverrideCursor).
I wrap this in an IDisposable class:
/// <summary>
/// Helper to display a wainting cursor for the whole application
/// </summary>
public class WaitingCursor : IDisposable {
private Cursor m_currentCursor;
public WaitingCursor() {
Enable();
}
/// <summary>
/// Sets the cursort to the waiting cursor
/// </summary>
public void Enable() {
m_currentCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
}
/// <summary>
/// Restores the cursor to the default one
/// </summary>
public void Disable() {
Mouse.OverrideCursor = m_currentCursor;
}
#region Implementation of IDisposable
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose() {
Disable();
}
#endregion
}
Then, one can simply write:
using (new WaitingCursor()) {
... // long-running op
}

WPF update binding in a background thread

I have a control that has its data bound to a standard ObservableCollection, and I have a background task that calls a service to get more data.
I want to, then, update my backing data behind my control, while displaying a "please wait" dialog, but when I add the new items to the collection, the UI thread locks up while it re-binds and updates my controls.
Can I get around this so that my animations and stuff keep running on my "please wait" dialog?
Or at least give the "appearance" to the user that its not locked up?
If i understand correctly, you already use a BackgroundWorker to retrieve the data, and that simply assigning this data to the ObservableCollection is locking up the UI.
One way to avoid locking up the UI is to assign the data to the ObservableCollection in smaller chunks by queuing multiple dispatcher methods. Between each method call, UI events can be handled.
the following would add one item on at a time, that's a bit extreme, but it illustrates the concept.
void UpdateItems()
{
//retrievedItems is the data you received from the service
foreach(object item in retrievedItems)
Dispatcher.BeginInvoke(DispatcherPriority.Background, new ParameterizedThreadStart(AddItem), item);
}
void AddItem(object item)
{
observableCollection.Add(item);
}
ObservableCollection will raise CollectionChanged events that will force UI to rebind data, measure, arrange and redraw. This might take a lot of time if you have many updates coming.
It is possible to make user think that UI is alive by splitting the job in small packages. Use Dispatcher from UI thread (any control has reference to it) to schedule collection update actions with 10-100 items (determine number by experiment, these just to support the idea).
Your background code might looks like this:
void WorkInBackground()
{
var results = new List<object>();
//get results...
// feed UI in packages no more than 100 items
while (results.Count > 0)
{
Application.Current.MainWindow.Dispatcher.BeginInvoke(
new Action<List<object>>(FeedUI),
DispatcherPriority.Background,
results.GetRange(0, Math.Min(results.Count, 100)));
results.RemoveRange(0, Math.Min(results.Count, 100));
}
}
void FeedUI(List<object> items)
{
// items.Count must be small enough to keep UI looks alive
foreach (var item in items)
{
MyCollection.Add(item);
}
}
I have a DLL which runs a worker thread and sends events back to the application - worked perfectly on windows forms, switched to WPF and everything stopped working. I've been smashing my head against a brick wall for 4 hours trying to get this to work. But the solution I ended up with, thanks to Microsoft's UI Thread Safe marshalling EnableCollectionSynchronization, gives a really clean implementation to solve this.
This Collection extends ObservableCollection and implements EnableCollectionSynchronization making these objects usable between WPF and also background workers.
Edit: Microsoft's docs say the following, so I'm going to assume that the object context sharing doesn't matter.
The context parameter is an arbitrary object that you can use to information known when you enable collection synchronization. Context can be null.
ThreadSafeCollection.cs
using System.Collections.ObjectModel;
using System.Windows.Data;
namespace NSYourApplication
{
/// <summary>
/// This ObservableCollection is thread safe
/// You can update it from any thread and the changes will be safely
/// marshalled to the UI Thread WPF bindings
/// Thanks Microsoft!
/// </summary>
/// <typeparam name="T">Whatever type of collection you want!</typeparam>
public class ThreadSafeCollection<T> : ObservableCollection<T>
{
private static object __threadsafelock = new object();
public ThreadSafeCollection()
{
BindingOperations.EnableCollectionSynchronization(this, __threadsafelock);
}
}
}
Example WindowViewModel
WindowViewModel.cs
namespace NSYourApplication
{
/// <summary>
/// Example View
/// BaseModelView implements "PropertyChanged" to update WPF automagically
/// </summary>
class TestViewModel : BaseModelView
{
public ThreadSafeCollection<string> StringCollection { get; set; }
/// <summary>
/// background thread implemented elsewhere...
/// but it calls this method eventually ;)
/// Depending on the complexity you might want to implement
/// [MethodImpl(MethodImplOptions.Synchronized)]
/// to Synchronize multiple threads to prevent chase-conditions,deadlocks etc
/// </summary>
public void NonUIThreadMethod()
{
// No dispatchers or invokes required here!
StringCollection.Add("Some Text from a background worker");
}
/// <summary>
/// Somewhere in the UIThread code it'll call this method
/// </summary>
public void UIThreadMethod()
{
StringCollection.Add("This text come from UI Thread");
}
/// <summary>
/// Constructor, creates a thread-safe collection
/// </summary>
public TestViewModel()
{
StringCollection = new ThreadSafeCollection<string>();
}
}
}
Usage in a listbox in a xaml window/control
MainWindow.xaml
<ListBox x:Name="wpfStringCollection" ItemsSource="{Binding StringCollection,Mode=OneWay}">
</ListBox>
use BackgroundWorker to accomplish this task. update the obsrvablecollection in the DoWork method
Use this:
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(UpdateData), value);
private void UpdateData(int value)
{
BindingSourceProperty = value;
}

Resources