Improved Validation for Universal Windows Apps

I wrote a couple weeks back on my attempt to re-write the ValidatableBase class I wrote for Universal Windows Apps. The original idea was great, but in practice it turned out to be a headache. Writing a method per property (in some cases more than one!) really bloated the code and made it difficult to read. So I set out to tackle this in a different way.

To demonstrate the improvements, I want to first revisit how the previous version worked. The following is a simple User model that would perform validation on a user name and password with the old ValidatableBase.

public class User : ValidatableBase
{
    private string email = string.Empty;

    private string password = string.Empty;

    public User()
    {
        this.RegisterProperty("Email", "Password");
    }

    public string Email
    {
        get 
        {
            return this.email;
        }
        set 
        { 
            this.email = value;
            this.OnPropertyChanged("Email");
        }
    }

    public string Password
    {
        get 
        { 
            return this.password; 
        }
        set 
        { 
            this.password = value;
            this.OnPropertyChanged("Password");
        }
    }

    public override void Validate()
    {
        this.ValidateProperty(this.ValidateEmailIsNotEmpty, "Invalid Email Address.", "Email");
        this.ValidateProperty(this.ValidateEmailIsFormatted, "Email Address is not in the correct format.", "Email");
        this.ValidateProperty(this.ValidatePasswordIsNotEmpty, "Password can not be empty.", "Password");
        this.ValidateProperty(this.ValidatePasswordIsToShort, "Password must be greater than 8 characters.", "Password");
        this.ValidateProperty(this.ValidateIfPasswordContainsSpaces, "Password must not contain spaces.", "Password");

        base.Validate();
    }

    private IValidationMessage ValidateEmailIsNotEmpty(string failureMessage)
    {
        if (string.IsNullOrEmpty(this.Email))
        {
            return new ValidationErrorMessage(failureMessage);
        }

        return null;
    }

    private IValidationMessage ValidateEmailIsFormatted(string failureMessage)
    {
        string[] addressParts = this.Email.Split('@');

        if (addressParts.Length < 2)
        {
            var msg = new ValidationErrorMessage(failureMessage);
            return msg;
        }

        string[] domainPiece = addressParts.LastOrDefault().Split('.');
        if (domainPiece.Length < 2)
        {
            var msg = new ValidationErrorMessage(failureMessage);
            return msg;
        }

        return null;
    }

    private IValidationMessage ValidatePasswordIsNotEmpty(string failureMessage)
    {
        if (string.IsNullOrEmpty(this.Password))
        {
            return new ValidationErrorMessage(failureMessage);
        }

        return null;
    }

    private IValidationMessage ValidatePasswordIsToShort(string failureMessage)
    {
        if (this.Password.Length < 8)
        {
            return new ValidationErrorMessage(failureMessage);
        }

        return null;
    }

    private IValidationMessage ValidateIfPasswordContainsSpaces(string failureMessage)
    {
        if (this.Password.Contains(' '))
        {
            return new ValidationErrorMessage(failureMessage);
        }

        return null;
    }
}

As you can see, I had to write a single method for each different type of validation I wanted to perform. In this case, as shown in the Validate() method, I have 5 different methods to validate 2 properties. Imaging a more complex model? This quickly turned in to a messy model.

The solution

I heavily modified the ValidatableBase class and its interface initially, but ultimately ended up being able to keep it mostly unchanged and just add on to it. The validation via method delegates certainly has a place, and I didn't want to loose that. The goal this time-around however was to make delegates the last choice instead of the first choice.

The class now supports attribute based validation, very much like Microsoft's DataAnnotation. Since Data Annotations are not supported in Universal Apps, I set out to build my own version, which actually ended up being a bit different with some nifty features.

Let's take the User class, and re-write it to use the new validation scheme.

public class User : ValidatableBase
{

    private string email = string.Empty;

    private string password = string.Empty;

    [ValidateValueIsNotNull(FailureMessage = "E-Mail can not be left blank.", ValidationMessageType = typeof(ErrorMessage))]
    [ValidateWithCustomHandler(DelegateName = "ValidateEmailFormat")]
    public string Email
    {
        get
        {
            return this.email;
        }

        set
        {
            this.email = value;
            this.OnPropertyChanged("Email");
        }
    }

