changeset 58:241e2f22ed3c

Latest version
author Steven Hollidge <stevenhollidge@hotmail.com>
date Sat, 21 Apr 2012 15:06:48 +0100
parents 558c09d76726
children 3591c26bd63e
files SilverlightValidation/Libs/System.Windows.Controls.Data.dll SilverlightValidation/Libs/System.Windows.Controls.Navigation.dll SilverlightValidation/SilverlightGlimpse/Controls/BrokenBindingsViewer.xaml SilverlightValidation/SilverlightGlimpse/Controls/BrokenBindingsViewer.xaml.cs SilverlightValidation/SilverlightGlimpse/Controls/ExceptionsViewer.xaml SilverlightValidation/SilverlightGlimpse/Controls/ExceptionsViewer.xaml.cs SilverlightValidation/SilverlightGlimpse/Controls/GlimpseViewer.xaml SilverlightValidation/SilverlightGlimpse/Controls/GlimpseViewer.xaml.cs SilverlightValidation/SilverlightGlimpse/Controls/LoadExceptionViewer.xaml SilverlightValidation/SilverlightGlimpse/Controls/LoadExceptionViewer.xaml.cs SilverlightValidation/SilverlightGlimpse/Models/BrokenBinding.cs SilverlightValidation/SilverlightGlimpse/Models/ExceptionWrapper.cs SilverlightValidation/SilverlightGlimpse/Properties/AppManifest.xml SilverlightValidation/SilverlightGlimpse/Properties/AssemblyInfo.cs SilverlightValidation/SilverlightGlimpse/Services/GlimpseService.cs SilverlightValidation/SilverlightGlimpse/SilverlightGlimpse.csproj SilverlightValidation/SilverlightValidation.Web/ClientBin/SilverlightValidation.xap SilverlightValidation/SilverlightValidation.sln SilverlightValidation/SilverlightValidation/App.xaml.cs SilverlightValidation/SilverlightValidation/Commands/RelayCommand.cs SilverlightValidation/SilverlightValidation/Data/Factory.cs SilverlightValidation/SilverlightValidation/Diagrams/Form.cd SilverlightValidation/SilverlightValidation/Diagrams/List.cd SilverlightValidation/SilverlightValidation/Interfaces/ICloneable.cs SilverlightValidation/SilverlightValidation/Interfaces/IUserModel.cs SilverlightValidation/SilverlightValidation/Models/UserModel.cs SilverlightValidation/SilverlightValidation/Overview.cd SilverlightValidation/SilverlightValidation/RelayCommand.cs SilverlightValidation/SilverlightValidation/SilverlightValidation.csproj SilverlightValidation/SilverlightValidation/UserModel.cs SilverlightValidation/SilverlightValidation/UserModelValidator.cs SilverlightValidation/SilverlightValidation/UserView.xaml SilverlightValidation/SilverlightValidation/UserView.xaml.cs SilverlightValidation/SilverlightValidation/UserViewModel.cs SilverlightValidation/SilverlightValidation/Validators/UserModelValidator.cs SilverlightValidation/SilverlightValidation/ViewModelBase.cs SilverlightValidation/SilverlightValidation/ViewModels/UserListViewModel.cs SilverlightValidation/SilverlightValidation/ViewModels/UserViewModel.cs SilverlightValidation/SilverlightValidation/ViewModels/ViewModelBase.cs SilverlightValidation/SilverlightValidation/Views/UserListView.xaml SilverlightValidation/SilverlightValidation/Views/UserListView.xaml.cs SilverlightValidation/SilverlightValidation/Views/UserView.xaml SilverlightValidation/SilverlightValidation/Views/UserView.xaml.cs
diffstat 43 files changed, 1868 insertions(+), 693 deletions(-) [+]
line wrap: on
line diff
Binary file SilverlightValidation/Libs/System.Windows.Controls.Data.dll has changed
Binary file SilverlightValidation/Libs/System.Windows.Controls.Navigation.dll has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Controls/BrokenBindingsViewer.xaml	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,9 @@
+<UserControl x:Class="SilverlightGlimpse.Controls.BrokenBindingsViewer"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+    <Grid x:Name="LayoutRoot" Background="White">
+        <ScrollViewer>
+            <ItemsControl x:Name="icBrokenBindings" />
+        </ScrollViewer>
+    </Grid>
+</UserControl>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Controls/BrokenBindingsViewer.xaml.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,91 @@
+using System.Reflection;
+using System.Windows.Data;
+using SilverlightGlimpse.Models;
+using System.Windows;
+using System.Diagnostics;
+using System.Windows.Media;
+
+namespace SilverlightGlimpse.Controls
+{
+    public partial class BrokenBindingsViewer
+    {
+        public BrokenBindingsViewer()
+        {
+            InitializeComponent();
+        }
+
+        private void BrokenBindings_Loaded(object sender, RoutedEventArgs e)
+        {
+            this.icBrokenBindings.Items.Clear();
+            LoadBrokenBindings(GlimpseService.CreateInstance.RootVisual);
+        }
+
+        private void LoadBrokenBindings(UIElement uiElement)
+        {
+            var frameworkElement = uiElement as FrameworkElement;
+
+            if (frameworkElement != null)
+            {
+                foreach (var fieldInfo in frameworkElement.GetType().GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static))
+                {
+                    if (object.ReferenceEquals(fieldInfo.FieldType, typeof(DependencyProperty)))
+                    {
+                        var bindingExpression = frameworkElement.GetBindingExpression((DependencyProperty)fieldInfo.GetValue(null));
+
+                        if (bindingExpression != null && bindingExpression.ParentBinding.Source == null && bindingExpression.ParentBinding.RelativeSource == null)
+                        {
+                            var isInherited = false;
+
+                            if (frameworkElement.DataContext != null && !string.IsNullOrEmpty(bindingExpression.ParentBinding.Path.Path))
+                            {
+                                foreach (var propertyInfo in frameworkElement.DataContext.GetType().GetProperties(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Instance))
+                                {
+                                    if (string.Compare(propertyInfo.Name, bindingExpression.ParentBinding.Path.Path) == 0)
+                                    {
+                                        isInherited = true;
+                                        break; // TODO: might not be correct. Was : Exit For
+                                    }
+                                }
+                            }
+
+                            if (isInherited)
+                            {
+                                break; // TODO: might not be correct. Was : Exit For
+                            }
+
+                            //this code handles empty bindings on the Button controls
+                            //I'll have to look into why the Button has an empty or unresolved binding
+                            if (string.IsNullOrEmpty(frameworkElement.Name) 
+                                && frameworkElement.GetType().Name == "TextBlock" 
+                                && fieldInfo.Name == "TextProperty" 
+                                && string.IsNullOrEmpty(bindingExpression.ParentBinding.Path.Path))
+                            {
+                                break; // TODO: might not be correct. Was : Exit For
+                            }
+
+                            BrokenBinding objBrokenBinding = new BrokenBinding(
+                                frameworkElement.Name, 
+                                frameworkElement.GetType().Name, 
+                                fieldInfo.Name, 
+                                bindingExpression.ParentBinding.Path.Path);
+                            this.icBrokenBindings.Items.Add(objBrokenBinding);
+                            Debug.WriteLine("Broken Binding - ", objBrokenBinding.ToString());
+                        }
+                    }
+                }
+
+                int children = VisualTreeHelper.GetChildrenCount(frameworkElement);
+
+                for (int j = 0; j <= children - 1; j++)
+                {
+                    FrameworkElement child = VisualTreeHelper.GetChild(frameworkElement, j) as FrameworkElement;
+
+                    if (child != null)
+                    {
+                        LoadBrokenBindings(child);
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Controls/ExceptionsViewer.xaml	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,105 @@
+<UserControl x:Class="SilverlightGlimpse.Controls.ExceptionsViewer"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+    <Grid x:Name="LayoutRoot" Background="White">
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="250" />
+            <ColumnDefinition Width="*" />
+        </Grid.ColumnDefinitions>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="*" />
+            <RowDefinition Height="Auto" />
+        </Grid.RowDefinitions>
+
+        <TextBlock Grid.ColumnSpan="2"
+                   Margin="3.5"
+                   VerticalAlignment="Center"
+                   FontSize="18"
+                   Foreground="Red"
+                   Text="Exceptions Viewer" />
+
+        <ListBox x:Name="lbExceptions"
+                 Grid.Row="1"
+                 Margin="3.5"
+                 ItemsSource="{Binding}"
+                 SelectionChanged="lbExceptions_SelectionChanged" />
+
+        <ScrollViewer Grid.Row="1"
+                      Grid.Column="1"
+                      Margin="3.5"
+                      DataContext="{Binding ElementName=lbExceptions,
+                                            Path=SelectedItem}">
+            <Grid>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="Auto" />
+                </Grid.RowDefinitions>
+
+                <Rectangle Fill="BlanchedAlmond" />
+                <TextBlock x:Name="tbAction"
+                           FontSize="14"
+                           Text="Action"
+                           TextDecorations="Underline" />
+                <TextBlock Grid.Row="1"
+                           FontSize="11"
+                           Text="{Binding Path=Action}"
+                           TextWrapping="Wrap"
+                           Visibility="{Binding ElementName=tbAction,
+                                                Path=Visibility}" />
+
+                <Rectangle Grid.Row="2" Fill="BlanchedAlmond" />
+                <TextBlock Grid.Row="2"
+                           Margin="0,7,0,0"
+                           FontSize="14"
+                           Text="Control Name"
+                           TextDecorations="Underline"
+                           Visibility="{Binding ElementName=tbAction,
+                                                Path=Visibility}" />
+                <TextBlock Grid.Row="3"
+                           FontSize="11"
+                           Text="{Binding Path=ControlName}"
+                           TextWrapping="Wrap"
+                           Visibility="{Binding ElementName=tbAction,
+                                                Path=Visibility}" />
+
+                <Rectangle Grid.Row="4" Fill="BlanchedAlmond" />
+                <TextBlock Grid.Row="4"
+                           Margin="0,7,0,0"
+                           FontSize="14"
+                           Text="Message"
+                           TextDecorations="Underline" />
+                <TextBlock Grid.Row="5"
+                           FontSize="11"
+                           Text="{Binding Path=Exception.Message}"
+                           TextWrapping="Wrap" />
+
+                <Rectangle Grid.Row="6" Fill="BlanchedAlmond" />
+                <TextBlock Grid.Row="6"
+                           Margin="0,7,0,0"
+                           FontSize="14"
+                           Text="Stack Trace"
+                           TextDecorations="Underline" />
+                <TextBlock Grid.Row="7"
+                           FontSize="11"
+                           Text="{Binding Path=Exception.StackTrace}"
+                           TextWrapping="Wrap" />
+
+            </Grid>
+        </ScrollViewer>
+        <Button Grid.Row="2"
+                Grid.Column="1"
+                Margin="11"
+                HorizontalAlignment="Right"
+                VerticalAlignment="Center"
+                Click="ClearExceptions_Click"
+                Content="Clear Exceptions"
+                Padding="7" />
+    </Grid>
+</UserControl>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Controls/ExceptionsViewer.xaml.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,38 @@
+using System;
+using System.Windows;
+using SilverlightGlimpse.Models;
+using System.Windows.Controls;
+
+namespace SilverlightGlimpse.Controls 
+{
+    public partial class ExceptionsViewer
+    {
+        public ExceptionsViewer()
+        {
+            InitializeComponent();
+        }
+
+        private void ClearExceptions_Click(object sender, RoutedEventArgs e)
+        {
+            GlimpseService.CreateInstance.HostExceptions.Clear();
+        }
+
+        private void ExceptionsViewer_Loaded(object sender, RoutedEventArgs e)
+        {
+            this.DataContext = GlimpseService.CreateInstance.HostExceptions;
+            if (GlimpseService.CreateInstance.HostExceptions.Count > 0)
+                this.lbExceptions.SelectedIndex = 0;
+        }
+
+        private void lbExceptions_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (this.lbExceptions.SelectedItem != null && this.lbExceptions.SelectedItem is ExceptionWrapper)
+            {
+                if (((ExceptionWrapper)this.lbExceptions.SelectedItem).IsValidationException)
+                    this.tbAction.Visibility = Visibility.Visible;
+                else
+                    this.tbAction.Visibility = Visibility.Collapsed;
+            }
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Controls/GlimpseViewer.xaml	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,79 @@
+<UserControl x:Class="SilverlightGlimpse.Controls.GlimpseViewer"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:c="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
+             xmlns:localc="clr-namespace:SilverlightGlimpse.Controls">
+    <Grid x:Name="LayoutRoot" Background="Khaki">
+        <Grid.Resources>
+            <SolidColorBrush x:Name="noExceptionsBrush" Color="LightGreen" />
+            <SolidColorBrush x:Name="hasExceptionsBrush" Color="Red" />
+        </Grid.Resources>
+        <Grid x:Name="layoutInstrumentPanel">
+            <StackPanel Orientation="Horizontal">
+                <Grid Margin="7">
+                    <Ellipse x:Name="elpValidationExceptions"
+                             Width="40"
+                             Height="40"
+                             Fill="LightGreen"
+                             Stroke="Brown"
+                             StrokeThickness="2" />
+                    <TextBlock x:Name="tbValidationExceptions"
+                               HorizontalAlignment="Center"
+                               VerticalAlignment="Center"
+                               FontSize="12"
+                               FontWeight="Bold"
+                               Text="0"
+                               ToolTipService.ToolTip="Binding Exception Count" />
+                </Grid>
+                <Grid Margin="7">
+                    <Ellipse x:Name="elpUnhandledExceptions"
+                             Width="40"
+                             Height="40"
+                             Fill="LightGreen"
+                             Stroke="Brown"
+                             StrokeThickness="2" />
+                    <TextBlock x:Name="tbUnhandledExceptions"
+                               HorizontalAlignment="Center"
+                               VerticalAlignment="Center"
+                               FontSize="12"
+                               FontWeight="Bold"
+                               Text="0"
+                               ToolTipService.ToolTip="Unhandled Exception Count" />
+                </Grid>
+                <Button x:Name="btnExpand"
+                        Margin="7"
+                        VerticalAlignment="Center"
+                        Content="Expand" />
+            </StackPanel>
+        </Grid>
+        <Grid x:Name="layoutViewer" Visibility="Collapsed">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="Auto" />
+                <RowDefinition Height="*" />
+            </Grid.RowDefinitions>
+            <TextBlock Margin="3.5"
+                       VerticalAlignment="Center"
+                       FontSize="18"
+                       Foreground="DarkGreen"
+                       Text="Glimpse Viewer" />
+            <Button x:Name="btnContract"
+                    Margin="7"
+                    HorizontalAlignment="Right"
+                    VerticalAlignment="Center"
+                    Content="Contract" />
+            <c:TabControl Grid.Row="1"
+                          Width="690"
+                          Height="390"
+                          Background="Khaki"
+                          SelectedIndex="2">
+                <c:TabItem Header="Exceptions">
+                    <localc:ExceptionsViewer />
+                </c:TabItem>
+                <c:TabItem Header="Bindings with no Source">
+                    <localc:BrokenBindingsViewer />
+                </c:TabItem>
+            </c:TabControl>
+        </Grid>
+    </Grid>
+
+</UserControl>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Controls/GlimpseViewer.xaml.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,52 @@
+using System.Collections.Specialized;
+using System.Windows;
+using SilverlightGlimpse.Models;
+
+namespace SilverlightGlimpse.Controls
+{
+    public partial class GlimpseViewer
+    {
+        public GlimpseViewer()
+        {
+            InitializeComponent();
+            this.DataContext = GlimpseService.CreateInstance;
+            GlimpseService.CreateInstance.HostExceptions.CollectionChanged += HostExceptions_CollectionChanged;
+        }
+
+        private void btnContract_Click(object sender, System.Windows.RoutedEventArgs e)
+        {
+            this.layoutViewer.Visibility = Visibility.Collapsed;
+        }
+
+        private void btnExpand_Click(object sender, System.Windows.RoutedEventArgs e)
+        {
+            this.layoutViewer.Visibility = Visibility.Visible;
+        }
+
+        private void HostExceptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+        {
+            int unhandledExceptionCount = 0;
+            int validationExceptionCount = 0;
+
+            foreach (ExceptionWrapper ew in GlimpseService.CreateInstance.HostExceptions)
+            {
+                if (ew.IsValidationException)
+                    validationExceptionCount++;
+                else
+                    unhandledExceptionCount++;
+            }
+
+            this.tbValidationExceptions.Text = validationExceptionCount.ToString();
+
+            this.elpValidationExceptions.Fill = validationExceptionCount == 0
+                ? this.noExceptionsBrush
+                : this.hasExceptionsBrush;
+
+            this.tbUnhandledExceptions.Text = unhandledExceptionCount.ToString();
+
+            this.elpUnhandledExceptions.Fill = unhandledExceptionCount == 0
+                ? this.noExceptionsBrush
+                : this.hasExceptionsBrush;
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Controls/LoadExceptionViewer.xaml	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,79 @@
+<UserControl x:Class="SilverlightGlimpse.Controls.LoadExceptionViewer"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+    <Border Width="700"
+            Height="375"
+            Margin="11"
+            Background="LightYellow"
+            BorderBrush="Red"
+            BorderThickness="2"
+            CornerRadius="20"
+            Padding="11">
+        <Grid x:Name="LayoutRoot">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="200" />
+                <ColumnDefinition Width="*" />
+            </Grid.ColumnDefinitions>
+            <Grid.RowDefinitions>
+                <RowDefinition Height="Auto" />
+                <RowDefinition Height="*" />
+            </Grid.RowDefinitions>
+            <TextBlock Grid.ColumnSpan="2"
+                       Margin="3.5"
+                       VerticalAlignment="Center"
+                       FontSize="18"
+                       Foreground="Red"
+                       Text="Exception Viewer" />
+
+            <ListBox x:Name="lbExceptions"
+                     Grid.Row="1"
+                     Margin="3.5"
+                     DisplayMemberPath="Message" />
+
+            <TextBlock x:Name="txtSourceLocation"
+                       Grid.ColumnSpan="2"
+                       HorizontalAlignment="Right"
+                       VerticalAlignment="Center"
+                       FontSize="14" />
+            <ScrollViewer Grid.Row="1"
+                          Grid.Column="1"
+                          Margin="3.5"
+                          Background="White"
+                          DataContext="{Binding ElementName=lbExceptions,
+                                                Path=SelectedItem}">
+                <Grid>
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
+                    </Grid.RowDefinitions>
+                    <Rectangle Fill="BlanchedAlmond" />
+                    <TextBlock FontSize="14"
+                               Text="Message"
+                               TextDecorations="Underline" />
+                    <TextBlock Grid.Row="1"
+                               FontSize="11"
+                               Text="{Binding Path=Message}"
+                               TextWrapping="Wrap" />
+
+                    <Rectangle Grid.Row="2" Fill="BlanchedAlmond" />
+                    <TextBlock Grid.Row="2"
+                               Margin="0,11,0,0"
+                               FontSize="14"
+                               Text="Stack Trace"
+                               TextDecorations="Underline" />
+                    <TextBlock Grid.Row="3"
+                               FontSize="11"
+                               Text="{Binding Path=StackTrace}"
+                               TextWrapping="Wrap" />
+
+                </Grid>
+            </ScrollViewer>
+        </Grid>
+    </Border>
+
+</UserControl>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Controls/LoadExceptionViewer.xaml.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,31 @@
+using System;
+
+namespace SilverlightGlimpse.Controls
+{
+    public partial class LoadExceptionViewer
+    {
+        public LoadExceptionViewer()
+        {
+            InitializeComponent();
+        }
+
+        public LoadExceptionViewer(Exception e, string sourceLocation)
+            : this()
+        {
+            this.txtSourceLocation.Text = string.Concat("Source Location: ", sourceLocation);
+
+            Exception ex = e;
+
+            while (ex != null)
+            {
+                this.lbExceptions.Items.Add(ex);
+                ex = ex.InnerException;
+            }
+
+            if (this.lbExceptions.Items.Count > 0)
+            {
+                this.lbExceptions.SelectedIndex = 0;
+            }
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Models/BrokenBinding.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,50 @@
+
+namespace SilverlightGlimpse.Models
+{
+    public class BrokenBinding
+    {
+        #region Fields
+
+        private string _controlName = string.Empty;
+        private string _controlTypeName = string.Empty;
+        private string _path = string.Empty;
+        private string _propertyName = string.Empty;
+
+        #endregion
+
+        #region Constructor
+
+        public BrokenBinding(string controlName, string controlTypeName, string propertyName, string path)
+        {
+            _controlName = controlName;
+            _controlTypeName = controlTypeName;
+            _propertyName = propertyName;
+            _path = path;
+        }
+
+        #endregion
+
+        #region Properties
+
+        public string ControlName { get { return string.IsNullOrEmpty(_controlName) ? "(none)" : _controlName; } }
+        public string ControlTypeName { get { return _controlTypeName; } }
+        public string Path { get { return _path; } }
+        public string PropertyName { get { return _propertyName; } }
+
+        #endregion
+
+        #region Methods
+
+        public override string ToString()
+        {
+            return string.Format(
+                "Control Name: {0}, Type: {1}, Property: {2}, Path: {3}",
+                this.ControlName,
+                this.ControlTypeName,
+                this.PropertyName,
+                this.Path);
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Models/ExceptionWrapper.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,54 @@
+using System;
+using System.Windows.Controls;
+
+namespace SilverlightGlimpse.Models
+{
+    public class ExceptionWrapper
+    {
+        #region Fields
+
+        private bool _isValidationException = false;
+        private ValidationErrorEventAction _enumAction = ValidationErrorEventAction.Added;
+        private Exception _exception;
+        private string _controlName = string.Empty;
+
+        #endregion
+
+        #region Constructor
+
+        public ExceptionWrapper(Exception e)
+        {
+            _exception = e;
+        }
+
+        #endregion
+
+        #region Properties
+
+        public ExceptionWrapper(ValidationErrorEventAction enumAction, string controlName, Exception validationException)
+        {
+            _enumAction = enumAction;
+            _controlName = controlName;
+            _exception = validationException;
+            _isValidationException = true;
+        }
+
+        public ValidationErrorEventAction Action { get { return _enumAction; } }
+        public string ControlName { get { return _controlName; } }
+        public Exception Exception { get { return _exception; } }
+        public bool IsValidationException { get { return _isValidationException; } }
+
+        #endregion
+
+        #region Methods
+
+        public override string ToString()
+        {
+            return _isValidationException
+                ? string.Format("({0}) - {1}", this.Action, Exception.Message)
+                : Exception.Message;
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Properties/AppManifest.xml	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,6 @@
+<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+>
+    <Deployment.Parts>
+    </Deployment.Parts>
+</Deployment>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Properties/AssemblyInfo.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SilverlightGlimpse")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SilverlightGlimpse")]
+[assembly: AssemblyCopyright("Copyright ©  2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("52ef33ca-e923-41ef-a7c8-98ec475bc956")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/Services/GlimpseService.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Controls;
+using SilverlightGlimpse.Controls;
+
+namespace SilverlightGlimpse.Models
+{
+    public class GlimpseService
+    {
+        #region Fields
+
+        private static GlimpseService _instance;
+
+        #endregion
+
+        #region Private constructor
+
+        private GlimpseService()
+        {
+            RootVisual.BindingValidationError += HostRootVisual_BindingValidationError;
+            App.UnhandledException += Application_UnhandledException;
+        }
+
+        #endregion
+
+        #region Properties
+
+        public static GlimpseService CreateInstance
+        {
+            get { return (_instance == null) ? _instance = new GlimpseService() : _instance; }
+        }
+
+        internal Application App { get; private set; }
+        internal ChildWindow GlimpseWindow { get; set;}
+        internal string HostApplicationName { get; set; }
+        internal ObservableCollection<ExceptionWrapper> HostExceptions { get; private set; }
+        internal FrameworkElement RootVisual { get; private set; }
+
+        #endregion
+
+        #region Creation and Loading
+
+        public void DisplayLoadFailure(Application app, Exception ex, string hostApplicationName)
+        {
+            Debug.WriteLine("{0} had exception. {1}", this.HostApplicationName, ex.ToString());
+            App = app;
+            RootVisual = new LoadExceptionViewer(ex, hostApplicationName);
+
+            //RootVisual.BindingValidationError += HostRootVisual_BindingValidationError;
+            //App.UnhandledException += Application_UnhandledException;
+        }
+
+        public void Load(Application app, string hostApplicationName)
+        {
+            this.App = app;
+            this.RootVisual = App.RootVisual as FrameworkElement;
+            this.HostApplicationName = hostApplicationName;
+
+            //RootVisual.BindingValidationError += HostRootVisual_BindingValidationError;
+            //App.UnhandledException += Application_UnhandledException;
+
+            ChildWindow window = new ChildWindow();
+            window.Title = this.HostApplicationName;
+            window.Content = new GlimpseViewer();
+            window.Show();
+        }
+
+        #endregion
+
+        #region Events handlers
+
+        private void HostRootVisual_BindingValidationError(object sender, ValidationErrorEventArgs e)
+        {
+            string controlName = "(none)";
+            Control control = e.OriginalSource as Control;
+
+            if (control != null && !string.IsNullOrEmpty(control.Name))
+            {
+                controlName = control.Name;
+            }
+
+            Exception ex = e.Error.Exception;
+
+            while (ex != null)
+            {
+                this.HostExceptions.Add(new ExceptionWrapper(e.Action, controlName, e.Error.Exception));
+                ex = ex.InnerException;
+            }
+        }
+
+        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
+        {
+            Debug.WriteLine("{0} had exception.  {1}", this.HostApplicationName, e.ExceptionObject.ToString());
+
+            Exception ex = e.ExceptionObject;
+
+            while (ex != null)
+            {
+                this.HostExceptions.Add(new ExceptionWrapper(ex));
+                ex = ex.InnerException;
+            }
+
+            e.Handled = true;
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightGlimpse/SilverlightGlimpse.csproj	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.50727</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{BB51026B-2864-4389-AACA-0BBDF1926E46}</ProjectGuid>
+    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SilverlightGlimpse</RootNamespace>
+    <AssemblyName>SilverlightGlimpse</AssemblyName>
+    <TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
+    <TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
+    <SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
+    <SilverlightApplication>true</SilverlightApplication>
+    <SupportedCultures>
+    </SupportedCultures>
+    <XapOutputs>true</XapOutputs>
+    <GenerateSilverlightManifest>true</GenerateSilverlightManifest>
+    <XapFilename>SilverlightGlimpse.xap</XapFilename>
+    <SilverlightManifestTemplate>Properties\AppManifest.xml</SilverlightManifestTemplate>
+    <SilverlightAppEntry>SilverlightGlimpse.App</SilverlightAppEntry>
+    <TestPageFileName>SilverlightGlimpseTestPage.html</TestPageFileName>
+    <CreateTestPage>true</CreateTestPage>
+    <ValidateXaml>true</ValidateXaml>
+    <EnableOutOfBrowser>false</EnableOutOfBrowser>
+    <OutOfBrowserSettingsFile>Properties\OutOfBrowserSettings.xml</OutOfBrowserSettingsFile>
+    <UsePlatformExtensions>false</UsePlatformExtensions>
+    <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+    <LinkedServerProject>
+    </LinkedServerProject>
+  </PropertyGroup>
+  <!-- This property group is only here to support building this project using the 
+       MSBuild 3.5 toolset. In order to work correctly with this older toolset, it needs 
+       to set the TargetFrameworkVersion to v3.5 -->
+  <PropertyGroup Condition="'$(MSBuildToolsVersion)' == '3.5'">
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>Bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;SILVERLIGHT</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>Bin\Release</OutputPath>
+    <DefineConstants>TRACE;SILVERLIGHT</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="mscorlib" />
+    <Reference Include="System.Windows" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Net" />
+    <Reference Include="System.Windows.Controls, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Windows.Browser" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Controls\BrokenBindingsViewer.xaml.cs">
+      <DependentUpon>BrokenBindingsViewer.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\GlimpseViewer.xaml.cs">
+      <DependentUpon>GlimpseViewer.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\LoadExceptionViewer.xaml.cs">
+      <DependentUpon>LoadExceptionViewer.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Controls\ExceptionsViewer.xaml.cs">
+      <DependentUpon>ExceptionsViewer.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Models\BrokenBinding.cs" />
+    <Compile Include="Models\ExceptionWrapper.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Services\GlimpseService.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Page Include="Controls\BrokenBindingsViewer.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="Controls\GlimpseViewer.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="Controls\LoadExceptionViewer.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="Controls\ExceptionsViewer.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Properties\AppManifest.xml" />
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Silverlight\$(SilverlightVersion)\Microsoft.Silverlight.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <SilverlightProjectProperties />
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
\ No newline at end of file
Binary file SilverlightValidation/SilverlightValidation.Web/ClientBin/SilverlightValidation.xap has changed
--- a/SilverlightValidation/SilverlightValidation.sln	Thu Apr 19 14:39:59 2012 +0100
+++ b/SilverlightValidation/SilverlightValidation.sln	Sat Apr 21 15:06:48 2012 +0100
@@ -5,6 +5,8 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightValidation.Web", "SilverlightValidation.Web\SilverlightValidation.Web.csproj", "{E65C6757-932B-4D01-9A8A-6D02F8FAA25A}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightGlimpse", "SilverlightGlimpse\SilverlightGlimpse.csproj", "{BB51026B-2864-4389-AACA-0BBDF1926E46}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -19,6 +21,10 @@
 		{E65C6757-932B-4D01-9A8A-6D02F8FAA25A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E65C6757-932B-4D01-9A8A-6D02F8FAA25A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{E65C6757-932B-4D01-9A8A-6D02F8FAA25A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BB51026B-2864-4389-AACA-0BBDF1926E46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BB51026B-2864-4389-AACA-0BBDF1926E46}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BB51026B-2864-4389-AACA-0BBDF1926E46}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BB51026B-2864-4389-AACA-0BBDF1926E46}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
--- a/SilverlightValidation/SilverlightValidation/App.xaml.cs	Thu Apr 19 14:39:59 2012 +0100
+++ b/SilverlightValidation/SilverlightValidation/App.xaml.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -1,59 +1,71 @@
 using System;
 using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Navigation;
+using Glimpse;
 
 namespace SilverlightValidation
 {
-    public partial class App : Application
+  public partial class App : Application
+  {
+
+    public App()
+    {
+      this.Startup += this.Application_Startup;
+      this.Exit += this.Application_Exit;
+      this.UnhandledException += this.Application_UnhandledException;
+
+      InitializeComponent();
+    }
+
+    private void Application_Startup(object sender, StartupEventArgs e)
+    {
+      try
+      {
+        this.RootVisual = new Views.UserListView();
+            
+        GlimpseService.CreateInstance.Load(this, "Glimpse Demo");
+      }
+      catch (Exception ex)
+      {
+        GlimpseService.CreateInstance.DisplayLoadFailure(this, ex, "Glimpse Demo");
+      }
+    }
+
+    private void Application_Exit(object sender, EventArgs e)
     {
 
-        public App()
-        {
-            this.Startup += this.Application_Startup;
-            this.Exit += this.Application_Exit;
-            this.UnhandledException += this.Application_UnhandledException;
-
-            InitializeComponent();
-        }
+    }
 
-        private void Application_Startup(object sender, StartupEventArgs e)
-        {
-            this.RootVisual = new UserView();
-        }
-
-        private void Application_Exit(object sender, EventArgs e)
-        {
-
-        }
+    private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
+    {
+      // If the app is running outside of the debugger then report the exception using
+      // the browser's exception mechanism. On IE this will display it a yellow alert 
+      // icon in the status bar and Firefox will display a script error.
+      if (!System.Diagnostics.Debugger.IsAttached)
+      {
 
-        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
-        {
-            // If the app is running outside of the debugger then report the exception using
-            // the browser's exception mechanism. On IE this will display it a yellow alert 
-            // icon in the status bar and Firefox will display a script error.
-            if (!System.Diagnostics.Debugger.IsAttached)
-            {
+        // NOTE: This will allow the application to continue running after an exception has been thrown
+        // but not handled. 
+        // For production applications this error handling should be replaced with something that will 
+        // report the error to the website and stop the application.
+        e.Handled = true;
+        Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
+      }
+    }
 
-                // NOTE: This will allow the application to continue running after an exception has been thrown
-                // but not handled. 
-                // For production applications this error handling should be replaced with something that will 
-                // report the error to the website and stop the application.
-                e.Handled = true;
-                Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
-            }
-        }
+    private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)
+    {
+      try
+      {
+        string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;
+        errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");
 
-        private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)
-        {
-            try
-            {
-                string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;
-                errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");
-
-                System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");");
-            }
-            catch (Exception)
-            {
-            }
-        }
+        System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");");
+      }
+      catch (Exception)
+      {
+      }
     }
+  }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Commands/RelayCommand.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,40 @@
+using System;
+using System.Windows.Input;
+
+namespace SilverlightValidation.Commands
+{
+    public class RelayCommand : ICommand
+    {
+        public event EventHandler CanExecuteChanged = delegate { };
+
+        readonly Action<object> _execute;
+        readonly Predicate<object> _canExecute;
+
+        public RelayCommand(Action<object> execute,
+                            Predicate<object> canExecute = null)
+        {
+            if (execute == null) throw new ArgumentNullException("execute");
+
+            _execute = execute;
+            _canExecute = canExecute;
+        }
+
+
+        public void UpdateCanExecuteCommand()
+        {
+            CanExecuteChanged(this, new EventArgs());
+        }
+
+
+        public bool CanExecute(object parameter)
+        {
+            return _canExecute == null || _canExecute(parameter);
+        }
+
+
+        public void Execute(object parameter)
+        {
+            _execute(parameter);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Data/Factory.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using SilverlightValidation.Models;
+
+namespace SilverlightValidation.Data
+{
+    public class Factory
+    {
+        public static IList<UserModel> CreateUserModels()
+        {
+            return new List<UserModel>(5)
+            {
+                new UserModel() { Username = "StevenH", Password = "Password1*", Email = "steven@hotmail.com", DateOfBirth = new DateTime(1977, 09, 01), Description = ""},
+                new UserModel() { Username = "RichardJ", Password = "&12N456a", Email = "dicky@gmail.com", DateOfBirth = new DateTime(1983, 03, 13), Description = "Rebel"},
+                new UserModel() { Username = "BobbyP", Password = "p@a33Word", Email = "bob@yahoo.co.uk", DateOfBirth = new DateTime(1992, 08, 30), Description = ""},
+                new UserModel() { Username = "DavidM", Password = "][poIu789*", Email = "daveyboy@marsh.com", DateOfBirth = new DateTime(1965, 06, 21), Description = "Renegade"},
+                new UserModel() { Username = "JessieJ", Password = "';lkJh567", Email = "jj@apple.co.uk", DateOfBirth = new DateTime(1990, 10, 15), Description = ""}
+            };
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Diagrams/Form.cd	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ClassDiagram MajorVersion="1" MinorVersion="1">
+  <Class Name="SilverlightValidation.RelayCommand" Collapsed="true" BaseTypeListCollapsed="true">
+    <Position X="6.75" Y="5.5" Width="2" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAIEACAAQAAECAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+      <FileName>RelayCommand.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" Collapsed="true" />
+  </Class>
+  <Class Name="SilverlightValidation.ViewModelBase">
+    <Position X="1.75" Y="0.5" Width="2" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAACAABQAAABAAAAAgAAgAAAAACIAAAAAAARAAA=</HashCode>
+      <FileName>ViewModelBase.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" />
+  </Class>
+  <Class Name="SilverlightValidation.UserModel" BaseTypeListCollapsed="true">
+    <Position X="9.25" Y="0.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAACAgAAAAAQAAAAAAAAAQAEACAAAAAAAAABA=</HashCode>
+      <FileName>UserModel.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" Collapsed="true" />
+  </Class>
+  <Class Name="SilverlightValidation.UserModelValidator">
+    <Position X="6.75" Y="3.75" Width="2" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAA=</HashCode>
+      <FileName>UserModelValidator.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="SilverlightValidation.UserViewModel">
+    <Position X="4.25" Y="0.5" Width="2" />
+    <TypeIdentifier>
+      <HashCode>UAAAoIIABCIgAAAAEYAAGAAAAAAQABAKQAAAAAAAIAA=</HashCode>
+      <FileName>UserViewModel.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" />
+  </Class>
+  <Interface Name="SilverlightValidation.IUserModel">
+    <Position X="6.75" Y="0.5" Width="2" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAACAgAAAAAQAAAAAAAAAQAAACAAAAAAAAAAA=</HashCode>
+      <FileName>UserModel.cs</FileName>
+    </TypeIdentifier>
+  </Interface>
+  <Interface Name="SilverlightValidation.ICloneable&lt;T&gt;">
+    <Position X="6.75" Y="2.5" Width="2" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA=</HashCode>
+      <FileName>UserModel.cs</FileName>
+    </TypeIdentifier>
+  </Interface>
+  <Font Name="Segoe UI" Size="9" />
+</ClassDiagram>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Diagrams/List.cd	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ClassDiagram MajorVersion="1" MinorVersion="1">
+  <Class Name="SilverlightValidation.UserListViewModel">
+    <Position X="8.25" Y="0.75" Width="2" />
+    <TypeIdentifier>
+      <HashCode>QAAAIAIAAAIAAAAAAAAAEAAAAAAAABAIQAAAAAAAAAA=</HashCode>
+      <FileName>UserListViewModel.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" />
+  </Class>
+  <Class Name="SilverlightValidation.ViewModelBase">
+    <Position X="5.5" Y="0.75" Width="2.25" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAACAABQAAABAAAAAgAAgAAAAACIAAAAAAARAAA=</HashCode>
+      <FileName>ViewModelBase.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" />
+  </Class>
+  <Class Name="SilverlightValidation.UserViewModel">
+    <Position X="3" Y="0.75" Width="2" />
+    <TypeIdentifier>
+      <HashCode>UAAAoIIADCIgAAAAEYAAGAAAAAAQABAKQEAAAAAgIAA=</HashCode>
+      <FileName>UserViewModel.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" />
+  </Class>
+  <Class Name="SilverlightValidation.UserModel" BaseTypeListCollapsed="true">
+    <Position X="5.5" Y="4.75" Width="2.25" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAACAgAAAAAQAAAAAAAAAQAEACAAAAAAAAABA=</HashCode>
+      <FileName>UserModel.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" Collapsed="true" />
+  </Class>
+  <Interface Name="SilverlightValidation.IUserModel">
+    <Position X="8.25" Y="4.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAACAgAAAAAQAAAAAAAAAQAAACAAAAAAAAAAA=</HashCode>
+      <FileName>UserModel.cs</FileName>
+    </TypeIdentifier>
+  </Interface>
+  <Font Name="Segoe UI" Size="9" />
+</ClassDiagram>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Interfaces/ICloneable.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,7 @@
+namespace SilverlightValidation.Interfaces
+{
+  public interface ICloneable<T>
+  {
+    T Clone();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Interfaces/IUserModel.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,12 @@
+using System;
+namespace SilverlightValidation.Interfaces
+{
+  public interface IUserModel
+  {
+    string Username { get; set; }
+    string Email { get; set; }
+    string Password { get; set; }
+    DateTime? DateOfBirth { get; set; }
+    string Description { get; set; }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Models/UserModel.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,25 @@
+using System;
+using System.ComponentModel;
+using SilverlightValidation.Interfaces;
+
+namespace SilverlightValidation.Models
+{
+    public class UserModel : IUserModel, ICloneable<UserModel>
+    {
+        public string Username { get; set; }
+        public string Email { get; set; }
+        public string Password { get; set; }
+        public DateTime? DateOfBirth { get; set; }
+        public string Description { get; set; }
+
+        public static UserModel Create()
+        {
+            return new UserModel() { Username = "", Email = "", Password = "", DateOfBirth = null, Description = "" };
+        }
+
+        public UserModel Clone()
+        {
+            return (UserModel) this.MemberwiseClone(); 
+        }
+    }
+}
--- a/SilverlightValidation/SilverlightValidation/Overview.cd	Thu Apr 19 14:39:59 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ClassDiagram MajorVersion="1" MinorVersion="1">
-  <Class Name="SilverlightValidation.RelayCommand" Collapsed="true" BaseTypeListCollapsed="true">
-    <Position X="6.75" Y="5.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAIEACAAQAAECAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
-      <FileName>RelayCommand.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" Collapsed="true" />
-  </Class>
-  <Class Name="SilverlightValidation.ViewModelBase">
-    <Position X="1.75" Y="0.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAACAABQAAABAAAAAgAAgAAAAACIAAAAAAARAAA=</HashCode>
-      <FileName>ViewModelBase.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Class Name="SilverlightValidation.UserModel" BaseTypeListCollapsed="true">
-    <Position X="9.25" Y="0.5" Width="1.5" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAACAgAAAAAQAAAAAAAAAQAEACAAAAAAAAABA=</HashCode>
-      <FileName>UserModel.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" Collapsed="true" />
-  </Class>
-  <Class Name="SilverlightValidation.UserModelValidator">
-    <Position X="6.75" Y="3.75" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAA=</HashCode>
-      <FileName>UserModelValidator.cs</FileName>
-    </TypeIdentifier>
-  </Class>
-  <Class Name="SilverlightValidation.UserViewModel">
-    <Position X="4.25" Y="0.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>UAAAoIIABCIgAAAAEYAAGAAAAAAQABAKQAAAAAAAIAA=</HashCode>
-      <FileName>UserViewModel.cs</FileName>
-    </TypeIdentifier>
-    <Lollipop Position="0.2" />
-  </Class>
-  <Interface Name="SilverlightValidation.IUserModel">
-    <Position X="6.75" Y="0.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAACAgAAAAAQAAAAAAAAAQAAACAAAAAAAAAAA=</HashCode>
-      <FileName>UserModel.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Interface Name="SilverlightValidation.ICloneable&lt;T&gt;">
-    <Position X="6.75" Y="2.5" Width="2" />
-    <TypeIdentifier>
-      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA=</HashCode>
-      <FileName>UserModel.cs</FileName>
-    </TypeIdentifier>
-  </Interface>
-  <Font Name="Segoe UI" Size="9" />
-</ClassDiagram>
\ No newline at end of file
--- a/SilverlightValidation/SilverlightValidation/RelayCommand.cs	Thu Apr 19 14:39:59 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-using System;
-using System.Windows.Input;
-
-namespace SilverlightValidation
-{
-    public class RelayCommand : ICommand
-    {
-        public event EventHandler CanExecuteChanged = delegate { };
-
-        readonly Action<object> _execute;
-        readonly Predicate<object> _canExecute;
-
-        public RelayCommand(Action<object> execute,
-                            Predicate<object> canExecute = null)
-        {
-            if (execute == null) throw new ArgumentNullException("execute");
-
-            _execute = execute;
-            _canExecute = canExecute;
-        }
-
-
-        public void UpdateCanExecuteCommand()
-        {
-            CanExecuteChanged(this, new EventArgs());
-        }
-
-
-        public bool CanExecute(object parameter)
-        {
-            return _canExecute == null || _canExecute(parameter);
-        }
-
-
-        public void Execute(object parameter)
-        {
-            _execute(parameter);
-        }
-    }
-}
--- a/SilverlightValidation/SilverlightValidation/SilverlightValidation.csproj	Thu Apr 19 14:39:59 2012 +0100
+++ b/SilverlightValidation/SilverlightValidation/SilverlightValidation.csproj	Sat Apr 21 15:06:48 2012 +0100
@@ -71,9 +71,15 @@
     <Reference Include="System.Windows.Controls, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\Libs\System.Windows.Controls.dll</HintPath>
     </Reference>
+    <Reference Include="System.Windows.Controls.Data, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\Libs\System.Windows.Controls.Data.dll</HintPath>
+    </Reference>
     <Reference Include="System.Windows.Controls.Data.Input, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\Libs\System.Windows.Controls.Data.Input.dll</HintPath>
     </Reference>
+    <Reference Include="System.Windows.Controls.Navigation, Version=5.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\Libs\System.Windows.Controls.Navigation.dll</HintPath>
+    </Reference>
     <Reference Include="System.Xml" />
     <Reference Include="System.Windows.Browser" />
   </ItemGroup>
@@ -81,31 +87,51 @@
     <Compile Include="App.xaml.cs">
       <DependentUpon>App.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Data\Factory.cs" />
+    <Compile Include="Interfaces\ICloneable.cs" />
+    <Compile Include="Interfaces\IUserModel.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="RelayCommand.cs" />
-    <Compile Include="UserModel.cs" />
-    <Compile Include="UserModelValidator.cs" />
-    <Compile Include="UserViewModel.cs" />
-    <Compile Include="ViewModelBase.cs" />
-    <Compile Include="UserView.xaml.cs">
+    <Compile Include="Commands\RelayCommand.cs" />
+    <Compile Include="ViewModels\UserListViewModel.cs" />
+    <Compile Include="Models\UserModel.cs" />
+    <Compile Include="Validators\UserModelValidator.cs" />
+    <Compile Include="Views\UserListView.xaml.cs">
+      <DependentUpon>UserListView.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Views\UserView.xaml.cs">
       <DependentUpon>UserView.xaml</DependentUpon>
     </Compile>
+    <Compile Include="ViewModels\UserViewModel.cs" />
+    <Compile Include="ViewModels\ViewModelBase.cs" />
   </ItemGroup>
   <ItemGroup>
     <ApplicationDefinition Include="App.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </ApplicationDefinition>
-    <Page Include="UserView.xaml">
+    <Page Include="Views\UserListView.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Views\UserView.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
   </ItemGroup>
   <ItemGroup>
-    <None Include="Overview.cd" />
+    <None Include="Diagrams\List.cd" />
+    <None Include="Diagrams\Form.cd" />
     <None Include="Properties\AppManifest.xml" />
   </ItemGroup>
-  <ItemGroup />
+  <ItemGroup>
+    <Folder Include="Messages\" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\SilverlightGlimpse\SilverlightGlimpse.csproj">
+      <Project>{BB51026B-2864-4389-AACA-0BBDF1926E46}</Project>
+      <Name>SilverlightGlimpse</Name>
+    </ProjectReference>
+  </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Silverlight\$(SilverlightVersion)\Microsoft.Silverlight.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
--- a/SilverlightValidation/SilverlightValidation/UserModel.cs	Thu Apr 19 14:39:59 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-using System;
-using System.ComponentModel;
-
-namespace SilverlightValidation
-{
-    public interface IUserModel
-    {
-        string Username { get; set; }
-        string Email { get; set; }
-        string Password { get; set; }
-        DateTime? DateOfBirth { get; set; }
-        string Description { get; set; }
-    }
-
-    public interface ICloneable<T>
-    {
-        T Clone();
-    }
-
-    public class UserModel : IUserModel, ICloneable<UserModel>
-    {
-        public string Username { get; set; }
-        public string Email { get; set; }
-        public string Password { get; set; }
-        public DateTime? DateOfBirth { get; set; }
-        public string Description { get; set; }
-
-        public static UserModel Create()
-        {
-            return new UserModel() { Username = "", Email = "", Password = "", DateOfBirth = null, Description = "" };
-        }
-
-        public UserModel Clone()
-        {
-            return (UserModel) this.MemberwiseClone(); 
-        }
-    }
-}
--- a/SilverlightValidation/SilverlightValidation/UserModelValidator.cs	Thu Apr 19 14:39:59 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-using System;
-using FluentValidation;
-
-namespace SilverlightValidation
-{
-    public class UserModelValidator : AbstractValidator<IUserModel>
-    {
-        public UserModelValidator()
-        {
-            RuleFor(x => x.Username)
-                .Length(3, 8)
-                .WithMessage("Must be between 3-8 characters.");
-
-            RuleFor(x => x.Password)
-                .Matches(@"^\w*(?=\w*\d)(?=\w*[a-z])(?=\w*[A-Z])\w*$")
-                .WithMessage("Must contain lower, upper and numeric chars.");
-
-            RuleFor(x => x.Email)
-                .EmailAddress()
-                .WithMessage("A valid email address is required.");
-
-            RuleFor(x => x.DateOfBirth)
-                .Must(BeAValidDateOfBirth)
-                .WithMessage("Must be within 100 years of today.");
-        }
-
-        private bool BeAValidDateOfBirth(DateTime? dateOfBirth)
-        {
-            if (dateOfBirth == null) return false;
-            if (dateOfBirth.Value > DateTime.Today || dateOfBirth < dateOfBirth.Value.AddYears(-100))
-                return false;
-            return true;
-        }
-    }
-}
--- a/SilverlightValidation/SilverlightValidation/UserView.xaml	Thu Apr 19 14:39:59 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-<UserControl x:Class="SilverlightValidation.UserView"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-             xmlns:p="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls"
-             xmlns:s="clr-namespace:System;assembly=mscorlib"
-             xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
-             d:DesignHeight="400"
-             d:DesignWidth="450"
-             mc:Ignorable="d">
-
-    <Grid x:Name="LayoutRoot" Background="White">
-
-        <Grid.RowDefinitions>
-            <RowDefinition Height="30" />
-            <RowDefinition Height="30" />
-            <RowDefinition Height="30" />
-            <RowDefinition Height="30" />
-            <RowDefinition Height="30" />
-            <RowDefinition Height="30" />
-            <RowDefinition Height="50" />
-            <RowDefinition Height="150" />
-        </Grid.RowDefinitions>
-        <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="*" />
-            <ColumnDefinition Width="100" />
-            <ColumnDefinition Width="300" />
-            <ColumnDefinition Width="30" />
-            <ColumnDefinition Width="*" />
-        </Grid.ColumnDefinitions>
-
-        <TextBlock Grid.Row="1"
-                   Grid.Column="1"
-                   Style="{StaticResource LabelStyle}"
-                   Text="Username:" />
-
-        <TextBox x:Name="tbUsername"
-                 Grid.Row="1"
-                 Grid.Column="2"
-                 LostFocus="tbUsername_LostFocus"
-                 Style="{StaticResource TextBoxStyle}"
-                 Text="{Binding Username,
-                                Mode=TwoWay,
-                                ValidatesOnNotifyDataErrors=True,
-                                NotifyOnValidationError=True}" />
-
-        <sdk:DescriptionViewer Grid.Row="1"
-                               Grid.Column="3"
-                               Width="20"
-                               Description="Required"
-                               Target="{Binding ElementName=tbUsername}" />
-
-        <TextBlock Grid.Row="2"
-                   Grid.Column="1"
-                   Style="{StaticResource LabelStyle}"
-                   Text="Password:" />
-
-        <PasswordBox x:Name="tbPassword"
-                     Grid.Row="2"
-                     Grid.Column="2"
-                     LostFocus="tbPassword_LostFocus"
-                     Password="{Binding Password,
-                                        Mode=TwoWay,
-                                        ValidatesOnNotifyDataErrors=True,
-                                        NotifyOnValidationError=True}"
-                     Style="{StaticResource PasswordBoxStyle}" />
-
-        <sdk:DescriptionViewer Grid.Row="2"
-                               Grid.Column="3"
-                               Width="20"
-                               Description="Required"
-                               Target="{Binding ElementName=tbPassword}" />
-
-        <TextBlock Grid.Row="3"
-                   Grid.Column="1"
-                   Style="{StaticResource LabelStyle}"
-                   Text="Email:" />
-
-        <TextBox x:Name="tbEmail"
-                 Grid.Row="3"
-                 Grid.Column="2"
-                 LostFocus="tbEmail_LostFocus"
-                 Style="{StaticResource TextBoxStyle}"
-                 Text="{Binding Email,
-                                Mode=TwoWay,
-                                ValidatesOnNotifyDataErrors=True,
-                                NotifyOnValidationError=True}" />
-
-        <sdk:DescriptionViewer Grid.Row="3"
-                               Grid.Column="3"
-                               Width="20"
-                               Description="Required"
-                               Target="{Binding ElementName=tbEmail}" />
-
-        <TextBlock Grid.Row="4"
-                   Grid.Column="1"
-                   Style="{StaticResource LabelStyle}"
-                   Text="Date of Birth:" />
-
-        <sdk:DatePicker x:Name="dpDateOfBirth"
-                        Grid.Row="4"
-                        Grid.Column="2"
-                        KeyDown="DatePicker_KeyDown"
-                        LostFocus="dpDateOfBirth_LostFocus"
-                        SelectedDate="{Binding DateOfBirth,
-                                               Mode=TwoWay,
-                                               ValidatesOnNotifyDataErrors=True,
-                                               NotifyOnValidationError=True}"
-                        Style="{StaticResource DatePickerStyle}" />
-        <sdk:DescriptionViewer Grid.Row="4"
-                               Grid.Column="3"
-                               Width="20"
-                               Description="Required"
-                               Target="{Binding ElementName=dpDateOfBirth}" />
-
-        <TextBlock x:Name="tbDescription"
-                   Grid.Row="5"
-                   Grid.Column="1"
-                   Style="{StaticResource LabelStyle}"
-                   Text="Description:" />
-
-        <TextBox Grid.Row="5"
-                 Grid.Column="2"
-                 Style="{StaticResource TextBoxStyle}"
-                 Text="{Binding Description}" />
-        <StackPanel Grid.Row="6"
-                    Grid.Column="2"
-                    HorizontalAlignment="Right"
-                    Orientation="Horizontal">
-            <Button Command="{Binding OkCommand}"
-                    Content="OK"
-                    Style="{StaticResource ButtonStyle}" />
-            <Button Command="{Binding CancelCommand}"
-                    Content="Cancel"
-                    Style="{StaticResource ButtonStyle}" />
-        </StackPanel>
-
-        <sdk:ValidationSummary Grid.Row="7"
-                               Grid.Column="1"
-                               Grid.ColumnSpan="2"
-                               Style="{StaticResource ValidationSummaryStyle}" />
-
-    </Grid>
-</UserControl>
--- a/SilverlightValidation/SilverlightValidation/UserView.xaml.cs	Thu Apr 19 14:39:59 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-using System.Windows;
-using System.Windows.Browser;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Input;
-
-namespace SilverlightValidation
-{
-    public partial class UserView
-    {
-        private UserViewModel vm;
-        public UserView()
-        {
-            InitializeComponent();
-            HtmlPage.Document.SetProperty("title", "Silverlight Validation");
-
-            vm = new UserViewModel(UserModel.Create(), new UserModelValidator());
-            this.DataContext = vm;
-        }
-
-        private static void UpdateTextBoxSource(object sender)
-        {
-            BindingExpression be = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
-            be.UpdateSource();
-        }
-
-        private void DatePicker_KeyDown(object sender, KeyEventArgs e)
-        {
-            if (e.Key != Key.Tab)
-                e.Handled = true;
-        }
-
-        private void tbUsername_LostFocus(object sender, RoutedEventArgs e)
-        {
-            UpdateTextBoxSource(sender);
-        }
-
-        private void tbPassword_LostFocus(object sender, RoutedEventArgs e)
-        {
-            BindingExpression be = ((PasswordBox)sender).GetBindingExpression(PasswordBox.PasswordProperty);
-            be.UpdateSource();
-        }
-
-        private void tbEmail_LostFocus(object sender, RoutedEventArgs e)
-        {
-            UpdateTextBoxSource(sender);
-        }
-
-        private void dpDateOfBirth_LostFocus(object sender, RoutedEventArgs e)
-        {
-            BindingExpression be = ((DatePicker)sender).GetBindingExpression(DatePicker.SelectedDateProperty);
-            be.UpdateSource();
-        }
-    }
-}
\ No newline at end of file
--- a/SilverlightValidation/SilverlightValidation/UserViewModel.cs	Thu Apr 19 14:39:59 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,197 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Linq;
-using System.Windows;
-using System.Windows.Input;
-using FluentValidation;
-
-namespace SilverlightValidation
-{
-    public class UserViewModel : ViewModelBase, IUserModel, IChangeTracking
-    {
-        #region Fields
-
-        private readonly UserModelValidator _validator;
-        private UserModel _data;
-        private UserModel _backup;
-
-        #endregion
-
-        #region Constructor
-
-        public UserViewModel(UserModel model, UserModelValidator validator)
-        {
-            _validator = validator;
-
-            OkCommand = new RelayCommand(OkCommandExecute);
-            CancelCommand = new RelayCommand(CancelCommandExecute);
-
-            _data = model;
-            _backup = model.Clone();
-        }
-
-        #endregion
-
-        #region Methods
-
-        private void SetProperties(IUserModel source)
-        {
-            Username = source.Username;
-            Password = source.Password;
-            Email = source.Email;
-            DateOfBirth = source.DateOfBirth;
-            Description = source.Description;
-        }
-
-        #endregion
-
-        #region Properties
-
-        private const string UsernameProperty = "Username";
-        public string Username
-        {
-            get { return _data.Username; }
-            set
-            {
-                if (_data.Username != value)
-                {
-                    _data.Username = value;
-                    RaisePropertyChanged(UsernameProperty);
-                    IsChanged = true;
-                }
-
-                ClearError(UsernameProperty);
-                var validationResult = _validator.Validate(this, UsernameProperty);
-                if (!validationResult.IsValid)
-                    validationResult.Errors.ToList().ForEach(x => SetError(UsernameProperty, x.ErrorMessage));
-            }
-        }
-
-        private const string PasswordProperty = "Password";
-        public string Password
-        {
-            get { return _data.Password; }
-            set
-            {
-                if (_data.Password != value)
-                {
-                    _data.Password = value;
-                    RaisePropertyChanged(PasswordProperty);
-                    IsChanged = true;
-                }
-
-                ClearError(PasswordProperty);
-                var validationResult = _validator.Validate(this, PasswordProperty);
-                if (!validationResult.IsValid)
-                    validationResult.Errors.ToList().ForEach(x => SetError(PasswordProperty, x.ErrorMessage));
-            }
-        }
-
-        private const string EmailProperty = "Email";
-        public string Email
-        {
-            get { return _data.Email; }
-            set
-            {
-                if (_data.Email != value)
-                {
-                    _data.Email = value;
-                    RaisePropertyChanged(EmailProperty);
-                    IsChanged = true;
-                }
-
-                ClearError(EmailProperty);
-                var validationResult = _validator.Validate(this, EmailProperty);
-                if (!validationResult.IsValid)
-                    validationResult.Errors.ToList().ForEach(x => SetError(EmailProperty, x.ErrorMessage));
-            }
-        }
-
-        private const string DateOfBirthProperty = "DateOfBirth";
-        public DateTime? DateOfBirth
-        {
-            get { return _data.DateOfBirth; }
-            set
-            {
-                if (_data.DateOfBirth != value)
-                {
-                    _data.DateOfBirth = value;
-                    RaisePropertyChanged(DateOfBirthProperty);
-                    IsChanged = true;
-                }
-
-                ClearError(DateOfBirthProperty);
-                var validationResult = _validator.Validate(this, DateOfBirthProperty);
-                if (!validationResult.IsValid)
-                    validationResult.Errors.ToList().ForEach(x => SetError(DateOfBirthProperty, x.ErrorMessage));
-            }
-        }
-
-        private const string DescriptionProperty = "Description";
-        public string Description
-        {
-            get { return _data.Description; }
-            set
-            {
-                if (_data.Description != value)
-                {
-                    _data.Description = value;
-                    RaisePropertyChanged(DescriptionProperty);
-                    IsChanged = true;
-                }
-
-                ClearError(DescriptionProperty);
-                var validationResult = _validator.Validate(this, DescriptionProperty);
-                if (!validationResult.IsValid)
-                    validationResult.Errors.ToList().ForEach(x => SetError(DescriptionProperty, x.ErrorMessage));
-            }
-        }
-
-        #endregion
-
-        #region Commands
-
-        public ICommand OkCommand { get; set; }
-        public ICommand CancelCommand { get; set; }
-
-        private void OkCommandExecute(object obj)
-        {
-            SetProperties(_data);
-
-            if (IsChanged && !HasErrors)
-            {
-                AcceptChanges();
-            }
-        }
-
-        private void CancelCommandExecute(object obj)
-        {
-            CancelChanges();
-        }
-        
-        #endregion
-
-        #region IChangeTrack plus Cancel
-
-        public void AcceptChanges()
-        {
-            MessageBox.Show("Saving...");
-            SetProperties(_backup);
-
-            ClearAllErrors();
-            IsChanged = false;
-        }
-
-        public void CancelChanges()
-        {
-            ClearAllErrors();
-            if (!IsChanged) return;
-            SetProperties(_backup);
-            IsChanged = false;
-        }
-
-        public bool IsChanged { get; private set; }
-
-        #endregion
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Validators/UserModelValidator.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,36 @@
+using System;
+using FluentValidation;
+using SilverlightValidation.Interfaces;
+
+namespace SilverlightValidation.Validators
+{
+    public class UserModelValidator : AbstractValidator<IUserModel>
+    {
+        public UserModelValidator()
+        {
+            RuleFor(x => x.Username)
+                .Length(3, 8)
+                .WithMessage("Must be between 3-8 characters.");
+
+            RuleFor(x => x.Password)
+                .Matches(@"^\w*(?=\w*\d)(?=\w*[a-z])(?=\w*[A-Z])\w*$")
+                .WithMessage("Must contain lower, upper and numeric chars.");
+
+            RuleFor(x => x.Email)
+                .EmailAddress()
+                .WithMessage("A valid email address is required.");
+
+            RuleFor(x => x.DateOfBirth)
+                .Must(BeAValidDateOfBirth)
+                .WithMessage("Must be within 100 years of today.");
+        }
+
+        private bool BeAValidDateOfBirth(DateTime? dateOfBirth)
+        {
+            if (dateOfBirth == null) return false;
+            if (dateOfBirth.Value > DateTime.Today || dateOfBirth < DateTime.Today.AddYears(-100))
+                return false;
+            return true;
+        }
+    }
+}
--- a/SilverlightValidation/SilverlightValidation/ViewModelBase.cs	Thu Apr 19 14:39:59 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-
-namespace SilverlightValidation
-{
-    public class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
-    {
-        #region INotifyPropertyChanged method plus event
-
-        public event PropertyChangedEventHandler PropertyChanged = delegate { };
-
-        protected void RaisePropertyChanged(string propertyName)
-        {
-            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
-        }
-
-        #endregion
-
-        #region INotifyDataErrorInfo methods and helpers
-
-        private readonly Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
-
-        public void SetError(string propertyName, string errorMessage)
-        {
-            if (!_errors.ContainsKey(propertyName))
-                _errors.Add(propertyName, new List<string> { errorMessage });
-
-            RaiseErrorsChanged(propertyName);
-        }
-
-        protected void ClearError(string propertyName)
-        {
-            if (_errors.ContainsKey(propertyName))
-                _errors.Remove(propertyName);
-
-            RaiseErrorsChanged(propertyName);
-        }
-
-        protected void ClearAllErrors()
-        {
-            var errors = _errors.Select(error => error.Key).ToList();
-
-            foreach (var propertyName in errors)
-                ClearError(propertyName);
-        }
-
-        public void RaiseErrorsChanged(string propertyName)
-        {
-            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
-        }
-
-        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { };
-
-        public IEnumerable GetErrors(string propertyName)
-        {
-            return _errors.ContainsKey(propertyName)
-                    ? _errors[propertyName]
-                    : null;
-        }
-
-        public bool HasErrors
-        {
-            get { return _errors.Count > 0; }
-        }
-
-        #endregion
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/ViewModels/UserListViewModel.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+using SilverlightValidation.Commands;
+using SilverlightValidation.Models;
+using SilverlightValidation.Validators;
+
+namespace SilverlightValidation.ViewModels
+{
+  public class UserListViewModel
+  {
+    public UserListViewModel(IList<UserModel> models, UserModelValidator validator)
+    {
+      Data = new ObservableCollection<UserViewModel>();
+
+      foreach (var model in models)
+        Data.Add(new UserViewModel(model, validator));
+
+      AddCommand = new RelayCommand(AddCommandExecute);
+      DeleteCommand = new RelayCommand(DeleteCommandExecute);
+    }
+
+    #region Properties
+
+    public ObservableCollection<UserViewModel> Data { get; set; }
+
+    public UserViewModel SelectedItem { get; set; }
+
+    #endregion
+    
+    #region Commands
+
+    public ICommand AddCommand { get; set; }
+    public ICommand DeleteCommand { get; set; }
+
+    private void AddCommandExecute(object obj)
+    {
+      
+    }
+
+    private void DeleteCommandExecute(object obj)
+    {
+      Data.Remove(SelectedItem);
+    }
+
+    #endregion
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/ViewModels/UserViewModel.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,227 @@
+using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using FluentValidation;
+using SilverlightValidation.Interfaces;
+using SilverlightValidation.Validators;
+using SilverlightValidation.Models;
+using SilverlightValidation.Commands;
+
+namespace SilverlightValidation.ViewModels
+{
+    public class UserViewModel : ViewModelBase, IUserModel, IChangeTracking, IEditableObject
+    {
+        #region Fields
+
+        private readonly UserModelValidator _validator;
+        private UserModel _data;
+        private UserModel _backup;
+
+        #endregion
+
+        #region Constructor
+
+        public UserViewModel(UserModel model, UserModelValidator validator)
+        {
+            _validator = validator;
+            _data = model;
+            _backup = model.Clone();
+
+            OkCommand = new RelayCommand(OkCommandExecute);
+            CancelCommand = new RelayCommand(CancelCommandExecute);
+        }
+
+        #endregion
+
+        #region Methods
+
+        private void SetProperties(IUserModel source)
+        {
+            Username = source.Username;
+            Password = source.Password;
+            Email = source.Email;
+            DateOfBirth = source.DateOfBirth;
+            Description = source.Description;
+        }
+
+        #endregion
+
+        #region Properties
+
+        private const string UsernameProperty = "Username";
+        public string Username
+        {
+            get { return _data.Username; }
+            set
+            {
+                if (_data.Username != value)
+                {
+                    _data.Username = value;
+                    RaisePropertyChanged(UsernameProperty);
+                    IsChanged = true;
+                }
+
+                ClearError(UsernameProperty);
+                var validationResult = _validator.Validate(this, UsernameProperty);
+                if (!validationResult.IsValid)
+                    validationResult.Errors.ToList().ForEach(x => SetError(UsernameProperty, x.ErrorMessage));
+            }
+        }
+
+        private const string PasswordProperty = "Password";
+        public string Password
+        {
+            get { return _data.Password; }
+            set
+            {
+                if (_data.Password != value)
+                {
+                    _data.Password = value;
+                    RaisePropertyChanged(PasswordProperty);
+                    IsChanged = true;
+                }
+
+                ClearError(PasswordProperty);
+                var validationResult = _validator.Validate(this, PasswordProperty);
+                if (!validationResult.IsValid)
+                    validationResult.Errors.ToList().ForEach(x => SetError(PasswordProperty, x.ErrorMessage));
+            }
+        }
+
+        private const string EmailProperty = "Email";
+        public string Email
+        {
+            get { return _data.Email; }
+            set
+            {
+                if (_data.Email != value)
+                {
+                    _data.Email = value;
+                    RaisePropertyChanged(EmailProperty);
+                    IsChanged = true;
+                }
+
+                ClearError(EmailProperty);
+                var validationResult = _validator.Validate(this, EmailProperty);
+                if (!validationResult.IsValid)
+                    validationResult.Errors.ToList().ForEach(x => SetError(EmailProperty, x.ErrorMessage));
+            }
+        }
+
+        private const string DateOfBirthProperty = "DateOfBirth";
+        public DateTime? DateOfBirth
+        {
+            get { return _data.DateOfBirth; }
+            set
+            {
+                if (_data.DateOfBirth != value)
+                {
+                    _data.DateOfBirth = value;
+                    RaisePropertyChanged(DateOfBirthProperty);
+                    IsChanged = true;
+                }
+
+                ClearError(DateOfBirthProperty);
+                var validationResult = _validator.Validate(this, DateOfBirthProperty);
+                if (!validationResult.IsValid)
+                    validationResult.Errors.ToList().ForEach(x => SetError(DateOfBirthProperty, x.ErrorMessage));
+            }
+        }
+
+        private const string DescriptionProperty = "Description";
+        public string Description
+        {
+            get { return _data.Description; }
+            set
+            {
+                if (_data.Description != value)
+                {
+                    _data.Description = value;
+                    RaisePropertyChanged(DescriptionProperty);
+                    IsChanged = true;
+                }
+
+                ClearError(DescriptionProperty);
+                var validationResult = _validator.Validate(this, DescriptionProperty);
+                if (!validationResult.IsValid)
+                    validationResult.Errors.ToList().ForEach(x => SetError(DescriptionProperty, x.ErrorMessage));
+            }
+        }
+
+        #endregion
+
+        #region Commands
+
+        public ICommand OkCommand { get; set; }
+        public ICommand CancelCommand { get; set; }
+
+        private void OkCommandExecute(object obj)
+        {
+            SetProperties(_data);
+
+            if (IsChanged && !HasErrors)
+            {
+                AcceptChanges();
+            }
+        }
+
+        private void CancelCommandExecute(object obj)
+        {
+            CancelChanges();
+        }
+        
+        #endregion
+
+        #region IChangeTrack plus Cancel
+
+        public void AcceptChanges()
+        {
+            MessageBox.Show("Saving...");
+            SetProperties(_backup);
+
+            ClearAllErrors();
+            IsChanged = false;
+        }
+
+        public void CancelChanges()
+        {
+            if (!IsChanged) return;
+            SetProperties(_backup);
+            ClearAllErrors();
+            IsChanged = false;
+        }
+
+        public bool IsChanged { get; private set; }
+
+        #endregion
+
+        #region 
+
+        private bool inEdit;
+        public void BeginEdit()
+        {
+            if (inEdit) return;
+            inEdit = true;
+            SetProperties(_backup);
+        }
+
+        public void CancelEdit()
+        {
+            if (!inEdit) return;
+            inEdit = false;
+            CancelChanges();
+        }
+
+        public void EndEdit()
+        {
+            if (!inEdit) return;
+            inEdit = false;
+            SetProperties(_backup);
+        }
+
+        #endregion
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/ViewModels/ViewModelBase.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,73 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+
+namespace SilverlightValidation.ViewModels
+{
+    public class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
+    {
+        #region INotifyPropertyChanged method plus event
+
+        public event PropertyChangedEventHandler PropertyChanged = delegate { };
+
+        protected void RaisePropertyChanged(string propertyName)
+        {
+            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+        }
+
+        #endregion
+
+        #region INotifyDataErrorInfo methods and helpers
+
+        private readonly Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
+
+        public void SetError(string propertyName, string errorMessage)
+        {
+            if (!_errors.ContainsKey(propertyName))
+                _errors.Add(propertyName, new List<string> { errorMessage });
+
+            RaiseErrorsChanged(propertyName);
+        }
+
+        protected void ClearError(string propertyName)
+        {
+            if (_errors.ContainsKey(propertyName))
+            {
+                _errors.Remove(propertyName);
+                RaiseErrorsChanged(propertyName);
+            }
+        }
+
+        protected void ClearAllErrors()
+        {
+            var errors = _errors.Select(error => error.Key).ToList();
+
+            foreach (var propertyName in errors)
+                ClearError(propertyName);
+        }
+
+        public void RaiseErrorsChanged(string propertyName)
+        {
+            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
+        }
+
+        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged = delegate { };
+
+        public IEnumerable GetErrors(string propertyName)
+        {
+            if (propertyName == null) return null;
+            return _errors.ContainsKey(propertyName)
+                    ? _errors[propertyName]
+                    : null;
+        }
+
+        public bool HasErrors
+        {
+            get { return _errors.Count > 0; }
+        }
+
+        #endregion
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Views/UserListView.xaml	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,83 @@
+<UserControl x:Class="SilverlightValidation.Views.UserListView"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:p="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls"
+             xmlns:s="clr-namespace:System;assembly=mscorlib"
+             xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
+             d:DesignHeight="400"
+             d:DesignWidth="725"
+             mc:Ignorable="d">
+
+    <Grid x:Name="LayoutRoot" Background="White">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="30" />
+            <RowDefinition Height="40" />
+            <RowDefinition Height="300" />
+            <RowDefinition Height="50" />
+        </Grid.RowDefinitions>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="*" />
+            <ColumnDefinition Width="725" />
+            <ColumnDefinition Width="*" />
+        </Grid.ColumnDefinitions>
+
+        <StackPanel Grid.Row="1"
+                    Grid.Column="1"
+                    HorizontalAlignment="Right"
+                    Orientation="Horizontal">
+            <Button Width="60"
+                    Command="AddCommand"
+                    Content="Add"
+                    Style="{StaticResource ButtonStyle}" />
+            <Button Width="60"
+                    Command="{Binding CancelCommand}"
+                    Content="Delete"
+                    Style="{StaticResource ButtonStyle}" />
+        </StackPanel>
+
+        <controls:DataGrid Grid.Row="2"
+                           Grid.Column="1"
+                           AutoGenerateColumns="False"
+                           ItemsSource="{Binding Data}"
+                           SelectedItem="{Binding SelectedItem}">
+            <controls:DataGrid.Columns>
+                <controls:DataGridTextColumn Width="125"
+                                             Binding="{Binding Username,
+                                                               Mode=TwoWay,
+                                                               ValidatesOnNotifyDataErrors=True,
+                                                               NotifyOnValidationError=True}"
+                                             Header="Username" />
+                <controls:DataGridTemplateColumn Width="125" Header="Password">
+                    <sdk:DataGridTemplateColumn.CellTemplate>
+                        <DataTemplate>
+                            <PasswordBox Password="{Binding Password, Mode=TwoWay, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}" />
+                        </DataTemplate>
+                    </sdk:DataGridTemplateColumn.CellTemplate>
+                </controls:DataGridTemplateColumn>
+                <controls:DataGridTextColumn Width="150"
+                                             Binding="{Binding Email,
+                                                               Mode=TwoWay,
+                                                               ValidatesOnNotifyDataErrors=True,
+                                                               NotifyOnValidationError=True}"
+                                             Header="Email" />
+
+                <controls:DataGridTemplateColumn Width="150" Header="Date of Birth">
+                    <sdk:DataGridTemplateColumn.CellTemplate>
+                        <DataTemplate>
+                            <sdk:DatePicker KeyDown="DatePicker_KeyDown" SelectedDate="{Binding DateOfBirth, Mode=TwoWay, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}" />
+                        </DataTemplate>
+                    </sdk:DataGridTemplateColumn.CellTemplate>
+                </controls:DataGridTemplateColumn>
+                <controls:DataGridTextColumn Width="150"
+                                             Binding="{Binding Description,
+                                                               Mode=TwoWay,
+                                                               ValidatesOnNotifyDataErrors=True,
+                                                               NotifyOnValidationError=True}"
+                                             Header="Description" />
+            </controls:DataGrid.Columns>
+        </controls:DataGrid>
+    </Grid>
+</UserControl>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Views/UserListView.xaml.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,28 @@
+using System.Windows.Browser;
+using System.Windows.Input;
+using SilverlightValidation.Data;
+using SilverlightValidation.ViewModels;
+using SilverlightValidation.Validators;
+
+namespace SilverlightValidation.Views
+{
+    public partial class UserListView
+    {
+        private UserListViewModel vm;
+
+        public UserListView()
+        {
+            InitializeComponent();
+            HtmlPage.Document.SetProperty("title", "Silverlight Validation");
+
+            vm = new UserListViewModel(Factory.CreateUserModels(), new UserModelValidator());
+            this.DataContext = vm;
+        }
+
+        private void DatePicker_KeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.Key != Key.Tab)
+                e.Handled = true;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Views/UserView.xaml	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,145 @@
+<UserControl x:Class="SilverlightValidation.Views.UserView"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:p="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls"
+             xmlns:s="clr-namespace:System;assembly=mscorlib"
+             xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
+             d:DesignHeight="400"
+             d:DesignWidth="450"
+             mc:Ignorable="d">
+
+    <Grid x:Name="LayoutRoot" Background="White">
+
+        <Grid.RowDefinitions>
+            <RowDefinition Height="30" />
+            <RowDefinition Height="30" />
+            <RowDefinition Height="30" />
+            <RowDefinition Height="30" />
+            <RowDefinition Height="30" />
+            <RowDefinition Height="30" />
+            <RowDefinition Height="50" />
+            <RowDefinition Height="150" />
+        </Grid.RowDefinitions>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="*" />
+            <ColumnDefinition Width="100" />
+            <ColumnDefinition Width="300" />
+            <ColumnDefinition Width="30" />
+            <ColumnDefinition Width="*" />
+        </Grid.ColumnDefinitions>
+
+        <TextBlock Grid.Row="1"
+                   Grid.Column="1"
+                   Style="{StaticResource LabelStyle}"
+                   Text="Username:" />
+
+        <TextBox x:Name="tbUsername"
+                 Grid.Row="1"
+                 Grid.Column="2"
+                 LostFocus="tbUsername_LostFocus"
+                 Style="{StaticResource TextBoxStyle}"
+                 Text="{Binding Username,
+                                Mode=TwoWay,
+                                ValidatesOnNotifyDataErrors=True,
+                                NotifyOnValidationError=True}" />
+
+        <sdk:DescriptionViewer Grid.Row="1"
+                               Grid.Column="3"
+                               Width="20"
+                               Description="Required"
+                               Target="{Binding ElementName=tbUsername}" />
+
+        <TextBlock Grid.Row="2"
+                   Grid.Column="1"
+                   Style="{StaticResource LabelStyle}"
+                   Text="Password:" />
+
+        <PasswordBox x:Name="tbPassword"
+                     Grid.Row="2"
+                     Grid.Column="2"
+                     LostFocus="tbPassword_LostFocus"
+                     Password="{Binding Password,
+                                        Mode=TwoWay,
+                                        ValidatesOnNotifyDataErrors=True,
+                                        NotifyOnValidationError=True}"
+                     Style="{StaticResource PasswordBoxStyle}" />
+
+        <sdk:DescriptionViewer Grid.Row="2"
+                               Grid.Column="3"
+                               Width="20"
+                               Description="Required"
+                               Target="{Binding ElementName=tbPassword}" />
+
+        <TextBlock Grid.Row="3"
+                   Grid.Column="1"
+                   Style="{StaticResource LabelStyle}"
+                   Text="Email:" />
+
+        <TextBox x:Name="tbEmail"
+                 Grid.Row="3"
+                 Grid.Column="2"
+                 LostFocus="tbEmail_LostFocus"
+                 Style="{StaticResource TextBoxStyle}"
+                 Text="{Binding Email,
+                                Mode=TwoWay,
+                                ValidatesOnNotifyDataErrors=True,
+                                NotifyOnValidationError=True}" />
+
+        <sdk:DescriptionViewer Grid.Row="3"
+                               Grid.Column="3"
+                               Width="20"
+                               Description="Required"
+                               Target="{Binding ElementName=tbEmail}" />
+
+        <TextBlock Grid.Row="4"
+                   Grid.Column="1"
+                   Style="{StaticResource LabelStyle}"
+                   Text="Date of Birth:" />
+
+        <sdk:DatePicker x:Name="dpDateOfBirth"
+                        Grid.Row="4"
+                        Grid.Column="2"
+                        KeyDown="DatePicker_KeyDown"
+                        LostFocus="dpDateOfBirth_LostFocus"
+                        SelectedDate="{Binding DateOfBirth,
+                                               Mode=TwoWay,
+                                               ValidatesOnNotifyDataErrors=True,
+                                               NotifyOnValidationError=True}"
+                        Style="{StaticResource DatePickerStyle}" />
+        <sdk:DescriptionViewer Grid.Row="4"
+                               Grid.Column="3"
+                               Width="20"
+                               Description="Required"
+                               Target="{Binding ElementName=dpDateOfBirth}" />
+
+        <TextBlock x:Name="tbDescription"
+                   Grid.Row="5"
+                   Grid.Column="1"
+                   Style="{StaticResource LabelStyle}"
+                   Text="Description:" />
+
+        <TextBox Grid.Row="5"
+                 Grid.Column="2"
+                 Style="{StaticResource TextBoxStyle}"
+                 Text="{Binding Description}" />
+        <StackPanel Grid.Row="6"
+                    Grid.Column="2"
+                    HorizontalAlignment="Right"
+                    Orientation="Horizontal">
+            <Button Command="{Binding OkCommand}"
+                    Content="OK"
+                    Style="{StaticResource ButtonStyle}" />
+            <Button Command="{Binding CancelCommand}"
+                    Content="Cancel"
+                    Style="{StaticResource ButtonStyle}" />
+        </StackPanel>
+
+        <sdk:ValidationSummary Grid.Row="7"
+                               Grid.Column="1"
+                               Grid.ColumnSpan="2"
+                               Style="{StaticResource ValidationSummaryStyle}" />
+
+    </Grid>
+</UserControl>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SilverlightValidation/SilverlightValidation/Views/UserView.xaml.cs	Sat Apr 21 15:06:48 2012 +0100
@@ -0,0 +1,58 @@
+using System.Windows;
+using System.Windows.Browser;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+using SilverlightValidation.ViewModels;
+using SilverlightValidation.Models;
+using SilverlightValidation.Validators;
+
+namespace SilverlightValidation.Views
+{
+    public partial class UserView
+    {
+        private UserViewModel vm;
+        public UserView()
+        {
+            InitializeComponent();
+            HtmlPage.Document.SetProperty("title", "Silverlight Validation");
+
+            vm = new UserViewModel(UserModel.Create(), new UserModelValidator());
+            this.DataContext = vm;
+        }
+
+        private static void UpdateTextBoxSource(object sender)
+        {
+            BindingExpression be = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
+            be.UpdateSource();
+        }
+
+        private void DatePicker_KeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.Key != Key.Tab)
+                e.Handled = true;
+        }
+
+        private void tbUsername_LostFocus(object sender, RoutedEventArgs e)
+        {
+            UpdateTextBoxSource(sender);
+        }
+
+        private void tbPassword_LostFocus(object sender, RoutedEventArgs e)
+        {
+            BindingExpression be = ((PasswordBox)sender).GetBindingExpression(PasswordBox.PasswordProperty);
+            be.UpdateSource();
+        }
+
+        private void tbEmail_LostFocus(object sender, RoutedEventArgs e)
+        {
+            UpdateTextBoxSource(sender);
+        }
+
+        private void dpDateOfBirth_LostFocus(object sender, RoutedEventArgs e)
+        {
+            BindingExpression be = ((DatePicker)sender).GetBindingExpression(DatePicker.SelectedDateProperty);
+            be.UpdateSource();
+        }
+    }
+}
\ No newline at end of file