MVVM, DataObjects in Catel "best practies" - wpf

I'm using Catel in my application. I have any questions regarding DataObjects and ViewModels - what is the best way to use Catel efficientlu?
Scenario 1:
I have a MainViewModel and a MainView. In this View I call another View (DataWindow) with a own ViewModel (SettingsViewModel) and show it in a Dialog. In this Dialog I insert some SettingsValues and store it in xml. Last but not least I have a DataObject class to store the data from the Dialog. Here any pseudocode:
MainViewModel : ViewModelBase
{
private void OnSystemSettingsCommandExecute()
{
//create a new ViewModel and show as Dialog
uiVisualizerService.ShowDialog(new SystemSettingsViewModel());
}
...
}
SystemSettingsViewModel : ViewModelBase
{
/// <summary>
/// Gets or sets the property value.
/// </summary>
[Model]
public SettingsDataObject SettingsData
{
get { return GetValue<SettingsDataObject>(SettingsDataProperty); }
set { SetValue(SettingsDataProperty, value); }
}
/// <summary>
///
/// </summary>
public static readonly PropertyData SettingsDataProperty = RegisterProperty("SettingsData", typeof(SettingsDataObject));
/// <summary>
/// It is right to define the property again here?
/// </summary>
[ViewModelToModel("SettingsData")]
public string UserName
{
get { return GetValue<string>(UserNameProperty); }
set { SetValue(UserNameProperty, value); }
}
/// <summary>
/// Register the UserName property so it is known in the class.
/// </summary>
public static readonly PropertyData UserNameProperty = RegisterProperty("UserName", typeof(string));
// Load and Save right here?
protected override bool Save()
{
SettingsData.Save(#"D:\Projects\Testdata\xml\Settings.xml");
return true;
}
protected override void Initialize()
{
SettingsData = SavableModelBase<SettingsDataObject>.Load(#"D:\Projects\Testdata\xml\Settings.xml", SerializationMode.Xml);
}
}
public class SettingsDataObject : SavableModelBase<SettingsDataObject>
{
// Propertys
/// <summary>
/// Gets or sets the property value.
/// </summary>
public string UserName
{
get { return GetValue<string>(UserNameProperty); }
set { SetValue(UserNameProperty, value); }
}
/// <summary>
/// Register the UserName property so it is known in the class.
/// </summary>
public static readonly PropertyData UserNameProperty = RegisterProperty("UserName", typeof(string), "MyUserName");
}
Is it right that I must define the property "UserName" in the DataClass and in the ViewModel class? Is that the "normal way" that I define my Model property in the ViewModel and than i access my data propertys with [ViewModelToModel("SettingsData")]?
How I can Load and Save automatic my Data Objects? In my case I override the "Save" and the "Initialize" methode? Is there a better way to do this in Catel?
Now I must have access of the SettingsDataObject in the MainViewModel but i didn't find a way to use the object in other ViewModels. What is the "best practices" to load the settings in other ViewModels?

Question 1)
Yes, this is "right", but totally depends on the form of MVVM you want to follow. For more information, read this. I like to protect my model (make it private on the VM) and only expose the properties I really want being exposed on the VM. But some other people like to directly bind on the model instead. It's just the different number of interpretations like you can read in the article.
Question 2)
Overriding the Initialize and Save are exactly meant for this, so you are doing it the right way!
Question 3)
When you need to share a model, you can either use nested user controls to pass the model from one view model to another. If you need to access the model in a lot of different places, it might be wise to register it in the ServiceLocator. It can then automatically be injected into your view models by Catel.
// Somewhere in your code
var serviceLocator = ServiceLocator.Default;
serviceLocator.RegisterType<ISettings>(mySettings);
// Your view model constructor
public MyViewModel(ISettings mySettings)
{
// injected here
}
If you want to create your own view model, you can do this:
var dependencyResolver = this.GetDependencyResolver();
var viewModelFactory = dependencyResolver.Resolve<IViewModelFactory>();
var viewModel = viewModelFactory.CreateViewModel<MyViewModel>(null);
Note that if you are inside another VM, you can of course let the IViewModelFactory be injected so you only need 1 line of code to create the view model.

Related

wpf mvvm : Why duplicating model collections in view models when binding to a treeview? how to avoid it?

