Automatic generation of View-Model – test drive

In the previous post I tried to present an approach for automatic View-Model generation. When trying to use it in a real life project as simple as a registration form, many missing features were revealed.

Here’s a list of issues which were noticed on simple implementation:

  • There’s no way to access the model from the abstract View-Model
  • There’s no way no pass arguments to the constructor
  • No built-in way to define the order of validations
  • No way to raise event of PropertyChanged other than the property being set
  • No way to declare additional errors on properties which are not mapped to the model

Actual attempt to use the framework in registration form

image
This is the simple registration – all fields are mandatory, email must match some regular expression and password verification must be the same as the password. The model has 3 properties – Email, Name and Password. These properties are mapped to the View-Model. Let’s see how this code looks like in the View-Model using the new framework:

public abstract class UserRegistrationViewModel : 
INotifyPropertyChanged, IDataErrorInfo, IUserViewModel
{
private static readonly ViewModelGenerator viewModelsGenerator = new ViewModelGenerator();

public static UserRegistrationViewModel CreateUserRegistrationViewModel(User user)
{
return viewModelsGenerator.Generate<UserRegistrationViewModel>(user);
}

protected UserRegistrationViewModel() { }

private const string MANDATORY_FIELD_ERROR_MESSAGE = "This field is mandatory";
private const string PASSWORD_VERIFICATION_PROPERTY_NAME = "PasswordVerification";

[
Model]
private readonly User user;
private ICommand save;

public event PropertyChangedEventHandler PropertyChanged = (sender, args) => { };

[
Validation(typeof (MandatoryValidator))]
public abstract string Name { get; set; }

[
Validation(typeof(EmailValidator))]
[
Validation(typeof (MandatoryValidator))]
public abstract string Email { get; set; }

[
Validation(typeof(MandatoryValidator))]
[
RelatedProperty(PASSWORD_VERIFICATION_PROPERTY_NAME)]
public abstract string Password { get; set; }

private string passwordVerification;
public string PasswordVerification
{
get { return passwordVerification; }
set
{
passwordVerification =
value;
}
}

private string GetPasswordVerificationValidation()
{
if(string.IsNullOrEmpty(passwordVerification))
{
return MANDATORY_FIELD_ERROR_MESSAGE;
}

if (user.Password != passwordVerification)
{
return "Password and Verification must be same";
}

return null;
}

public virtual string this[string columnName]
{
get
{
if (columnName == PASSWORD_VERIFICATION_PROPERTY_NAME)
{
return GetPasswordVerificationValidation();
}

return null;
}
}

public string Error { get { return null; } }

public ICommand Save
{
get
{
if (save == null)
{
save =
new SaveCommand(user);
}

return save;
}
}
}

Let’s see how this code demonstrates the solutions to some of the missing features.

Accessing the model – in order to get an instance of the model, a new attribute is presented [Model]. Decorating a field with it will make sure that field is initialized with the model instance.

Raising PropertyChaned event on other property than the one being set – in order to deal with it a new attribute is presented [RelatedProperty]. When the property is being set, the PropertyChanged event will be raised for all properties – the one being set and the related properties.

Defining additional errors for non-mapped properties – the generated View-Model tries to resolve errors for mapped properties, if it find none, it forwards the call the base View-Model (the abstract class) and checks for additional errors.

Download

The framework source can be found in CodePlex. It is still very partial and contains many bugs but it already works in the basic scenarios 🙂

Advertisements

Automatic generation of View-Model – first attempt

I recently found myself writing the same code again and again. As can be guessed, I’m writing a WPF application based on MVVM architecture. In order to avoid writing the same code again I am trying to generate the View-Model automatically using Castle Dynamic Proxy.

Simplest scenario – forwarding calls to model

This is probably the simplest case we encounter. This is so simple we are tempted to bind the view directly to the model.

Naive implementation

public class Model
{
public object Prop { get; set; }
}

public class ViewModel
{
private readonly Model model;

public ViewModel(Model model)
{
this.model = model;
}

public object Prop
{
get { return model.Prop; }
set { model.Prop = value; }
}
}

The generated alternative

So, what we’d like to achieve is skipping the forwarding implementation. The View-Model can look like:

public abstract class ViewModel
{
public abstract object Prop { get; set; }
}

So far, it’s very simple 🙂 Let’s see how it’s being used with the Model:

[Test]
public void Generate_SetPropertyValue_ModelPropertyUpdated()
{
var viewModelGenerator = new ViewModelGenerator();
var model = new Model();

ViewModel generatedViewModel = viewModelGenerator.Generate<ViewModel>(model);

object valueToAssign = new object();
generatedViewModel.Prop = valueToAssign;

Assert.That(model.Prop, Is.SameAs(valueToAssign));
}

This example shows that we’ve created a View-Model based on a Model instance, simulated a call to a property setter and the new value automatically reflected the Model. That was simple, wasn’t it?

Next scenario – Implementing INotifyPropertyChanged

This is a very common scenario, which has a very common implementation. It’s so common I’ll skip the naive implementation and jump to the generated version directly:

The generated alternative

public abstract class ViewModel : INotifyPropertyChanged
{
public abstract object Prop { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}

[
Test]
public void Generate_SetPropertyValue_PropertyChangedRaised()
{
var viewModelGenerator = new ViewModelGenerator();
var model = new Model();
ViewModel generatedViewModel = viewModelGenerator.Generate<ViewModel>(model);

bool wasRaised = false;
generatedViewModel.PropertyChanged += (sender, args) => wasRaised =
true;

generatedViewModel.Prop =
new object();

Assert.That(wasRaised, Is.True);
}

In this case we’ve generated a View-Model that implements INotifyPropertyChanged. We had to do nothing but declare the View-Model implements INotifyPropertyChanged. Whenever a property value is changed the event will be raised with the property name.

Last scenario – Implementing IDataErrorInfo

This case is trivial too so I’ll skip again the naive solution.

The generated alternative

First we’ll take a look at the format of the abstract View-Model and the validation declaration:

public abstract class ViewModel : IDataErrorInfo
{
public abstract string this[string columnName] { get; }
public abstract string Error { get; }

[
Validation(typeof(DummyValidator))]
public abstract object Prop { get; set; }
}

public class DummyValidator : IValidator
{
public string Validate(object value)
{
return "error";
}
}

The view model is now implementing the IDataErrorInfo interface. The interfaces require implementation of indexer that maps from property to error message. The automatic View-Model generation should take care of it. In order to declare the required validations we’ll use the Validation attribute. The attribute defines which validator to use on the property. The validation result will be mapped to the property name.
For example:

[Test]
public void Generate_SetInvalidPropertyValue_PropertyErrorIsCorrect()
{
var viewModelGenerator = new ViewModelGenerator();
var model = new Model();

ViewModel generatedViewModel = viewModelGenerator.Generate<ViewModel>(model);

generatedViewModel.Prop =
new object();

Assert.That(generatedViewModel["Prop"], Is.EqualTo("error"));
}

Conclusion

MVVM is used many times in common scenarios. The code is usually a template which we can generate dynamically. This way the code duplication will be drastically reduced and allow uniform implementation.
The examples here were simplified but the concept should be clear. There’s much more work on the framework in order to cover more real life scenarios, soon to come 🙂 I’ll upload the framework source code to CodePlex before the next post.