    [ValidateStringIsGreaterThan(GreaterThanValue = 6, FailureMessage = "Password must be greater than 6 characters.", ValidationMessageType = typeof(ErrorMessage))]
    [ValidateStringIsLessThan(LessThanValue = 20, FailureMessage = "Password must be less than 20 characters.", ValidationMessageType = typeof(ErrorMessage))]
    public string Password
    {
        get
        {
            return this.password;
        }

        set
        {
            this.password = value;
            this.OnPropertyChanged("Password");
        }
    }

    [ValidationCustomHandlerDelegate(DelegateName = "ValidateEmailFormat")]
    private bool ValidateEmailIsFormatted(IMessage failureMessage)
    {
        string[] addressParts = this.Email.Split('@');

        if (addressParts.Length < 2)
        {
            return false;
        }

        string[] domainPiece = addressParts.LastOrDefault().Split('.');
        if (domainPiece.Length < 2)
        {
            return false;
        }

        return true;
    }
}

Now we are down to just a single method that performs validation. The Email property has two validation attributes, a ValidateValueIsNotNull and a ValidateWithCustomHandler attribute. The value is not null attribute is self explanatory, it just ensures the value isn't null and progresses. The ValidateWithCustomHandler allows you to specify a delegate name that you want th validator to use when performing validation. You'll notice that there no longer needs to be a method within the model called Validate() as this is part of the base class and handles invoking all of the validation attributes, and their custom handlers if they exist.

We use validation on the Password property as well, by ensuring it meets the minimum and maximum length requirements we define.

A really cool feature of this, is the ability to perform validation based on other property values. For instance, let's assume that validation for the Password can only fire when the Email property has a value set. If that is the case, then we just modify the Password attributes to specify that the Email property must be valid in order for our validation to fire.

    [ValidateStringIsGreaterThan(GreaterThanValue = 6, ValidateIfMemberValueIsValid = "Email",  FailureMessage = "Password must be greater than 6 characters.", ValidationMessageType = typeof(ErrorMessage))]
    [ValidateStringIsLessThan(LessThanValue = 20, ValidateIfMemberValueIsValid = "Email", FailureMessage = "Password must be less than 20 characters.", ValidationMessageType = typeof(ErrorMessage))]
    public string Password
    {
        get
        {
            return this.password;
        }

        set
        {
            this.password = value;
            this.OnPropertyChanged("Password");
        }
    }

As you can see here, by setting the ValidateIfMemberValueIsValid, the validation will only get fired if the Email property is not empty or null. What if we wanted to have validation fire only if the Email property was empty? We can do that be prepending an exclamation mark in from of the string representation of the Email property.

ValidateIfMemberValueIsValid = "!Email"

This works with boolean values by checking if it is true or false, floats, doubles, ints, longs, shorts and decimals by checking if they are zero or not along with empty strings and null objects.

Finally, the method delegate feature still exists, and can be used by external objects to enforce additional validation on the model. Let's use a view model as an example. Assume the view model has a PasswordConfirmation property, that must equal the users Password before user creation validation is considered acceptable. We can do the following in our view model, within a CreateUser method or an ICommand.Execute method.

    public void Execute(object parameter)
    {
        // Perform validation on the user's built in validation.
        this.AppUser.ValidateAll();

        // Piggy-back on top of the user default validation with an additional level of validation in our view model.
        // We ensure the password and password confirmation matches.
        this.AppUser.ValidateProperty(
            () => PasswordConfirmation.Equals(this.AppUser.Password),
            new ErrorMessage("Passwords do not match!"),
                "Password");

        // Check if there are any errors.
        if (this.AppUser.HasValidationMessages<ErrorMessage data-preserve-html-node="true">())
        {
            return;
        }

        // Do stuff.
        return;
    }

Here, we fire the user validation off, then we piggy-back on it by creating our own validation via an anonymous method, and assigning it to the "Password" property of the user. If validation fails, it will be added to the Users validation message collection. We then check if the user has any validation error messages. If they do, we abort the user creation.