I have a 2-levels hierarchy in my model composed of constellations and entities, hosted in a root object, and I want to show them in a TreeView.
Root object
L Constellations
L Entities
I have my RootObjectViewModel exposing my root object. In that, I fully agree with Bryan Lagunas in MVVM best practices, that the model object should be exposed by the view model instead of doing facade. Excellent video by the way, really worth the time.
I read everywhere that the TreeView.ItemsSource should be mapped to a collection of viewmodels, that is, for my collection of Constellation, my RootObjectViewModel should provide a collection of ConstellationViewModel.
My concern is that if my collections of constellations, and entities within, are live, that is, if some items are added, changed (their order) or removed, I have to manually reflect those changes in my ViewModels' collections.
I would find it more elegant to map the ItemsSource to, say, the collection of Constellation itself, so that any change in the model is reflected without duplication, and then have some converter or so to map the TreeViewItems.DataContext to a ConstellationViewModel based on the constellation model.
If this is not clear enough, I can clarify this with some sample code.
Did anyone face the same question and/or try to implement this ?
Thanks you in advance for your help.
Cedric
It depends. If your model has exactly the properties the view needs, and the view can directly alter them when the user clicks around, it's fine to expose the model.
But if your model is, for example, read only and require calls to a service to apply changes, you have to wrap it in a view model to provide the view with writeable properties.
Got it working !
It is not possible out-of-the-box, and here's why:
It is possible to use model collections as items source, and to use a converter to get the appropriate view model it the components inside the TreeViewItem. But there isn't any way to interfere with the creation of TreeViewItem to apply the converter to its DataContext. Which means that the TreeViewItem's properties can't be binded to the ViewModel.
In other words :
if you want to stick with the standard behavior of the TreeView and don't have to deal with the TreeViewItems properties, if either your collections don't change or can implement ICollectionChanged, and if your models don't change or can implement IPropertyChanged, it is fine to go with the model's collections.
If any of these conditions is broken, then you will have to go with building ViewModel's collections and sync them with model's collection.
Now, I implemented a collection type named ConvertingCollection<Tin, Tout> that uses an original collection as an input and syncs its own contents with this input. This is just a basic class with many many ways of improvement, but it works. All you have to do is use this collection as a VM property, set the original collection and converter, and bind the ItemsSource to this collection.
Here's the full code:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Windows.Data;
namespace TreeViewPOC
{
class ConvertingCollection<Tin, Tout> : ObservableCollection<Tout>
{
private IValueConverter _converter;
private bool _isObservableCollection;
private IEnumerable<Tin> _originalCollection;
private Dictionary<Tin, Tout> _mapping = new Dictionary<Tin, Tout>();
public ConvertingCollection(IValueConverter converter)
{
// save parameters
_converter = converter;
}
public ConvertingCollection(IEnumerable<Tin> originalCollection, IValueConverter converter)
{
// save parameters
_converter = converter;
OriginalCollection = originalCollection;
}
#region Properties
public IEnumerable<Tin> OriginalCollection
{
get
{
return _originalCollection;
}
set
{
if (!value.Equals(_originalCollection))
{
// manage older collection
if (_originalCollection != null && _isObservableCollection)
{
(_originalCollection as ObservableCollection<Tin>).CollectionChanged -= originalCollection_CollectionChanged;
this.Clear();
}
_originalCollection = value;
// setup original collection information.
_isObservableCollection = _originalCollection is INotifyCollectionChanged;
if (_originalCollection != null && _isObservableCollection)
{
(_originalCollection as INotifyCollectionChanged).CollectionChanged += originalCollection_CollectionChanged;
foreach (Tin item in _originalCollection)
{
AddConverted(item);
}
}
}
}
}
#endregion
/// <summary>
/// Indicates the time in milliseconds between two refreshes.
/// </summary>
/// <notes>
/// When the original collection isn't observable, it must be explored to reflect changes in the converted collection.
/// </notes>
// TODO
//public int RefreshRate { get; set; } = 1000;
/// <summary>
/// Flushes the collection.
/// </summary>
public new void Clear()
{
_mapping.Clear();
base.Clear();
}
#region Events management
private void originalCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (Tin item in e.NewItems)
{
AddConverted(item);
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (Tin item in e.OldItems)
{
RemoveConverted(item);
}
break;
}
}
#endregion
#region Helpers
/// <summary>
/// Converts an item and adds it to the collection.
/// </summary>
/// <param name="item">The original item.</param>
private void AddConverted(Tin item)
{
Tout converted = (Tout) _converter.Convert(item, typeof(Tout), null, CultureInfo.CurrentCulture);
_mapping.Add(item, converted);
this.Add(converted);
}
/// <summary>
/// Removes a converted itemfrom the collection based on its original value.
/// </summary>
/// <param name="item">The original item.</param>
private void RemoveConverted(Tin item)
{
this.Remove(_mapping[item]);
_mapping.Remove(item);
}
#endregion
}
}
I created a project on Github named MVVMTreeViewPOC to POC the idea. It is working fine, and it is a good tool to show the limitations.
Cedric.

