tags:

views:

484

answers:

2

I'm just playing around with WPF and MVVM, and I have made a simple app that displays a Rectangle that changes color whenever Network availability changes.

But when that happens, I get this error: Cannot use a DependencyObject that belongs to a different thread than its parent Freezable.

Code

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="400" Width="600">
<DockPanel LastChildFill="True">
    <Rectangle x:Name="networkStatusRectangle" Width="200" Height="200" Fill="{Binding NetworkStatusColor}" />
</DockPanel>
</Window>

Code-behind

using System.Windows; using WpfApplication1.ViewModels;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            DataContext = new NetworkViewModel();
        }
    }
}

ViewModel

using System.ComponentModel;
using System.Net.NetworkInformation;
using System.Windows.Media;

namespace WpfApplication1.ViewModels
{
    public class NetworkViewModel : INotifyPropertyChanged
    {
        private Brush _NetworkStatusColor;

        public Brush NetworkStatusColor
        {
            get { return _NetworkStatusColor; }
            set
            {
                _NetworkStatusColor = value;
                NotifyOfPropertyChange("NetworkStatusColor");
            }
        }

        public NetworkViewModel()
        {
            NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
        }

        protected void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
        {
            if (e.IsAvailable)
            {
                this.NetworkStatusColor = new SolidColorBrush(Colors.Green);
            }
            else
            {
                this.NetworkStatusColor = new SolidColorBrush(Colors.Red);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        public void NotifyOfPropertyChange(string propertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

I assume that I should change the NetworkStatusColor property by invoking something?

+2  A: 

You assume correctly. It's the Dispatcher class and the .Invoke method you want to take a look at.

Something a bit like this:

if (this.Dispatcher.Thread != Thread.CurrentThread)
{
    this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(...your method...), any, params, here);
    return
}

There's an MSDN article here with some more info.

Simon P Stevens
Cool. So should you always have your ViewModels derive from the DispatcherObject class?
MartinHN
@MartinHN: No, sorry. I wouldn't have my viewmodel inherit from DispatcherObject. That code is a if you are running from within a view/window class. If you want to do this from within a viewmodel you can just pass the Dispatcher from the view when the viewmodel is bound (or created). Or you can use Dispatcher.CurrentDispatcher if your viewmodels are created on the main thread. Take a look at this question for some other suggestions for getting access to the window's dispatcher from the viewmodel: http://stackoverflow.com/questions/2354438/mvvm-best-practice-to-pass-dispatcher-to-the-viewmodel
Simon P Stevens
Although, if you are going to use DependencyProperties on your view model, you will end up inheriting from DispatcherObject indirectly in the hierarchy anyway (it's a parent class of DependencyObject), so I suppose it depends on what how you're doing things.
Simon P Stevens
MartinHN
A: 

With MVVM you have a couple of options when dealing with dispatching. Either you can send some kind of message to your view to have it invoke the operation for you, or you can create some kind of abstract dispatcher service that you are able to easily mock.

Take a look at the MVVM Light toolkit, as it includes a simple dispatcher-service you can use/copy.

Fara