The API still has a bit of tightening up to do before I can upload it, but it should be ready before the end of the week on GitHub. It's taken a bit of time due to ensuring that what I end up here can run properly. I managed to wire it up in to the latest build of the Mud Designer engine tonight which will really help with development.

Building an app targeting desktops & mobile

The Mud Designer is currently being developed to support a wide range of operating systems.

  1. Desktops
    1. Windows 7
    2. Windows 8
    3. Windows 8.1
    4. OS X
  2. Mobile
    1. WinRT 8
    2. WinRT 8.1
    3. Windows Phone 8
    4. Windows Phone 8.1
    5. iOS
    6. Android

To facility this, the engine at the lowest level will be wrote using Portable Class Libraries (PCL) so that I can target all of the operating systems with the same low-level codebase. The engine itself will be developed fairly abstractly, implementing a series of patterns that will help facilitate this requirement, with the bulk of the components hidden behind interfaces.

At the moment the engine is broken up in to 4 layers. From the highest layer to the lowest layer, the following provides an idea of what the overall acrchitectural layout looks like.

  1. App
  2. Engine
  3. Data Access
  4. Services

Each layer is broken down further, to facilitate the level of abstraction needed to provide cross-platform support.

App

The App layer looks like this

  1. Universal App
    1. Windows Phone
    2. WindowsRT
    3. Shared
    4. Presentation
  2. Windows
    1. Desktop
    2. Modules
    3. Infrastructure

