Nico's digital footprint

I grew up in the nineties, that makes me awesome by default

Marker based AR with Monogame and WP8

by Nico

Marker based Augmented Reality is really easy thanks to toolkits like SLAR. A bigger challenge is when you want to use those toolkits in a Monogame application. There’s a lot of information out there on how to do this in the classic Silverlight / XNA mashup we used to have in Windows Phone 7 but since XNA isn’t supported in WP8 and Monogame / Silverlight combinations aren’t possible I embarked on a journey to get this done.

Credits

Before I start with this post I would like to give credits to the three articles / demo apps that helped me create this post.

All of these articles contain code that can be found in my demo solution attached to this post.

Displaying the phone’s camera feed

In a XAML application it’s easy to get the camera feed displayed in the app, a videobrush attached to a Canvas or a Rectangle and done. In a Monogame application we’ll have to do a bit more work.

We’ll start with a blank Monogame Windows Phone 8 game.

Note: the current Monogame templates included in the installer are only suited for Visual Studio 2012. However, once the game’s created you can simply open the solution in Visual Studio 2013.

First thing to do is enable the correct capability. In the WMAppManifest GUI, in the Capabilities tab, check the ID_CAP_ISV_CAMERA checkbox.

From here on out, everything we’ll be doing will be in the Game1.cs class. The GamePage files are just for initializing Monogame and rendering the game. Running the app at this point should just give you a nice blue background.

If the app shows it’s pretty blue background, it’s time to declare some private fields in the Game1 class.

Code Snippet
  1. //camera preview
  2. private PhotoCaptureDevice _photoDevice;
  3. private Texture2D _previewTexture;
  4. private bool _newPreviewFrameAvailable;
  5. private int _backBufferXCenter;
  6. private int _backBufferYCenter;
  7. private int _textureYCenter;
  8. private int _textureXCenter;
  9. private float _yScale;
  10. private float _xScale;
  11. private int[] _previewData2;
  12. private int[] _previewData1;
  13. private bool _isFocussing;

Let’s go over these fields

  • _photoDevice will be our access to the phone’s camera
  • _previewTexture will hold the frame currently being drawn, coming from the camera’s previewbuffer
  • _newPreviewFrameAvailable is a flag that will be set to true whenever a new frame is ready to be fetched and drawn
  • _backBufferXCenter and _backBufferYCenter: these 2 fields together form the middle point of the device’s screen, we need this to position the preview image in the middle of the screen
  • _textureYCenter and _textureXCenter: these 2 fields together form the middle point of the preview image.
  • _yScale and _xScale will contain the height and width scale so that we can draw the preview image full screen
  • _previewData1 and _previewData2 will hold the new and previous pixels from the camera’s preview buffer, we need to hold both to prevent one from being overwritten with a new frame while still being drawn
  • _isFocussing is a flag that prevents the focus function of the camera being called multiple times.

The next step is the Initialize method, note that this method needs to be overridden from the base Game class that Game1 inherits. The method itself gets called automatically at game’s start.

Code Snippet
  1. protected override async void Initialize()
  2. {
  3.     _spriteBatch = new SpriteBatch(GraphicsDevice);
  4.  
  5.     Size previewSize = PhotoCaptureDevice.GetAvailablePreviewResolutions(CameraSensorLocation.Back)[0];
  6.     Size captureSize = PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back)[0];
  7.  
  8.     CreateTexture((int)previewSize.Width, (int)previewSize.Height);
  9.  
  10.     _previewData1 = new int[_previewTexture.Width * _previewTexture.Height];
  11.     _previewData2 = new int[_previewTexture.Width * _previewTexture.Height];
  12.  
  13.     _photoDevice = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Back, captureSize);
  14.     _photoDevice.PreviewFrameAvailable += photoDevice_PreviewFrameAvailable;
  15.  
  16.     _backBufferXCenter = GraphicsDevice.Viewport.Width / 2;
  17.     _backBufferYCenter = GraphicsDevice.Viewport.Height / 2;
  18.     _textureXCenter = _previewTexture.Width / 2;
  19.     _textureYCenter = _previewTexture.Height / 2;
  20.     _yScale = (float)GraphicsDevice.Viewport.Width / _previewTexture.Height;
  21.     _xScale = (float)GraphicsDevice.Viewport.Height / _previewTexture.Width;
  22.  
  23.     base.Initialize();
  24. }

