Developers who are writing Windows Store apps using C# and XAML might find some of the support for Model-View-ViewModel (MVVM) lacking. Both WPF and Silverlight provided specific interfaces that enabled you to store validation context about fields on a context and even supported asynchronous validation. Although there are some existing frameworks that provide this support, such as Prism for the Windows Runtime, it is also fairly simple and straightforward to roll your own.
I do appreciate the various MVVM frameworks that exist (I’ve even written one of my own) but I find that most of the support I need for Windows Store apps comes out of the box. If you create your project from any of the built-in templates, the classes you need are already generated. In the case of the Blank app template, you simply need to add a single BasicPage item to get the supporting classes for layout-aware apps with intrinsic MVVM support. My favorite class is the BindableBase that provides basic property change notification and a special way to set properties that only triggers change notification if the new value differs from the old.
I use this to create a ValidationErrors class that is simply a collection of errors with a validation flag.
public class ValidationErrors : BindableBase
{
private readonly Dictionary<string, string> validationErrors = new Dictionary<string, string>();
public bool IsValid
{
get
{
return this.validationErrors.Count < 1;
}
}
}
The indexer handles storing the errors and raising the appropriate property change notification so the dictionary can be bound in the UI.
public string this[string fieldName]
{
get
{
return this.validationErrors.ContainsKey(fieldName) ?
this.validationErrors[fieldName] : string.Empty;
}
set
{
if (this.validationErrors.ContainsKey(fieldName))
{
if (string.IsNullOrWhiteSpace(value))
{
this.validationErrors.Remove(fieldName);
}
else
{
this.validationErrors[fieldName] = value;
}
}
else
{
if (!string.IsNullOrWhiteSpace(value))
{
this.validationErrors.Add(fieldName, value);
}
}
this.OnPropertyChanged();
this.OnPropertyChanged("InValid");
}
}
Note this implementation simply removes the item. It also supports a single message per item, it could be enhanced by providing a dictionary of lists if you want to support multiple errors per field.
The next step is to create a base class for validations to use.
public abstract class ValidationBase : BindableBase
{
protected ValidationBase()
{
this.ValidationErrors = new ValidationErrors();
}
public ValidationErrors ValidationErrors { get; set; }
public bool IsValid { get; private set; }
public void Validate()
{
this.ValidationErrors.Clear();
this.ValidateSelf();
this.IsValid = this.ValidationErrors.IsValid;
this.OnPropertyChanged("IsValid");
this.OnPropertyChanged("ValidationErrors");
}
protected abstract void ValidateSelf();
}
Now you can implement the validation in your class. The current implementation assumes you have a validation method that fires at the end, but you could easily enhance it to validate specific items as they are input as well. The sample class is a simple note with a title and description, and both are required to be filled out for validation to pass.
protected override void ValidateSelf()
{
if (string.IsNullOrWhiteSpace(this.title))
{
this.ValidationErrors["Title"] = "Title is required.";
}
if (string.IsNullOrWhiteSpace(this.description))
{
this.ValidationErrors["Description"] = "You must type some text for the note.";
}
}
Now that the validation is in place, you can easily bind to it in the UI. In this case I’m not using any converters because I want the space for the validation text to remain even if the fields are valid. This prevents the UI from shifting unnaturally. The save command simply calls the Validate method on the note and only saves it if the result is true. Take a look at the XAML and note how the indexers are used to display the validation message.
<TextBlock Text="Title:" Style="{StaticResource PageSubheaderTextStyle}"/>
<TextBox Text="{Binding Title, Mode=TwoWay}" Grid.Row="1"/>
<TextBlock Text="{Binding ValidationErrors[Title]}"
Foreground="Red" Grid.Row="2" Style="{StaticResource ItemTextStyle}" Margin="12 0 0 0"/>
<TextBlock Text="Notes:" Style="{StaticResource PageSubheaderTextStyle}" Grid.Row="3"/>
<TextBox Text="{Binding Description, Mode=TwoWay}" Grid.Row="4" TextWrapping="Wrap"/>
<TextBlock Text="{Binding ValidationErrors[Description]}"
Foreground="Red" Grid.Row="5" Style="{StaticResource ItemTextStyle}" Margin="12 0 0 0"/>
That’s it … now I have a simple way to validate entities and provide messages to the UI through MVVM. The final result looks like this (obviously some more work can be done to highlight the input box itself and style the page, but you should get the general idea).
For the full working example (that includes how to use the WinRT data protection provider to encrypt notes) visit http://winrtexamples.codeplex.com/