How to unit test using a ViewModelLocator

I've created a custom view model locator using Autofac and set it up normally through the App.xaml like most of them are usually used. My problem is how do I unit test now? I'm getting an error every time I try to test a method that initializes a view
In my app.xaml:
<desktop:ViewModelLocator xmlns:local="clr-namespace:MyProject.Desktop" x:Key="ViewModelLocator" />
In each view:
DataContext="{Binding MyFirstViewModel, Source={StaticResource ViewModelLocator}}"
The Unit Test Error:
{"Cannot find resource named 'ViewModelLocator'. Resource names are case sensitive."}
I understand why cause when you unit test, there really isn't an instance of the actual App so what is a good way around this problem?
ViewModelLocator Code:
/// <summary>
/// Autofac object container
/// </summary>
private readonly IContainer objectContainer;
#region Constructor
/// <summary>
/// Constructor for view model locator
/// </summary>
public ViewModelLocator()
{
objectContainer = App.ObjectContainer;
//objectContainer.BeginLifetimeScope();
}
#endregion
#region Properties
/// <summary>
/// Gets the resolved instance of a main window view model
/// </summary>
public MainWindowViewModel MainWindowViewModel
{
get
{
return objectContainer.Resolve<MainWindowViewModel>();
}
}
public FirstViewModel MyFirstViewModel
{
get
{
return objectContainer.Resolve<FirstViewModel>();
}
}
public SecondViewModel MySecondViewModel
{
get
{
return objectContainer.Resolve<SecondViewModel>();
}
}
This is a bit late, but maybe useful. Instead of resolving objectContainer in the constructor, do it through the property:
//note this is a lazy getter, i.e. will be resolved when needed on the first call
private IContainer ObjectContainer
{
get
{
if(objectContainer == null)
objectContainer = App.ObjectContainer;
return objectContainer:
}
}
Then use the property through your code, not the field. Also when I am concerned about someone else using the field that I want to enforce through the property usage, I would rename it to something that would not easily be recognizable in the IntelliSence (zREgdnlksfObjectContainer for example:) ) Note the property is private, so nothing really changes. You can make the property internal and mark your lib to be visible to your unit test, so that in the unit test you can Mock it to WhenCalled() return/resolve IContainer.

MVVM and collections of VMs

