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 🙂

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s