First we initialize the spritebatch, this class will be responsible for drawing the 2D textures, meaning in this case, the camera preview.

Next we get the preview size and capture size from the camera in the phone. We create a texture with the CreateTexture method (explained a bit lower) and declare the two arrays that will hold the current and previous frames.

The camera is launched asynchronously on line 13, hooking up the event handler for the PreviewFrameAvailable event on line 14.

Next, the center points for both the device’s screen and the preview texture are calculated, followed by calculating the scale.

Here’s the CreateTexture method

Code Snippet
  1. private void CreateTexture(int textureWidth, int textureHeight)
  2. {
  3.     _previewTexture = new Texture2D(GraphicsDevice, textureWidth, textureHeight);
  4.     Color[] data = new Color[textureWidth * textureHeight];
  5.  
  6.     for (int i = 0; i < textureWidth * textureHeight; i++)
  7.     {
  8.         data[i] = Color.White;
  9.     }
  10.  
  11.     _previewTexture.SetData(data);
  12. }

This method just created a white texture, the size of what we expect the preview frames to be.

Next up is the event handler for the PreviewFrameAvailable event

Code Snippet
  1. void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
  2. {
  3.     _newPreviewFrameAvailable = true;
  4. }

This sets a flag to true, this flag will be checked in the Draw method to prevent synchronization problems between threads.

Almost time to show something on screen! Here’s the Draw method, note that this is also an overridden method. In Monogame Update and Draw are the game loop, they are called multiple times per second. Update is where you would update the world, check for collisions, … While Draw is where all the graphical drawing logic sits.

Code Snippet
  1. protected override void Draw(GameTime gameTime)
  2. {
  3.     if (_newPreviewFrameAvailable)
  4.     {
  5.         GraphicsDevice.Clear(Color.CornflowerBlue);
  6.  
  7.         // a new frame is available, get it from the previewbuffer
  8.         _photoDevice.GetPreviewBufferArgb(_previewData2);
  9.  
  10.         //camera uses RGB, Texture2D uses BGR, swap color channels
  11.         SwapRedBlueChannel(_previewData2);
  12.  
  13.         var previewDataTemp = _previewData1;
  14.         _previewData1 = _previewData2;
  15.         _previewData2 = previewDataTemp;
  16.  
  17.         //Convert the pixel array to a texture
  18.         _previewTexture.SetData(_previewData1);
  19.         _newPreviewFrameAvailable = false;
  20.     }
  21.  
  22.     //draw the previewframe
  23.     _spriteBatch.Begin();
  24.     _spriteBatch.Draw(_previewTexture, new Vector2(_backBufferXCenter, _backBufferYCenter), null,
  25.         Color.White,
  26.         (float)Math.PI / 2.0f, new Vector2(_textureXCenter, _textureYCenter),
  27.         new Vector2(_xScale, _yScale), SpriteEffects.None, 0.0f);
  28.     _spriteBatch.End();
  29.  
  30.     base.Draw(gameTime);
  31. }

First thing we’ll do is check for the flag that is set in the PreviewFrameAvailable event. If that’s true we fetch the ARGB preview buffer from the device. That buffer is an integer array, we pass in one of our two integer arrays, it will get filled with the buffer’s data. A problem we’re having here is that the camera returns RGB values while Texture2D uses BGR values. SwapRedBlueChannel is a small method that swaps those channels. Feel free to comment that line out and see for yourself what it does, all blue colors will show up red on your phone’s screen and vice versa. This method is detailed a bit lower on this page.

The next part is swapping the current frame with the previous frame, this is done to prevent a frame that is currently drawn on screen to be overwritten by a new one.

The SetData method on Texture2D takes in an array and will use that data to create the texture’s image.

