patterncsharpMinor
Custom ViewModel to check property existance prior to update
Viewed 0 times
existanceupdatecustompropertyviewmodelpriorcheck
Problem
I have written an abstract View Model which all my other View Models inherit when I write any apps in Silverlight, WinRT, WPF etc. The class was written to attempt to address an issue I have run into frequently. That is, after refactoring code, all my property names are wrong and I often forget to rename them. Debugging by identifying views which don't reflect their view models is hard, thus this.
The idea is relatively simple. It provides an implementation of the
Thus:
Will throw an exception if I try to call
The
```
using System;
using System.ComponentModel;
#if DEBUG
// We need reflection in order to debug our Property Changed Notifications
using System.Reflection;
#endif
namespace WorldOfZero.Common.ViewModels
{
public class AbstractViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string name)
{
#if DEBUG
// This will detect and throw an error if we attempt to Notify a change on a none existant property.
// This helps eliminate stupid mistakes like spelling errors.
// This code is only included in the debug builds of the app, this prevents us from slowing down production code.
Type type = this.GetType();
PropertyInfo property = type.GetRuntimeProperty(name);
if (property == null)
{
throw new InvalidOperationException(String.Format("Attempting to Notify a change on a property ({0}) that does not exist.", name));
}
#endif
The idea is relatively simple. It provides an implementation of the
INotifyPropertyChanged interface with the addition of some additional safety checking when debugging the project. This is done via reflection by checking if the property you are attempting to notify actually exists.Thus:
class Foo : AbstractViewModel {
public object Bar { get; set; }
}Will throw an exception if I try to call
NotifyPropertyChanged("Ponies") since Ponies is not a property in Foo.The
AbstractViewModel looks like this:```
using System;
using System.ComponentModel;
#if DEBUG
// We need reflection in order to debug our Property Changed Notifications
using System.Reflection;
#endif
namespace WorldOfZero.Common.ViewModels
{
public class AbstractViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string name)
{
#if DEBUG
// This will detect and throw an error if we attempt to Notify a change on a none existant property.
// This helps eliminate stupid mistakes like spelling errors.
// This code is only included in the debug builds of the app, this prevents us from slowing down production code.
Type type = this.GetType();
PropertyInfo property = type.GetRuntimeProperty(name);
if (property == null)
{
throw new InvalidOperationException(String.Format("Attempting to Notify a change on a property ({0}) that does not exist.", name));
}
#endif
Solution
#if DEBUG
// We need reflection in order to debug our Property Changed Notifications
using System.Reflection;
#endifThat
#if directive is overkill. A using statement isn't like a C++ include, it doesn't clutter up your assembly with code you may not need. All it does, is facilitate coding by telling the parser that types in these namespaces don't need to be fully qualified; once the code is compiled, they're "inlined" anyway.Now, the idea is nice, but as you noticed, you can give it just about any value and it happily passes the "name" to whoever is listening for that event.
That
string parameter isn't refactoring-friendly. You've mitigated the issue by throwing an InvalidOperationException - if your ViewModel classes' setters are 100% covered with unit tests, you're probably good to go.The thing that worries me is that the exception isn't thrown in a release build, so instead of blowing up and failing promptly, you're failing silently, and worse things could result from this. Of course that should not happen.. but the room for human error is there.
The best way to implement that interface, is in my opinion with a strongly-typed overload that takes a selector expression and passes the selected member's name to the interface's stringly-typed method.
Something like this for example, can turn your setters into something like this:
private int _foo;
public int Foo
{
get { return _foo; }
set
{
if (value != _foo)
{
_foo = value;
OnPropertyChanged(() => this.Foo);
}
}
}Code Snippets
#if DEBUG
// We need reflection in order to debug our Property Changed Notifications
using System.Reflection;
#endifprivate int _foo;
public int Foo
{
get { return _foo; }
set
{
if (value != _foo)
{
_foo = value;
OnPropertyChanged(() => this.Foo);
}
}
}Context
StackExchange Code Review Q#97765, answer score: 8
Revisions (0)
No revisions yet.