A common senario: A model with a collection of item models.
E.g a House with a collection of People.
How to structure this correctly for MVVM - particulary with regard to updating the Model and ViewModel collections with additions and deletes?
Model House contains a collection of model People (normally a List<People>).
View model HouseVM contains the House object that it wraps and an ObservableCollection of view model PeopleVM (ObservableCollection<PeopleVM>). Note that we end up here with the HouseVM holding two collections (that require syncing):
1. HouseVM.House.List<People>
2. HouseVM.ObservableCollection<PeopleVM>
When the House is updated with new People (add) or People leave (remove) that event must now be handled in both collections the Model House People collection AND the the VM HouseVM PeopleVM ObservableCollection.
Is this structure correct MVVM?
Is there anyway to avoid having to do the double update for Adds and Removes?
Your general approach is perfectly fine MVVM, having a ViewModel exposing a collection of other ViewModels is a very common scenario, which I use all over the place. I would not recommend exposing items directly in a ViewModel, like nicodemus13 said, as you end up with your view binding to models without ViewModels in between for your collection's items. So, the answer to your first question is: Yes, this is valid MVVM.
The problem you are addressing in your second question is the synchronization between the list of people models in your house model and the list of people ViewModels in your house ViewModel. You have to do this manually. So, no there is no way to avoid this.
What you can do: Implement a custom ObservableCollection<T>, ViewModelCollection<T>, which pushes it's changes to an underlying collection. To get two way synching, make the model's collection an ObservableCollection<> too and register to the CollectionChanged event in your ViewModelCollection.
This is my implementation. It uses a ViewModelFactory service and so on, but just have a look at the general principal. I hope it helps...
/// <summary>
/// Observable collection of ViewModels that pushes changes to a related collection of models
/// </summary>
/// <typeparam name="TViewModel">Type of ViewModels in collection</typeparam>
/// <typeparam name="TModel">Type of models in underlying collection</typeparam>
public class VmCollection<TViewModel, TModel> : ObservableCollection<TViewModel>
where TViewModel : class, IViewModel
where TModel : class
{
private readonly object _context;
private readonly ICollection<TModel> _models;
private bool _synchDisabled;
private readonly IViewModelProvider _viewModelProvider;
/// <summary>
/// Constructor
/// </summary>
/// <param name="models">List of models to synch with</param>
/// <param name="viewModelProvider"></param>
/// <param name="context"></param>
/// <param name="autoFetch">
/// Determines whether the collection of ViewModels should be
/// fetched from the model collection on construction
/// </param>
public VmCollection(ICollection<TModel> models, IViewModelProvider viewModelProvider, object context = null, bool autoFetch = true)
{
_models = models;
_context = context;
_viewModelProvider = viewModelProvider;
// Register change handling for synchronization
// from ViewModels to Models
CollectionChanged += ViewModelCollectionChanged;
// If model collection is observable register change
// handling for synchronization from Models to ViewModels
if (models is ObservableCollection<TModel>)
{
var observableModels = models as ObservableCollection<TModel>;
observableModels.CollectionChanged += ModelCollectionChanged;
}
// Fecth ViewModels
if (autoFetch) FetchFromModels();
}
/// <summary>
/// CollectionChanged event of the ViewModelCollection
/// </summary>
public override sealed event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
/// <summary>
/// Load VM collection from model collection
/// </summary>
public void FetchFromModels()
{
// Deactivate change pushing
_synchDisabled = true;
// Clear collection
Clear();
// Create and add new VM for each model
foreach (var model in _models)
AddForModel(model);
// Reactivate change pushing
_synchDisabled = false;
}
private void ViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Return if synchronization is internally disabled
if (_synchDisabled) return;
// Disable synchronization
_synchDisabled = true;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var m in e.NewItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Add(m);
break;
case NotifyCollectionChangedAction.Remove:
foreach (var m in e.OldItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Remove(m);
break;
case NotifyCollectionChangedAction.Reset:
_models.Clear();
foreach (var m in e.NewItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Add(m);
break;
}
//Enable synchronization
_synchDisabled = false;
}
private void ModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_synchDisabled) return;
_synchDisabled = true;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var m in e.NewItems.OfType<TModel>())
this.AddIfNotNull(CreateViewModel(m));
break;
case NotifyCollectionChangedAction.Remove:
foreach (var m in e.OldItems.OfType<TModel>())
this.RemoveIfContains(GetViewModelOfModel(m));
break;
case NotifyCollectionChangedAction.Reset:
Clear();
FetchFromModels();
break;
}
_synchDisabled = false;
}
private TViewModel CreateViewModel(TModel model)
{
return _viewModelProvider.GetFor<TViewModel>(model, _context);
}
private TViewModel GetViewModelOfModel(TModel model)
{
return Items.OfType<IViewModel<TModel>>().FirstOrDefault(v => v.IsViewModelOf(model)) as TViewModel;
}
/// <summary>
/// Adds a new ViewModel for the specified Model instance
/// </summary>
/// <param name="model">Model to create ViewModel for</param>
public void AddForModel(TModel model)
{
Add(CreateViewModel(model));
}
/// <summary>
/// Adds a new ViewModel with a new model instance of the specified type,
/// which is the ModelType or derived from the Model type
/// </summary>
/// <typeparam name="TSpecificModel">Type of Model to add ViewModel for</typeparam>
public void AddNew<TSpecificModel>() where TSpecificModel : TModel, new()
{
var m = new TSpecificModel();
Add(CreateViewModel(m));
}
}
In this situation I simply make the model expose ObservableCollections rather than Lists. There's no particular reason why it shouldn't. The ObservableCollection is in the System.Collections.ObjectModel namespace of the System assembly, so there's no unreasonable extra dependencies, you almost certainly have System anyway. List is in mscorlib, but that's as much a historical artefact as anything.
This simplifies the model-viewmodel interactions massively, I can't see a reason not to do it, using Lists on the model just creates lots of unpleasant boiler-plate code. You are interested in the events, after all.
Also, why is your HouseVM wrapping an ObservableCollection<PeopleVM>, rather than ObservableCollection<People>? VMs are for binding to views, so I would think that whatever is binding to your ObservableCollection<PeopleVM> is actually interested in People, otherwise you're binding-within-a-binding, or is there a specific reason why this is useful? I wouldn't generally have a VM expose other VMs, but maybe that's just me.
Edit about libraries/WCF
I don't see why having a model in a library, or even exposed by a WCF-server should affect whether they raise events or not, it seems perfectly valid to me (obviously the WCF-service won't expose the events directly). If you don't like this, I think you're stuck with having to chain multiple updates, though I wonder if you're actually just manually doing the same work as the event would do in an ObservableCollection, unless I've misunderstood some of it.
Personally, like I said, I'd keep the VMs simple, and have them expose the minimum and not expose other VMs. It can take some redesign and make certain parts a bit of a pain (e.g. Converters, however, you end up with a simple, easy-to-manage design with some simple-to-handle irritations on the edges.
It seems to me that your current route is going to end up very complex rather quickly and, most importantly, awkward to follow... However, YMMV, it's just my experience :)
Perhaps moving some of the logic to explicit services might help?