And finally, we clear the flag again to wait for the next available frame. We are now ready to draw the image on screen. The drawing is done using the spritebatch, all drawing should happen between spritebatch.Begin() and spritebatch.End().

The Draw method has several overloads. The overload we’re using here gives us the ability to rotate and scale the texture. We need this as Monogame on Windows Phone currently has no landscape support.

Let’s break down the parameters for the Draw method.

  • _previewTexture: our Texture2D that got filled with the preview frame’s data, this is the texture that will get drawn on screen
  • new Vector2(_backBufferXCenter, _backBufferYCenter): The position where the texture will get drawn. By default Monogame uses the upper left corner of the texture to position it, in this overload of the Draw method we can change that upper left point to something else as you’ll see in a few parameters
  • null: we don’t need a source rectangle here, just pass in null
  • Color.White: Draw this texture in its original colors
  • (float)Math.PI / 2.0f: this is the rotation, it rotates the entire image 90 degrees, moving us from portrait to landscape mode
  • new Vector2(_textureXCenter, _textureYCenter): origin point, this moves the rotation and location point from the upper left corner of the texture to its center
  • new Vector2(_xScale, _yScale): scales the image to be fullscreen
  • SpriteEffects.None: no extra effects needed
  • 0.0f: default depth

With all this in place, run the game and you should see your camera image being drawn full screen inside a game. However, the image is not focusing. An image in focus is pretty important for marker detection I believe. Let’s implement that a screen tap focuses the camera.

Focusing the camera

First we need to enable the tap gesture in the game. In the Game1 constructor add this line.

Code Snippet
  1. TouchPanel.EnabledGestures = GestureType.Tap;

The logic will go in the Update part of our gameloop, once again an overridden method.

Code Snippet
  1. protected override async void Update(GameTime gameTime)
  2. {
  3.     if (_photoDevice == null) return;
  4.  
  5.     //if a touch event is available, focus the camera
  6.     if (TouchPanel.IsGestureAvailable)
  7.     {
  8.         if (TouchPanel.ReadGesture().GestureType == GestureType.Tap && !_isFocussing)
  9.         {
  10.             _isFocussing = true;
  11.             await _photoDevice.FocusAsync();
  12.             _isFocussing = false;
  13.         }
  14.     }
  15.  
  16.     base.Update(gameTime);
  17. }

First we check if our camera is already initialized, we can’t focus something that doesn’t exist yet. If a gesture is available and it is a tap gesture we set the is focusing flag to true to prevent another focus call when one is in progress. The focusing itself is as easy as calling the asynchronously FocusAsync function on the camera. Reset the flag and done. The camera should now focus whenever you tap the screen in the game.

Now that we have our camera in place, it’s time for the fun stuff. The marker detection!

Augmented Reality

As mentioned in the beginning of this article, we’re going to use the SLAR toolkit. The problem is that the current released version of SLAR (released in May 2010) isn’t compatible with our Windows Phone 8 project. Luckily we can just pluck the code from its Codeplex page, recompile it and it just works. I took the lazy way out and just added the SLAR project to my solution. You’ll notice that SLAR depends on another library called Matrix3DEx that has the same compatibility issue, luckily for us that project also lives on Codeplex. Here are the links

My solution currently looks like this

Also make sure to copy the folders Common and CommonData to the folder where your solution lives or the project won’t compile. Don’t forget to add a reference to your game for the SLAR project.

Back to the code, in the Game1 class we’ll need some extra fields

Code Snippet
  1. private GrayBufferMarkerDetector _arDetector;
  2. private bool _isInitialized;
  3. private bool _isDetecting;
  4. private byte[] _buffer;
  5. private DetectionResult _markerResult;

The first field is the detector, there are several kind of detectors in SLAR, we’re using the GrayBuffer one here. We need two flags to show that everything is initialized and if a detection is currently running. Last but not least we need a byte array that will store the frame that we’re currently scanning for markers. The detectionresult will hold the result of every detected marker so that we can use it to position our model.

Next we’ll initialize all the AR related bits, I’ve put this in a separate method that gets called from the existing Initialize method.