As you can see, the mobile apps will be wrote within the Universal App layer while the Windows Desktop apps will be wrote under the Windows layer. Each will be able to target a version of the engine (and it's repositories and services) that is designed specifically for that platform.

I really want to avoid using #if WinRT #elif Win7 #endif kind of macros through-out the engine, as that makes maintaining it difficult and doesn't provide the level of support I want to provide for 3rd party developers. Since the engine will fully support engine and editor plugins, it needs to be modular and flexible enough to allow other developers to target a specific device or platform if they want, without having to rely on macro's.

For instance, the core project contains an IGame interface that exposes the following contract.

/// < summary>
/// Gets or Sets the current World for the game. Contains all of the Realms, Zones and Rooms.
/// < /summary>
ICollection< IWorld> Worlds { get; set; }

Since Universal Apps don't support ObservableCollections in the same manor that WPF Desktop apps do, I can implement the IGame in a Desktop library like this.

/// < summary>
/// Gets or Sets the current World for the game. Contains all of the Realms, Zones and Rooms.
/// < /summary>
public ObservableCollection< IWorld> Worlds { get; set; }

This still satisfies the interface's contract, yet provides support for WPF XAML's binding engine. Ideally, I will be able to build an abstract implementation of IGame in the Core engine, and then have the individual platforms inherit and override/implement the specific components that apply to them.

While the solution is still being organized and structured, this is the initial look of how the over-all application suite will be set up.

Lightroom's dual monitor support

I'm not sure how i went so long without having dual monitor support for editing photo's. This support was essentially non-existant in Aperture. Lightroom handles this in an awesome fashion.

You can run Lightroom on your primary monitor in either standard mode or fullscreen mode. Regardless of how you run it on your primary monitor, you can output the photo's to a secondary monitor for previewing. In the following photo, you can see that I am in Grid view on my primary monitor. In this view, I can rate and add my keywords. I can move between photo's on my primary monitor and view the fullsize photo on my laptop's screen.

This feature works great can be accessed from the dual monitor icon on the bottom left of the screen.

Options for 2nd monitor

The more I use Lightroom, and get used to the different interface, the more I like it and find myself surprised at how long I put up with Apertures lack-luster feature set (relative to Lightrooms).

Wireless, Cross-device, photo syncing in 2014

For the last two years I have used Aperture for storing, organizing and sharing our photos. It wasn't the perfect photo solution, which I've blogged about more than once, when it came to wirelessly getting photos from several devices in to the Aperture library. I did however like how much flexibility it provided me for organization. I know that Lightroom had really good organizational features, but it didn't play well with my iOS devices and Photostream wasn't supported, which made wirelessly uploading my photo's impossible at the time.

The stars have since aligned, with Aperture being discontinued and Adobe releasing Lightroom for iOS, I now have the opportunity to move over to Lightroom and actually end up with a much better solution. After getting all settled in with Lightroom, I am now able to take a picture on my iPhone, open Lightroom on my iPad and make edits, then open up Lightroom on my Mac or Windows PC and have the original photo and it's edits already ready for me to add keywords to and organize. Even better yet, is that my wife's iPhone has Lightroom on it, and every picture she takes, shows up on both of my iOS devices and on our two computers, without any issues. Well worth the $10 a month it's costing, plus I get Photoshop for my machines and iOS devices, so it's a great value for the price.

The setup

I downloaded and installed Lightroom mobile on my iPhone and my two iPads. Then I downloaded and installed Lightroom mobile on my wife's iPhone and her iPad. Finally, I downloaded Lightroom for my Windows machine and my Mac (technically the same machine, just dual booted). Next, I created a collection on my iOS devices, one collection per device. When you create a new collection on a device, you can tell it to auto-import your camera roll. It will automatically upload your photo's to the Lightroom cloud, and sync them to all of your devices, in the background.

Once each of our devices has a collection created, and has the auto-import enabled, we see all of the collections on all of our devices. This allows us to take a picture on any device, and not have to deal with sending to photostream or AirDropping a large number of files. Our photo's just show up on all our devices. If you want, you could create a single collection, and assign it to auto-import on all devices, so all of your photo's go into 1 collection from all devices. We chose to create one collection per device though so we could make finding a photo easier. I know my wife snapped a photo of my daughter on her iPad, so I just go to her iPad collection from my phone and find it.

Here, you can see all of our collections easily accessible on my iPad.

Now that all of our mobile devices were set up with Lightroom, I launched Lightroom on my Mac and discovered that the collections were available there without any set up. Now I can easily select the photo's and import them in to my library, add keywords, edit them and back them up.

One of the other cool features of Lightroom is that I can select any photo from my library, add them to a collection and enable mobile sync on the collection. Now any photo I add to the custom collection will sync to all of our mobile devices. Extremely easy.

Differences

You can technically sync photo's to iOS from Aperture and iPhoto as well, using Photostream. The difference between Photostream and Lightroom Collections is that Photostream still syncs down the photo to your device (albeit a small version), while Lightroom only syncs a thumbnail. Since majority of the time, users won't actually open all 500 photo's to view on their device, you only need 500 thumbnails. As you can see from my photo above, I have 142 photo's from my wife's iPhone, but it's only using 31mb of storage. When you open a photo to view the full photo, Lightroom downloads the full-resolution for you to see. Much better!

With Aperture/iOS, I could share photo's with my wife through Photostream as well, but my wife would end up saving the photo to her camera roll and a duplicate ultimately ended up in my Aperture library. I would spend a lot of time removing duplicates. Luckily, it seems that Lightroom is smart enough to not import duplicates. Another benefit is that I don't have to manually upload 20 photo's that I just took of my daughter to Photostream for her to see. They just show up in our Lightroom.

If I wanted to have a photostream per device, it made things even more complex. Take a picture on my iPhone, and upload to photostream, then take a picture on my iPad 5 minutes later and manually upload to photostream again. With Lightroom, we can take pictures on any device at any time and not deal with uploading. Lightroom handles it all for us with less effort.

Tags & Keywords

Lightroom calls their version of Tags, Keywords. I downloaded an app on my iPad called Photosmith, that lets you create a collection of photos on your Mac (or Windows) and sync the collection of photo's to your iPad. You can then add keywords, edit the photo's, name them and then sync the changes back to Lightroom. It's a great way to 'tag' photo's while on the go. Until the Lightroom Mobile apps support keywording, I'll use Photosmith to do all my keywording while on the go. It's a bit difficult to set up initially, but in the end it works pretty well.

This process is by far the best photo syncing solution I've found, after several years of messing with trying to take care of syncing to the computers wirelessly and sharing across all devices.