Two comments:
- 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.
- 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