Code Snippet
  1. private void InitializeAR()
  2. {
  3.     //  Initialize the Detector
  4.     _arDetector = new GrayBufferMarkerDetector();
  5.  
  6.     // Load the marker pattern. It has 16x16 segments and a width of 80 millimeters
  7.     var marker = Marker.LoadFromResource("data/Marker_SLAR_16x16segments_80width.pat", 16, 16, 80);
  8.  
  9.     // The perspective projection has the near plane at 1 and the far plane at 4000
  10.     _arDetector.Initialize((int)_photoDevice.PreviewResolution.Width, (int)_photoDevice.PreviewResolution.Height, 1, 4000, marker);
  11.  
  12.     _isInitialized = true;
  13. }

The way SLAR works is that it loads in a pattern(*.pat) file. That pattern gets searched for in every detect call. You can create your own patterns or use prebuild ones. I’m using one that comes with the SLAR samples. Make sure that the pattern is included with your solution and that its build action is set to Resource. Line 7 loads the pattern, line 10 initializes our detector, passing in the expected resolution, the near and far planes and the marker.

And now for the magical piece of code that does the actual detecting

Code Snippet
  1. private void Detect()
  2. {
  3.     if (_isDetecting || !_isInitialized)
  4.     {
  5.         return;
  6.     }
  7.  
  8.     //Here is where we try to detect the marker
  9.     _isDetecting = true;
  10.  
  11.     try
  12.     {
  13.         // Update buffer size
  14.         var pixelWidth = _photoDevice.PreviewResolution.Width;
  15.         var pixelHeight = _photoDevice.PreviewResolution.Height;
  16.         if (_buffer == null || _buffer.Length != pixelWidth * pixelHeight)
  17.         {
  18.             _buffer = new byte[System.Convert.ToInt32(pixelWidth * pixelHeight)];
  19.         }
  20.  
  21.         // Grab snapshot for the marker detection
  22.         _photoDevice.GetPreviewBufferY(_buffer);
  23.  
  24.         //Detect the markers
  25.         _arDetector.Threshold = 100;
  26.         var dr = _arDetector.DetectAllMarkers(_buffer, System.Convert.ToInt32(pixelWidth), System.Convert.ToInt32(pixelHeight));
  27.  
  28.         //Set the marker result if the marker is found
  29.         _markerResult = dr.HasResults ? dr[0] : null;
  30.     }
  31.     finally
  32.     {
  33.         _isDetecting = false;
  34.     }
  35. }

The Detect method will get called from a timer, more on that a bit lower in the article.

First we’ll check if it’s okay to do detection, a detection cannot be in progress and everything should be initialized. Then we’ll set the is detecting flag to true.

We’ll keep the width and height of the camera’s preview resolution in two variables and initialize the buffer if necessary. We fill the buffer with the luminance data from the camera by calling the GetPreviewBufferY method, this differs from the method we’re using to show the camera stream. The luminance data is sufficient for SLAR to do its detection. Then we pass in the buffer to the marker detector, together with the frame’s width and height. If a result is found we’ll keep it in the DetectionResult field, if not we set the field to null. As last part we clear the is detecting flag so we are ready to detect again.

Very easy to use, SLAR takes care of all the rest. All we need to do is call the Detect method. In the overridden Initialize method, add this.

Code Snippet
  1. InitializeAR();
  2.  
  3. //marker detection sits on another counter than the update / draw mechanism to prevent excessive detection
  4. Deployment.Current.Dispatcher.BeginInvoke(() =>
  5. {
  6.     //Runt the detection separate from the update
  7.     var dispatcherTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
  8.     dispatcherTimer.Tick += (sender, e1) => Detect();
  9.     dispatcherTimer.Start();
  10. });

The detection runs separately from the gameloop to prevent excessive calls to the detect method. I only want to detect every 100 milliseconds.

When you run the game now and point the camera to the marker, nothing happens. That’s perfectly normal. The marker is getting detected but we’re not doing anything with the detection results yet. Let’s add a 3D model to our game and position it on the marker.

