7

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.

H.B.
  • 166,899
  • 29
  • 327
  • 400
Aks
  • 5,188
  • 12
  • 60
  • 101

3 Answers3

6

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.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • As an e.g., if I have a `ToggleButton` bound to a `bool` in the VM. And I'm doing a `if(bool)` in a separate thread, what would I have to do to maintain synchronization here so that there's no conflict between the user setting the toggle and the if condition executing. – Aks Dec 15 '11 at 17:51
  • @Aks No. If you're just reading a value like that, you'll have no issues. That being said, you should probably (technically) mark the bool volatile. For details, see: http://stackoverflow.com/questions/458173/can-a-c-sharp-thread-really-cache-a-value-and-ignore-changes-to-that-value-on-ot/458193#458193 – Reed Copsey Dec 15 '11 at 17:56
1

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.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • If I were to actually use locks, isn't it likely the UI thread hangs? – Aks Dec 15 '11 at 17:42
  • This is true - but "usual thread safety concerns" tend to require unusual workarounds, as WPF will ignore any locks or standard thread synchronization you try to use. – Reed Copsey Dec 15 '11 at 17:46
0

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;
Xcalibur37
  • 2,305
  • 1
  • 17
  • 20