ObservableCollection<EF4Entity> bound to ListView, but relation fields don't cause ListView to update

I have an ObservableCollection of Entity Framework 4 entities bound to a ListView. If I modify any of the normal, scalar properties of the entity, the values displayed in the ListView are updated.
Any relationship (navigation) properties are not updated in the ListView if they change, because the entity object doesn't implement change notifications for these properties.
Right now, I'm removing the entity from the collection and then reinserting it back into the same position to force the ListView to update.
There must be a better solution. What is it, if it exists?
Here's the generated code from VS2010's EF designer:
[EdmEntityTypeAttribute(NamespaceName="RovingAuditDb", Name="AuditRecord")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class AuditRecord : EntityObject
{
#region Factory Method
/// <summary>
/// Create a new AuditRecord object.
/// </summary>
/// <param name="id">Initial value of the Id property.</param>
/// <param name="date">Initial value of the Date property.</param>
public static AuditRecord CreateAuditRecord(global::System.Int32 id, global::System.DateTime date)
{
AuditRecord auditRecord = new AuditRecord();
auditRecord.Id = id;
auditRecord.Date = date;
return auditRecord;
}
#endregion
#region Primitive Properties
// Deleted for this post
#endregion
#region Navigation Properties
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("RovingAuditDb", "AuditRecordCell", "Cell")]
public Cell Cell
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Cell>("RovingAuditDb.AuditRecordCell", "Cell").Value;
}
set
{
((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Cell>("RovingAuditDb.AuditRecordCell", "Cell").Value = value;
}
}
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[BrowsableAttribute(false)]
[DataMemberAttribute()]
public EntityReference<Cell> CellReference
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Cell>("RovingAuditDb.AuditRecordCell", "Cell");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<Cell>("RovingAuditDb.AuditRecordCell", "Cell", value);
}
}
}
// Rest of the Navigation properties removed for this post
Those entities are not suitable for WPF two-way binding, which is based in INotifyPropertyChanged. I suggest you take a look at Self Tracking Entities, which are best suited for use in client/server type WPF application.

MVVM for collections