Adding the model

For the 3D model I choose a model of the Tardis I found online (if you don’t know what the Tardis is, go out and buy all the Doctor Who dvd boxes you can find and lock yourself in your room for a few months. Thank me afterwards).

To use this in Monogame you’ll need to push it through either the XNA or the Monogame pipeline to convert it into an XNB file. I’m not going to detail how to do this here, lots of info out there. If you want a quick start, grab the XNB file from my demo project.

Add a folder called Content to your solution, add the XNB file in there and set its build action to Content. Next, we’ll once again add some fields to the Game1 class.

Code Snippet
  1. private Vector3 _modelPosition;
  2. private Vector3 _cameraPosition;
  3. private Model _tardis;
  4. private float _aspectRatio;

The names speak for themselves, we’ve got two vectors, one for the position of the Tardis, one for the position of the camera. We’ve got the Tardis model and a field that holds the aspect ratio.

In the Initialize method, right before the call to InitializeAR add these lines.

Code Snippet
  1. _aspectRatio = _graphics.GraphicsDevice.Viewport.AspectRatio;
  2. _modelPosition = Vector3.Zero;
  3. _cameraPosition = new Vector3(0, 0, 50);
  4.  
  5. InitializeAR();

These are just basic vectors that we’ll use to calculate the actual position on screen where we need to render our model.

Next, we’ll need to load the model into memory. This is done in the overridden LoadContent method in the Game1 class.

Code Snippet
  1. protected override void LoadContent()
  2. {
  3.     _tardis = Content.Load<Model>("tardis");
  4.  
  5.     base.LoadContent();
  6. }

There’s no need to specify that the model lives in the Content folder as Monogame assumes that the project contains a folder called Content and that’s where it looks for XNB files.

Before we go into the draw logic of the model, there’s one problem that we’ll need to tackle. SLAR is using Matrix3D classes while Monogame has its own Matrix class. We’ll need a way to convert Matrix3D to Matrix. Here’s an extension method that does just that.

Code Snippet
  1. public static class MatrixConverter
  2. {
  3.     /// <summary>
  4.     /// Convert a Silverlight matrix into an Xna matrix
  5.     /// </summary>
  6.     /// <param name="matrix"></param>
  7.     /// <returns></returns>
  8.     public static Matrix ToXnaMatrix(this System.Windows.Media.Media3D.Matrix3D matrix)
  9.     {
  10.         var m = new Matrix(
  11.            (float)matrix.M11, (float)matrix.M12, (float)matrix.M13, (float)matrix.M14,
  12.            (float)matrix.M21, (float)matrix.M22, (float)matrix.M23, (float)matrix.M24,
  13.            (float)matrix.M31, (float)matrix.M32, (float)matrix.M33, (float)matrix.M34,
  14.            (float)matrix.OffsetX, (float)matrix.OffsetY, (float)matrix.OffsetZ, (float)matrix.M44);
  15.  
  16.         return m;
  17.     }
  18. }

Now, onto the Draw method. The position of the following code is really important. Monogame draws its stuff in the order that you feed it its instructions. Meaning that we first need to draw the camera feed, then the Tardis model. That way the model will be nicely overlaid over the image.

In the Draw method, after Spritebatch.End and before base.Draw add these lines

Code Snippet
  1. if (_markerResult != null)
  2. {
  3.     //a marker is detected, draw the Tardis model
  4.     var result = _markerResult;
  5.     _graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  6.     _graphics.GraphicsDevice.BlendState = BlendState.Opaque;
  7.     _graphics.GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
  8.  
  9.     // Copy any parent transforms.
  10.     Matrix[] transforms = new Matrix[_tardis.Bones.Count];
  11.     _tardis.CopyAbsoluteBoneTransformsTo(transforms);
  12.  
  13.     // Draw the model. A model can have multiple meshes, so loop.
  14.     foreach (ModelMesh mesh in _tardis.Meshes)
  15.     {
  16.         // This is where the mesh orientation is set, as well as our camera and projection.
  17.         foreach (BasicEffect effect in mesh.Effects)
  18.         {
  19.             effect.EnableDefaultLighting();
  20.             effect.World = Matrix.CreateScale(0.1f) *
  21.                            (transforms[mesh.ParentBone.Index] * mesh.ParentBone.Transform *
  22.                             Matrix.CreateTranslation(_modelPosition) *
  23.                             result.Transformation.ToXnaMatrix());
  24.  
  25.             effect.View = Matrix.CreateLookAt(_cameraPosition, Vector3.Zero, Vector3.Up);
  26.             effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
  27.                 _aspectRatio, 1.0f, 10000f);
  28.         }
  29.  
  30.         // Draw the mesh, using the effects set above.
  31.         mesh.Draw();
  32.     }
  33. }

