I ran in to an issue using custom complex classes in my model, that wouldn’t notify the View Model that something had changed. I didn’t like the idea of building everything as a one to one relationship between my View, View Model and Model just to get the UI to stay current. I prefer instead to create a single item, that is a collection or unit of my complex class.
here’s an example of the simple item model and it’s more complex element.
Model for SimpleItem
public class SimpleItem
{
private static FancyItem _item = new FancyItem();
public static FancyItem Item
{
get { return _item; }
set { _item = value; }
}
}
Model for FancyItem
public class FancyItem
{
private int _index = 0;
public int Index
{
get { return _index; }
set { _index = value; }
}
private string _name = string.Empty;
public string Name
{
get { return _name; }
set { _name = value; }
}
private string _label = string.Empty;
public string Label
{
get { return _label; }
set { _label = value; }
}
private DateTime _date = new DateTime();
public DateTime Label
{
get { return _date; }
set { _date = value; }
}
}
Then my ViewModel is something like this:
public FancyItem myFancyItem
{
set
{
if (SimpleItem.Item != value)
{
SimpleItem.Item = value;
InvokePropertyChanged("MyFancyItem");
}
}
get { return SimpleItem.Item; }
}
Then if my binding is something like this:
<TextBox Text="{Binding myFancyItem.Name, Mode=TwoWay}">
My PropertyChanged will never get called since I’m targeting down to the Name item. If I had an element that was directly exposing the Name string in the ViewModel then everything would work just fine. However, then I would have to duplicate all those lines of code for each variable that I want to expose and modify.
This is where my ModelBase event notification comes in to play.
public class ModelBase
{
public void ModelPropertyChanged(string p, EventHandler ModelItemUpdated)
{
ModelItemUpdated(p, EventArgs.Empty);
}
}
The idea with the ModelBase event, is pretty much the same as the View Model PropertyChangedEventHandler. The only challenge I’ve run in to is having to remember to feed it all the way of the food chain.
So here’s how I use it all together:
First I add some events to the parts that can be changed.
public class FancyItem : ModelBase
{
public static event EventHandler FancyItemUpdated = delegate { };
private int _index = 0;
public int Index
{
get { return _index; }
set
{
_index = value;
}
}
private string _name = string.Empty;
public string Name
{
get { return _name; }
set
{
_name = value;
ModelPropertyChanged("Name", FancyItemUpdated);
}
}
private string _label = string.Empty;
public string Label
{
get { return _label; }
set { _label = value; }
}
private DateTime _date = new DateTime();
public DateTime Date
{
get { return _date; }
set
{
_date = value;
ModelPropertyChanged("Date", FancyItemUpdated);
}
}
}
Then I add a listener to my simple model that fires off it’s own update event when something changes in the fancy model event it’s subscribed to.
public class SimpleItem : ModelBase
{
public static event EventHandler SimpleItemUpdated = delegate { };
public SimpleItem()
{
FancyItem.FancyItemUpdated +=new EventHandler(FancyItem_FancyItemUpdated);
}
void FancyItem_FancyItemUpdated(object sender, EventArgs e)
{
ModelPropertyChanged("updated", SimpleItemUpdated);
}
private static FancyItem _item = new FancyItem();
public static FancyItem Item
{
get { return _item; }
set { _item = value; }
}
}
Then on the ViewModel, it subscribes to the SimpleItem model event and then notifies the PropertyChanged event.
public class SimpleViewModel : ViewModelBase
{
public static readonly AudioViewModel Instance = new AudioViewModel();
public SimpleViewModel()
{
// to subscribe to the event in the model we need to call the contstructor
SimpleItem mySimpleItem = new SimpleItem();
SimpleItem.SimpleItemUpdated +=new EventHandler(SimpleItem_SimpleItemUpdated);
}
void SimpleItem_SimpleItemUpdated(object sender, EventArgs e)
{
InvokePropertyChanged("MyFancyItem");
}
public FancyItem myFancyItem
{
set
{
if (SimpleItem.Item != value)
{
SimpleItem.Item = value;
InvokePropertyChanged("MyFancyItem");
}
}
get { return SimpleItem.Item; }
}
}
It’s all a little crazy I know, but the idea here helps to extract the ViewModel from the underlying model even a step further. Then If I need to store and retrieve my model data from isolated storage or some other external location it’s just one item that I have to get and set from as apposed to each little part.
The best part is that my UI is always up to date.