21

I just noticed that when changing bound properties in my ViewModel (MVVM) from a background worker thread I do not get any exceptions and the view is updated correctly. Does this mean I can safely rely on wpf databinding marshalling all changes in the ViewModel to the UI Thread? I think I have read somewhere that one should make sure (in the ViewModel) that INotifyPropertyChanged.PropertyChanged is fired on the UI thread. Has this changed in 3.5 or something?

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
bitbonk
  • 48,890
  • 37
  • 186
  • 278

2 Answers2

16

Yes for scalars, no for collections. For collections, you'll need a specialized collection that marshals for you, or manually marshal to the UI thread yourself via the Dispatcher.

You may have read that INotifyCollectionChanged.CollectionChanged must fire on the UI thread, because it's simply not true of INotifyPropertyChanged.PropertyChanged. Below is a very simple example that proves WPF marshals property changes for you.

Window1.xaml.cs:

using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        private CustomerViewModel _customerViewModel;

        public Window1()
        {
            InitializeComponent();
            _customerViewModel = new CustomerViewModel();
            DataContext = _customerViewModel;

            var thread = new Thread((ThreadStart)delegate
            {
                while (true)
                {
                    Thread.Sleep(2000);
                    //look ma - no marshalling!
                    _customerViewModel.Name += "Appended";
                    _customerViewModel.Address.Line1 += "Appended";
                }
            });

            thread.Start();
        }
    }

    public abstract class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class CustomerViewModel : ViewModel
    {
        private string _name;
        private AddressViewModel _address = new AddressViewModel();

        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        public AddressViewModel Address
        {
            get { return _address; }
        }
    }

    public class AddressViewModel : ViewModel
    {
        private string _line1;

        public string Line1
        {
            get { return _line1; }
            set
            {
                if (_line1 != value)
                {
                    _line1 = value;
                    OnPropertyChanged("Line1");
                }
            }
        }
    }
}

Window1.xaml:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <TextBox Text="{Binding Name}"/>
        <TextBox Text="{Binding Address.Line1}"/>
    </StackPanel>
</Window>
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • What about complex scalar types? What if I have a property in my ViewModel of type "MyCompositeObject" ? – bitbonk Aug 24 '09 at 10:42
  • Yes, changes anywhere along a property path will be correctly marshalled, unless there's a collection in that path. – Kent Boogaart Aug 24 '09 at 10:47
  • The first time I have read that propertychanges should be on the UI Thread, was here: http://blogs.msdn.com/dancre/archive/2006/07/23/676300.aspx – bitbonk Aug 24 '09 at 11:20
  • Well, what can I say? You can't believe everything you read. I added a sample that proves WPF marshals the notifications for you. – Kent Boogaart Aug 24 '09 at 11:56
  • Here's an example of a collection that marshals the `CollectionChanged` event to the UI thread : http://tomlev2.wordpress.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/ – Thomas Levesque Aug 24 '09 at 12:35
  • @Kent Boogaart. +1. However correct me if I'm wrong, but you should clarify that you can fire OnPropertyChanged in a view model with a non INotifyCollectionChanged.CollectionChanged- ie a vanilla List firing OnPropertyChanged... – RichardOD Feb 18 '11 at 22:40
  • Do any built-in collection types handle thread marshalling? (e.g. `ObservableCollection`) – Merlyn Morgan-Graham Aug 09 '11 at 04:07
  • 1
    @ThomasLevesque you changed the website correct link: https://thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/ – Piotr Golacki Jan 18 '22 at 14:47
1

I believe that with 2.0 and previous incarnations of .NET you would have received an InvalidOperationException due to thread affinity when executing the aforementioned example (link posted by bitbonk is dated 2006).

Now, with 3.5, WPF does appear to marshal background thread property changes onto the dispatcher for you.

So, in short, depends which version of .NET you're targetting. Hopefully that clears up any confusion.

One of my fellow Lab49'ers blogged about it here in 2007:

http://blog.lab49.com/archives/1166