Since we’re using a 3D model we can’t use the spritebatch to draw it. We set some properties onto the graphicsdevice first. Then we grab all transforms that are included in the model. The Tardis model I’m using is very simple, it’s just a box, so no transformations there.

We loop through all the meshes in the model, for each mesh we loop through its effects and that’s where we set the position. We use the detection result his transformation matrix to calculate the world for each effect and we draw each mesh.

Here’s the result:

Conclusion

The end result might not seem like much, but that’s because my 3D monogame skills are very lacking Glimlach but just consider what we’ve done here. We’ve added a camera stream into a game, we’ve used that same stream to detect a certain pattern and we’ve positioned a game element onto that pattern. From here I’ll leave the rest to your imagination.

The project can be downloaded from my Skydrive.


Tags:

Augmented Reality | .Net | MonoGame | Devices | WP8 | XNA

Data validation in Windows Phone 8

by Nico

Whenever you need to do data input you’re probably going to add some form of data validation. There is a ton of information out there on how to do this in Silverlight and WPF but information on Windows Phone 8 is rather hard to find. After doing some research on the topic I’ve landed on the Fluent Validation library available on Nuget and decided to write something on the subject myself. I’ve put together a quick sample, including integration with SimpleIoc in MVVM Light.

Adding the package

We start of with an empty solution, add the MVVM Light libraries to it, bind the datacontext of MainPage to MainViewModel as you would usually do. Once the MVVM setup is complete it’s time to add the Fluent Validation package to the project.

Install-Package FluentValidation

Fluent Validation is an open source project available on Codeplex

Fluent Validation has libraries for

  • .net 4.0
  • MVC 3
  • MVC 4
  • MVC 5
  • Portable

The settings of the portable class library are

Meaning that the implementation I’m about to show will work just as well on Windows Store apps, so +1 for code sharing!

Creating the Model

The demo app is a simple registration form, a new user will be able to enter his name, birthdate and country. We’ll build validation rules on those properties later on. We’ll only have one model class in this app, called Member.

Code Snippet
  1. public class Member
  2. {
  3.     public string Name { get; set; }
  4.     public DateTime BirthDate { get; set; }
  5.     public string Country { get; set; }
  6. }

Setting up the validation

There is some work involved in getting everything setup, don’t worry, it’s hardly rocket science.

We’re going to create a ValidaterFactory. We need this because we’re going to use SimpleIoc to inject the validators into the ViewModels. If you don’t want to do this you can just create the validators and instantiate them whenever you need one, the Codeplex page of Fluent Validation has great documentation on this.

A ValidatorFactory inherits from the ValidatorFactoryBase class included in the library

Code Snippet
  1. public class ValidatorFactory : ValidatorFactoryBase
  2. {
  3.     public ValidatorFactory()
  4.     {
  5.         //register the Validators
  6.         SimpleIoc.Default.Register<IValidator<Member>, MemberValidator>();
  7.     }
  8.  
  9.     public override IValidator CreateInstance(Type validatorType)
  10.     {
  11.         return SimpleIoc.Default.GetInstance(validatorType) as IValidator;
  12.     }
  13. }

The constructor of the ValidatorFactory is where I’m registering all validators. You could do this in the ViewModelLocator as well, like any other class / repository / viewmodel but this keeps my VMLocator cleaner and keeps the validation logic a bit closer together.

