tags:

views:

131

answers:

3

I have a model that updates itself in the background. Currently I'm using INotifyPropertyChanged on the Model, but I've been told thats a bad idea as the model should be UI independent.

Is there a prefered pattern for updating the ViewModel when the Model changes in the MVVM design pattern?

+1  A: 

the model should be UI independent.

...well the model certainly should not be aware of the View, which is why you would make your model (or ViewModel) implement INotifyPropertyChanged, so the View can bind to Properties on the Model (or VM), and let the framework's change notification inform the View of changes (so your model doesn't need to)

Of course, if you are making changes to the UI based on data from a background thread, you will need to safely dispatch them to the UI thread - you can either use a standard .net threading mechanism (like BackgroundWorker) or you can use WPF's Dispatcher class.

IanR
A: 

The purpose of the ViewModel is to allow the model to be ui independent.

Just let the viewmodel listen to an event from the model

public MyViewModel(IView view, IModel model) {
    model.SomeEvent += HandleSomeEvent;
    ....
}

If you want to send INotifyPropertyChanged you need switch to the uithread first.

(If your model lives longer than the viewmodel you should look at some weak reference event pattern to allow the GC to clean up the viewmodel)

adrianm
+1  A: 

Two comments:

  1. There's nothing inherently UI-specific about INotifyPropertyChanged. It's just an interface. You are free to implement it on a Model object, and there's nothing wrong with that.
  2. If WPF binds to a property (Foo) on the ViewModel and you fire off a PropertyChanged event even on another thread, WPF will actually call the getter of that property on the GUI thread so you don't have to deal with the Dispatcher! Caveat: If you do this, make sure your accessors for the property are thread-safe.

For instance:

class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    public MyViewModel(MyModel mdl)
    {
        mdl.PropertyChanged += 
            new PropertyChangedEventHandler(
                mdl_PropertyChanged);
        _mdl = mdl;
    }

    private MyModel _mdl = null;

    void mdl_PropertyChanged(object sender, 
        PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Foo")
        {
            this.Foo = _mdl.Foo;
        }
    }

    public int Foo
    {
        get
        {
            lock(_foo_Lock)
            {
                return _foo;
            }
        }
        set
        {
            lock(_foo_Lock)
            {
                _foo = value;
            }
            NotifyPropertyChanged("Foo");
        }
    }
    private readonly object _foo_Lock = new object();
    private int _foo = 0;
}

EDIT: I don't actually suggest using hard coded strings for your property names. Here's a helper class you can use to get the property name during construction using reflection. Then create an AbstractViewModel base class. You can then inherit from AbstractViewModel and implement properties like this:

    #region IsCheckable
    public bool IsCheckable
    {
        get
        {
            lock(m_IsCheckable_Lock)
            {
                return m_IsCheckable;
            }
        }
        protected set
        {
            bool fire = false;
            lock(m_IsCheckable_Lock)
            {
                if (m_IsCheckable != value)
                {
                    m_IsCheckable = value;
                    fire = true;
                }
            }
            if(fire)
            {
                NotifyPropertyChanged(m_IsCheckableArgs);
            }
        }
    }
    private readonly object m_IsCheckable_Lock = new object();
    private bool m_IsCheckable = false;
    static readonly PropertyChangedEventArgs m_IsCheckableArgs =
        NotifyPropertyChangedHelper.CreateArgs<MyViewModel>(o => 
                o.IsCheckable);
    #endregion
Scott Whitlock
This is how I've coded my objects. I feel assured enough to continue using this 'pattern'.
rozon