One thing that always annoyed the hell out of me when implementing INotifyPropertyChanged for Silverlight (or WPF) databinding was the fact that you have to provide the name of the property as a string. There are some cool AOP solutions to this, but if you can't use those for whatever reason, you could use LINQ's Expressions to avoid having to use strings.
We already have a base PresentationModel which has the following method:
protected void NotifyPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
We also have a generic PresentationModel class which inherits from this class:
public abstract class PresentationModel<TPresentationModel> : PresentationModel
where TPresentationModel : PresentationModel<TPresentationModel>
{
protected void NotifyPropertyChanged(Expression<Func<TPresentationModel, object>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
throw new InvalidOperationException("You must specify a property!");
}
NotifyPropertyChanged(memberExpression.Member.Name);
}
}
Notice that you pass an expression to the NotifyPropertyChanged method. It can be used like this:
public class MyTestPresentationModel : PresentationModel<MyTestPresentationModel>
{
private string myString;
public string MyString
{
get
{
return myString;
}
set
{
myString = value;
NotifyPropertyChanged(t => t.MyString);
}
}
}
That's pretty cool, but you should be able to test this easily, right? That's pretty easy to do as well:
public class PresentationModelTest<TPresentationModel> where TPresentationModel : PresentationModel<TPresentationModel>
{
protected void AssertThatPropertyChangedIsCorrect<TArgument>(TPresentationModel model,
Expression<Func<TPresentationModel, TArgument>> property, TArgument value)
{
var memberInfo = ((MemberExpression)property.Body).Member;
var propertyName = memberInfo.Name;
bool eventProperlyTriggered = false;
model.PropertyChanged += (s, e) => eventProperlyTriggered = e.PropertyName.Equals(propertyName);
typeof(TPresentationModel).GetProperty(propertyName).SetValue(model, value, null);
Assert.IsTrue(eventProperlyTriggered);
}
}
[TestClass]
public class PresentationModelTests : PresentationModelTest<MyTestPresentationModel>
{
[TestMethod]
public void NotifyPropertyChanged_WithExpression_TriggersCorrectPropertyChangedEvent()
{
var model = new MyTestPresentationModel();
AssertThatPropertyChangedIsCorrect(model, m => m.MyString, "blah");
}
}
There we go... no more strings so refactoring won't break anything, and it's easy to test as well.