I'm going to give you the answer you don't want to hear: You shouldn't need to do this at all. Your model should have the (full or auto) properties, and then your ViewModel properties should just have a getter and a setter that returns _model.MyProperty
.
Also, you may want to look into CallerMemberName
to get rid of that pesky magic string. I'll post some examples if you want me to.
For example, I have a DealsUser
model class (note how internal logic like generating the email address if it was never explicitly set, is done here):
public class DealsUser : IDealsUser
{
public DealsUser() : this("GUEST")
{
}
public DealsUser(string username)
{
this.Username = username;
this.IsAdministrator = false;
this.IsPlanModerator = false;
this.IsPlanner = false;
}
public string Username { get; set; }
public bool IsAdministrator { get; set; }
public bool IsPlanModerator { get; set; }
public bool IsPlanner { get; set; }
private string _emailAddress;
public string EmailAddress
{
get
{
return _emailAddress ?? string.Format(
"{0}@mycompany.co.za", this.Username);
}
set
{
_emailAddress = value;
}
}
public override string ToString()
{
return this.Username;
}
And I have a BaseViewModel
class with the following event and protected methods (note how we use CallerMemberName
to eliminate magic strings):
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected bool SetProperty<T>(ref T storage, T value,
[CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value))
{
return false;
}
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected bool SetModelProperty<T>(T storage, T value, Action setter,
[CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value))
{
return false;
}
setter();
this.OnPropertyChanged(propertyName);
return true;
}
#endregion
I then inherit from BaseViewModel
, I dependency inject the model into my constructor, and I try to keep my ViewModel as lean as possible. Note that I have to use SetModelProperty
instead of SetProperty
, because you cannot pass a property (e.g. _dealsUser.Username
) into a lamba function as a reference variable. Also note that IsPlanner
and IsPlanModerator
contain extra logic that updates related notifying properties when they change:
public class DealsUserVM : BaseViewModel
{
private readonly IDealsUser _dealsUser;
public DealsUserVM()
: this(new DealsUser())
{
// Empty ctor
}
public DealsUserVM(IDealsUser dealsUser)
{
_dealsUser = dealsUser;
}
public IDealsUser Model
{
get
{
return _dealsUser;
}
}
public string Username
{
get { return _dealsUser.Username; }
set
{
SetModelProperty(_dealsUser.Username, value,
() => { _dealsUser.Username = value; });
}
}
public bool IsAdministrator
{
get { return _dealsUser.IsAdministrator; }
set
{
SetModelProperty(_dealsUser.IsAdministrator, value,
() => { _dealsUser.IsAdministrator = value; });
}
}
public bool IsPlanModerator
{
get { return _dealsUser.IsPlanModerator; }
set
{
// If IsPlanModerator has changed (and was updated as a result)
if (SetModelProperty(_dealsUser.IsPlanModerator, value,
() => { _dealsUser.IsPlanModerator = value; }))
{
// If IsPlanModerator is now TRUE
if (value)
{
this.IsPlanner = true;
}
}
}
}
public bool IsPlanner
{
get { return _dealsUser.IsPlanner; }
set
{
// If IsPlanner has changed (and was updated as a result)
if (SetModelProperty(_dealsUser.IsPlanner, value,
() => { _dealsUser.IsPlanner = value; }))
{
// If IsPlanner is now FALSE
if (!value)
{
this.IsPlanModerator = false;
}
}
}
}
public string EmailAddress
{
get { return _dealsUser.EmailAddress; }
set
{
SetModelProperty(_dealsUser.EmailAddress, value,
() => { _dealsUser.EmailAddress = value; });
}
}
public override string ToString()
{
return _dealsUser.ToString();
}
}
Hope this helps :-)
INotifyPropertyChanged
which massively reduces the amount of code needed. – Embrangle