The CreateInstance function needs to be overridden and returns the instance of the requested validator.

Building a validator

Almost there, in this step we’re building a validator for the Member class. A validator inherits from AbstractValidator<T> where T is the class you want to validate.

Code Snippet
  1. public class MemberValidator : AbstractValidator<Member>
  2. {
  3.     public MemberValidator()
  4.     {
  5.         RuleFor(member => member.Name).NotEmpty();
  6.         RuleFor(member => member.BirthDate).LessThan(DateTime.Today);
  7.     }
  8. }

In the constructor of the validator we can finally start to add some rules. In this case we’re saying that the Name property should not be empty and that the birthdate property should be before today’s date.

We’ll need to register this class into our IOC, so in ViewModelLocator add this line

Code Snippet
  1. SimpleIoc.Default.Register<ValidatorFactory>(true);

We pass in ‘true’ as parameter to make sure the object is instantiated at the moment of registration, that way all validators are registered in the IOC as well (as seen in the ValidatorFactory’s constructor).

As a reference, these are all the built in validators of Fluent Validation

  • NotNull
  • NotEmpty
  • NotEqual
  • Equal
  • Length
  • LessThan
  • LessThanOrEqual
  • GreaterThan
  • GreaterThanOrEqual
  • Predicate
  • RegEx
  • Email

Quite an impressive list, and probably most of what you’ll need. Just in case that the one that you need isn’t included, you can build your own, we’ll discuss that in a bit. Let’s get these rules to work first.

Validating data

So we have a factory, we have a validator, all are getting registered in our IOC, time to hook up the ViewModels and getting some validation done.

In the MainViewModel we add a property of type Member that we’ll bind against

Code Snippet
  1. private Member _newMember;
  2. public Member NewMember
  3. {
  4.     get { return _newMember; }
  5.     set
  6.     {
  7.         if (_newMember == value) return;
  8.  
  9.         _newMember = value;
  10.  
  11.         RaisePropertyChanged(() => NewMember);
  12.     }
  13. }

Next, we’ll add two fields, one for the validator and one for the save command

Code Snippet
  1. private ICommand _saveMemberCommand;
  2. private IValidator<Member> _validator;

Next up is the ICommand property for the Save Command

Code Snippet
  1. public ICommand SaveMemberCommand
  2. {
  3.     get { return _saveMemberCommand ?? (_saveMemberCommand = new RelayCommand(SaveMember)); }
  4. }

Before we dive into the SaveMember method, we’ll need to do some constructor injection and initialization.

Code Snippet
  1. public MainViewModel(ValidatorFactory validator)
  2. {
  3.     NewMember = new Member
  4.     {
  5.         BirthDate = DateTime.Today
  6.     };
  7.  
  8.     _validator = validator.GetValidator<Member>();
  9. }

As a parameter we get our ValidatorFactory, using its GetValidator<T> function we can fill up the _validator field. NewMember gets instantiated and the BirthDate property is set to a default of today.

And last but not least, the SaveMember method

Code Snippet
  1. private void SaveMember()
  2. {
  3.     if (IsValid())
  4.     {
  5.         MessageBox.Show("Registration completed!");
  6.     }
  7. }
  8.  
  9. private bool IsValid()
  10. {
  11.     ValidationResult validationResult = _validator.Validate(NewMember);
  12.  
  13.     if (!validationResult.IsValid)
  14.     {
  15.         ShowErrors(validationResult.Errors);
  16.  
  17.         return false;
  18.     }
  19.  
  20.     return true;
  21. }
  22.  
  23. private void ShowErrors(IEnumerable<ValidationFailure> errors)
  24. {
  25.     StringBuilder builder = new StringBuilder();
  26.  
  27.     builder.AppendLine("The following errors occured:");
  28.     foreach (ValidationFailure error in errors)
  29.     {
  30.         builder.AppendLine("- " + error.ErrorMessage);
  31.     }
  32.  
  33.     MessageBox.Show(builder.ToString(), "error", MessageBoxButton.OK);
  34. }