I have recently started learning wpf and am trying to use mvvm.
My understanding is that in the mvvm neither the view or the model should know the other exists.
What I am trying to do is show a list of customers on the screen. But if I code the viewModel as shown below. which is similar to many examples I see on the net, then I end up with some code looking like this
class Customer
{
public String Name {get;set;}
public String Address {get;set;} }
}
class MainWindowViewModel
{
ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
public ObservableCollection<Customer> Customer
{
get {return customers;}
}
public MainWindowViewModel()
{
//cust1 and cust2 are Customer objets
customers.Add(cust1);
customers.Add(cust2);
}
}
Now if I create an instance of my MainWindowViewModel and set it as the datacontext of my MainWindowView (my view) and i further bind the viewmodels Customers property to a listBox, then the view will need a reference to the assembly that contains my Models.
So my questions are.
1) Is adding a reference to Models assembly allowable in MVVM, since this would mean the view knows about the model.
2) would a better solution be to wrap each Customer object in a CustomerViewModel and have the MainWindowViewModel contain ObservableCollection of CustomerViewModel
instead of ObservableCollection of Customer. This would separate the models completely from the view.
I'm not sure why you think the project containing your views requires a reference to your model project? There is nothing in your view which references your models directly - your binding expressions in XAML are linked by name only, and to properties on your view model anyway, not your model.
Wrapping your model in a view model is a good option if your view requires additional data than your model provides, and it is undesirable to change your model. For example, you view may need to display the Age of a User type, but User only has a DateOfBirth property. Creating a UserViewModel with an Age property would be a good option if you didn't want to alter your model.
Answers to your questions:
What is bad about the View referring the Model? This is absolutely ok when it simplifies the code. Just the other way around (Model -> View) is bad practice.
You don't need to wrap each Customer object in a CustomerViewModel when you don't have special needs. I would suggest to follow a pragmatic way and keep the code simple.
You might be interested in the BookLibrary sample application of the WPF Application Framework (WAF) which shows the scenario you describe here.
We usually create a CustomerViewModel. That is enforced by our generic CollectionViewModelBase class. This unsures that every part the user interface uses is exspecially created to be displayed and we don't have any UI related code in the models which are often serializable POCOs.
The MVVM pattern is similar to any other MVx pattern (MVC, MVP, ...) in that it encourages separation of concerns (SoC), which in turn improve maintainability / testability of your code. Beyond the usual SoC, MVVM gives the following:
Unit testing of your view logic; this is because you move logic from your view into your view-model, making your view as dumb as possible.
Developer-designer workflow; because the view is 'dumb', it is easier to work with the XAML without the logic behind it.
Regarding visibility, i.e. what is visible to what, it is strictly as follows:
Model <= ViewModel <= View
In other words, the ViewModel can see the Model, but the Model cannot see the ViewModel. Likewise, the View can see the ViewModel, but not vice-versa.
Because the ViewModel has no reference to the View, it enables your code to be executed without any view components present, this enables (1) above.
The purpose of your ViewModel is to 'shape' your Model to make binding to the View easier. If your View is simple, then it is quite acceptable to do the following:
Model <= View
This still allows (1) unit testing, (2) developer-designer workflow.
It is also fine to use a hybrid approach, sometimes exposing your Model to your view, other times wrapping it in a ViewModel. For example:
http://www.scottlogic.co.uk/blog/colin/2009/08/the-mini-viewmodel-pattern/
Please don't create a bunch of boiler-plate ViewModel code, just because you think you have to!
You will definitively want to wrap your models in view only objects like below :
/// <summary>
/// Business model object : Should be in your separate business model only library
/// </summary>
public class BusinessModelObject
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
}
/// <summary>
/// Base notifying object : Should be in your GUI library
/// </summary>
public abstract class NotifyingObject<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
private static readonly PropertyChangedEventArgs ModelPropertyChanged = new PropertyChangedEventArgs("Model");
private T _model;
public T Model
{
get { return _model; }
set
{
_model = value;
NotifyPropertyChanged(ModelPropertyChanged);
}
}
}
/// <summary>
/// Model decorator : Should be in your GUI library
/// </summary>
public class BusinessModelObjectAdapter : NotifyingObject<BusinessModelObject>
{
public BusinessModelObjectAdapter(BusinessModelObject model)
{
this.Model = Model;
}
private static readonly PropertyChangedEventArgs Prop1PropertyChanged = new PropertyChangedEventArgs("Prop1");
private string _prop1;
public string Prop1
{
get { return Model.Prop1; }
set
{
Model.Prop1 = value;
NotifyPropertyChanged(Prop1PropertyChanged);
}
}
private static readonly PropertyChangedEventArgs Prop2PropertyChanged = new PropertyChangedEventArgs("Prop2");
private int _prop2;
public int Prop2
{
get { return Model.Prop2; }
set
{
Model.Prop2 = value;
NotifyPropertyChanged(Prop1PropertyChanged);
}
}
//and here you can add whatever property aimed a presenting your model without altering it at any time
}

Resources