To validate an instance we call the Validate function on a validator for that type, in this case ‘Member’. That function returns a ValidationResult. ValidationResult contains a bool property IsValid and a list of errors that it found.

We iterate over this list and put every error into a StringBuilder to get a nice error message in a messagebox. If I try to save with an empty name and today’s date as birthdate I get this.

Our validation is working! But those messages could be a bit better. Fluent Validator provides us with options to adjust the property name or the entire message. Change the constructor of the MemberValidator to this

Code Snippet
  1. public MemberValidator()
  2. {
  3.     RuleFor(member => member.Name).NotEmpty().WithMessage("You should enter your name");
  4.     RuleFor(member => member.BirthDate).LessThan(DateTime.Today).WithName("date of birth");
  5. }

On line 3 we use .WithMessage(“”) to replace the default message by whatever we want. Line 4 replaces the default name of the property, the result is similar yet slightly different

Adding a custom validator

Let’s say that we want to add validation to the Country property. This property must have a value of either ‘Belgium’, ‘Netherlands’ or ‘Luxemburg’ (this is called the BeNeLux). Obviously, this isn’t included as one simple validator so we’ll just build it ourselves.

We’ll need a class that inherits from PropertyValidator and overrides the IsValid function.

Code Snippet
  1. public class CountryMustBeInBenelux : PropertyValidator
  2. {
  3.     public CountryMustBeInBenelux()
  4.         : base("{PropertyName} is not a Benelux country")
  5.     {
  6.  
  7.     }
  8.  
  9.     protected override bool IsValid(PropertyValidatorContext context)
  10.     {
  11.         if (context.PropertyValue == null)
  12.             return false;
  13.  
  14.         string country = context.PropertyValue.ToString().ToLower();
  15.  
  16.         return country == "belgium" || country == "netherlands" || country == "luxemburg";
  17.     }
  18. }

The string that we pass in to the base constructor is the default message that will be used for this validator. The IsValid function takes a parameter of PropertyValidatorContext. This will contain the property that we want to validate and its value.

Next, in the MemberValidator constructor we add the validation for the Country property

Code Snippet
  1. public MemberValidator()
  2. {
  3.     RuleFor(member => member.Name).NotEmpty().WithMessage("You should enter your name");
  4.     RuleFor(member => member.BirthDate).LessThan(DateTime.Today).WithName("date of birth");
  5.  
  6.     RuleFor(member => member.Country).SetValidator(new CountryMustBeInBenelux());
  7. }

As you can see we need to use the SetValidator function to add our own validator.

The result now is this (I’ve entered a name and a valid birthdate to clean up the error list a bit)

 

Conclusion

In this article I’ve discussed the use of Fluent Validator to do data validation in Windows Phone 8 apps. I’ve used the portable version of the library so these examples should work just fine on Windows 8 as well.

The Fluent Validator library has many more options than what I’ve discussed here and they are all well documented, head over the the Codeplex page if you want to do more and deeper validation.

The sample project can be found on my SkyDrive


Tags:

.Net | Data | Validation | Fluent | MVVM Light | NuGet | WP8 | Windows 8 | WinRT | XAML

  Log in

About the author

Hi,

My name is Nico, I’m an MVP Windows Platform Development living in Belgium.
I’m currently employed as a .NET consultant at RealDolmen, one of Belgium’s leading IT single source providers.

I'm also founding member and board member of the Belgian Metro App Developer Network, a user group focussed on Windows 8 and Windows Phone development. If you're in Belgium feel free to drop by if we're doing an event. http://www.madn.be

Since June 2012 I'm a proud member of Microsoft's Extended Experts Team Belgium. And in February 2013 I became a member of DZone's Most Valuable Bloggers family.

In 2013 I became a book author and wrote "Windows 8 app projects, XAML & C# edition".

In 2014 I received the MVP award for the very first time.

I hope to get feedback from my readers either through comments, mail (nico_vermeir@hotmail.com), twitter, facebook, …

 

mvp

 

mvp

 

 

MeetLogo

 

MVBLogo

mybook

 

Month List