Sunday 29 April 2012

RabbitMQ C# Pub/Sub

RabbitMQ   Burrow

In this blog post I’ll show how to set up RabbitMQ on a Windows7 machine and create a C# pub/sub workflow application using Burrow.NET.

Unable to display content. Adobe Flash is required.

Installing up RabbitMQ

Download and install Erlang and then RabbitMQ:

http://www.erlang.org/download.html  (Windows Binary File)

http://www.rabbitmq.com/server.html  (Installer for Windows systems)

Configuring RabbitMQ

Create the following file:  C:\Users\{username}\AppData\Roaming\RabbitMQ\rabbitmq.config

{"[{listeners,     [{mgmt, [{port, 55672}]}]},
{default_listener, [{port, 55670}]},
{contexts, [{rabbit_mgmt, mgmt}]}]"}

rabbitmq-config


Open a command prompt and run the following commands:

cd C:\Program Files (x86)\RabbitMQ Server\rabbitmq_server-2.8.0\sbin
rabbitmq-plugins enable rabbitmq_management
rabbitmq-service.bat install

rabbitmq-admin


Open Services and configure your RabbitMQ service as per your preferences (startup type and logon account).  Make sure the service is started.


image


RabbitMQ is now installed as a service with the web management portal available at http://localhost:55672

Username:  guest
Password:  guest

rabbit-web 

Setting up RabbitMQ


Add an Exchange of type “direct”


Select the Exchange tab and scroll down to add a new Exchange.


image


Add a Queue per message type


Select the Queue tab and scroll down to add a new Queue for “Durable” durability.

Naming convention:  Burrow.Queue.{SubscriptionName}.{MessageType}

The message type is the serialisable C# object you’ll be publishing to the queue.


image


Note: The Burrow.Queue.Error will be automatically generated if required


image


Finally add a binding per queue


Click on a queue in the list then add a binding to the exchange.

image


image


The Workflow


image


C# Pub/Sub with Burrow.NET


Burrow.NET is available from NuGest, here is how to use it:

string QUEUE_CONNECTION_STRING = "host=localhost;username=guest;password=guest";
string ORDER_ROUTING_KEY = "Order";

// set up rabbitmq tunnel
ITunnel tunnel = RabbitTunnel.Factory.Create(QUEUE_CONNECTION_STRING);
tunnel.SetSerializer(new JsonSerializer());

// sub
// where the named method interacts with the UI
// "Workflow" is used as the subscription name within the queue Burrow.Queue.Workflow.OrderMessage
var subscriptionOrder = tunnel.SubscribeAsync<OrderMessage>("Workflow",
(message, args) => Dispatcher.BeginInvoke(new Action(() => OnOrderReceived(message, args))));

// pub
tunnel.Publish(new OrderMessage
{
OrderId = orderId,
OrderDate = DateTime.Now,
ProductCode = "ABCDE",
Quantity = 10,
UnitPrice = 9.99m
}, ORDER_ROUTING_KEY);

Source


http://stevenhollidge.com/blog-source-code/RabbitMQ-Workflow.zip

UIMessage for Silverlight

Here is a shameless (and less functional) copy of the great little JavaScript project by Hans FjÀllemark and John Papa https://github.com/KnockedUp/toastR.

The UIMessage control also works in WPF.

Demo (please click the submit button)

Other Message Types

errorquestioninfo

How to use UIMessage

<UserControl x:Class="UIMessageDemo_SL5.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UIMessageDemo_SL5"
Width="400"
Height="150">

<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>

<Button x:Name="btnShowMessage"
Grid.Column="0"
Width="80"
Height="50"
Click="btnShowMessage_Click"
Content="Submit" />

<local:UIMessage x:Name="Message"
Grid.Column="1"
MessageType="Success"
Text="Success" />

</Grid>

</UserControl>

How to open UIMessage

using System.Windows;

namespace UIMessageDemo_SL5
{
public partial class MainPage
{
public MainPage()
{
InitializeComponent();
}

private void btnShowMessage_Click(object sender, RoutedEventArgs e)
{
Message.Show();
}
}
}

UIMessage.xaml

<UserControl x:Class="UIMessageDemo_SL5.UIMessage"
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"
x:Name="UIMessageControl"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<UserControl.Resources>
<SolidColorBrush x:Key="ErrorBrush" Color="#D5BD3630" />
<SolidColorBrush x:Key="InfoBrush" Color="#D759ABC3" />
<SolidColorBrush x:Key="SuccessBrush" Color="#E151A351" />
<SolidColorBrush x:Key="QuestionBrush" Color="#DCF9A938" />
<BitmapImage x:Key="ErrorImage" UriSource="images/error.png" />
<BitmapImage x:Key="InfoImage" UriSource="images/info.png" />
<BitmapImage x:Key="SuccessImage" UriSource="images/success.png" />
<BitmapImage x:Key="QuestionImage" UriSource="images/question.png" />
<Storyboard x:Key="FadeIn">
<DoubleAnimation BeginTime="0"
Duration="00:00:02"
From="0"
Storyboard.TargetName="Display"
Storyboard.TargetProperty="Opacity"
To="1" />
</Storyboard>
<Storyboard x:Key="FadeOut">
<DoubleAnimation BeginTime="00:00:01"
Duration="00:00:02"
From="1"
Storyboard.TargetName="Display"
Storyboard.TargetProperty="Opacity"
To="0" />
</Storyboard>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">

<Border x:Name="Display"
Width="150"
Height="70"
Background="Pink"
CornerRadius="5"
Opacity="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Image x:Name="Icon"
Grid.Row="0"
Grid.Column="0"
Width="30"
Height="30"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Foreground="White"
Text="{Binding Path=Text,
ElementName=UIMessageControl,
UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Border>
</Grid>
</UserControl>


UIMessage.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;

namespace UIMessageDemo_SL5
{
public enum MessageType
{
NotSet = 0,
Error,
Info,
Success,
Question
}
public partial class UIMessage : UserControl
{
#region Fields

private SolidColorBrush _errorBrush;
private SolidColorBrush _infoBrush;
private SolidColorBrush _successBrush;
private SolidColorBrush _questionBrush;

private BitmapImage _errorImage;
private BitmapImage _infoImage;
private BitmapImage _successImage;
private BitmapImage _questionImage;

private Storyboard _fadeInStoryboard;
private Storyboard _fadeOutStoryboard;

#endregion

#region Constructor

public UIMessage()
{
InitializeComponent();
CheckResourcesAreAvailable();

_fadeInStoryboard.Completed += (sender, args) => _fadeOutStoryboard.Begin();
}

#endregion

#region Dependency Properties: MessageType, Text

public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register("MessageType",
typeof(MessageType),
typeof(UIMessage),
new PropertyMetadata(MessageType.NotSet, TypePropertyChanged));

private static void TypePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var uiMessage = (UIMessage)dependencyObject;
uiMessage.Display.SetValue(Border.BackgroundProperty, uiMessage.GetBrush(uiMessage.MessageType));
uiMessage.Icon.SetValue(Image.SourceProperty, uiMessage.GetImage(uiMessage.MessageType));
}

public MessageType MessageType
{
get { return (MessageType)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}

public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(UIMessage),
new PropertyMetadata(default(string)));

public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}

#endregion

#region Methods

private void CheckResourcesAreAvailable()
{
_errorBrush = (SolidColorBrush)this.Resources["ErrorBrush"];
_infoBrush = (SolidColorBrush)this.Resources["InfoBrush"];
_successBrush = (SolidColorBrush)this.Resources["SuccessBrush"];
_questionBrush = (SolidColorBrush)this.Resources["QuestionBrush"];
_errorImage = (BitmapImage)this.Resources["ErrorImage"];
_infoImage = (BitmapImage)this.Resources["InfoImage"];
_successImage = (BitmapImage)this.Resources["SuccessImage"];
_questionImage = (BitmapImage)this.Resources["QuestionImage"];
_fadeInStoryboard = (Storyboard)this.Resources["FadeIn"];
_fadeOutStoryboard = (Storyboard)this.Resources["FadeOut"];

if (_errorBrush == null) throw new Exception("Missing ErrorBrush resource");
if (_infoBrush == null) throw new Exception("Missing InfoBrush resource");
if (_successBrush == null) throw new Exception("Missing SuccessBrush resource");
if (_questionBrush == null) throw new Exception("Missing QuestionBrush resource");
if (_errorImage == null) throw new Exception("Missing BitmapImage ErrorImage resource");
if (_infoImage == null) throw new Exception("Missing BitmapImage InfoImage resource");
if (_successImage == null) throw new Exception("Missing BitmapImage SuccessImage resource");
if (_questionImage == null) throw new Exception("Missing BitmapImage QuestionImage resource");
if (_fadeInStoryboard == null) throw new Exception("Missing Storyboard FadeIn resource");
if (_fadeOutStoryboard == null) throw new Exception("Missing Storyboard FadeOut resource");
}

private SolidColorBrush GetBrush(MessageType type)
{
SolidColorBrush brush = null;
switch (type)
{
case MessageType.Info:
brush = _infoBrush;
break;
case MessageType.Error:
brush = _errorBrush;
break;
case MessageType.Success:
brush = _successBrush;
break;
case MessageType.Question:
brush = _questionBrush;
break;
}
return brush;
}

private BitmapImage GetImage(MessageType type)
{
BitmapImage image = null;
switch (type)
{
case MessageType.Info:
image = _infoImage;
break;
case MessageType.Error:
image = _errorImage;
break;
case MessageType.Success:
image = _successImage;
break;
case MessageType.Question:
image = _questionImage;
break;
}
return image;
}

public void Show()
{
_fadeInStoryboard.Begin();
}

#endregion
}
}

Source

http://stevenhollidge.com/blog-source-code/UIMessageDemo_SL5.zip

http://stevenhollidge.com/blog-source-code/UIMessageDemo_WPF.zip

TryFindResource Silverlight

A nice hierarchical resource search in Silverlight I came across in on internet, returns null if the resource cannot be found.

public static class FrameworkElementExtensions
{
public static object TryFindResource(this FrameworkElement element, object resourceKey)
{
FrameworkElement currentElement = element;

while (currentElement != null)
{
object resource = currentElement.Resources[resourceKey];
if (resource != null)
{
return resource;
}

currentElement = currentElement.Parent as FrameworkElement;
}

return Application.Current.Resources[resourceKey];
}
}

Original post:  http://blog.functionalfun.net/2011/01/findresource-implementation-for.html

Friday 27 April 2012

NCrunch: Try it, you’ll love it!

NCrunch is an automated parallel continuous testing tool in Visual Studio.

Your tests are run in parallel as you code, giving you real time feedback on failing tests, code coverage and long running code called hotspots.

I love it and think it should probably be built into Visual Studio but for now download it and give it a try – you won’t regret it!

Home page for NCrunch:    http://www.ncrunch.net/

Sample project with tests:  LibrarySystem.zip

ncrunch

Thursday 26 April 2012

Singletons

As per the word of Jon Skeet: http://csharpindepth.com/Articles/General/Singleton.aspx

A Simple Solution

public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();

static Singleton() { }

private Singleton() { }

public static Singleton Instance { get { return instance; } }
}

A Lazy Singleton

public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());

public static Singleton Instance { get { return lazy.Value; } }

private Singleton() { }
}

Wednesday 25 April 2012

Binding Converter Parameters to StaticResources

Here is the bad news, you can’t but here’s the good news, using a bit of jiggery pokery you can easily work around it!

Scenario

image

I had a view containing three ellipses that I wanted to colour based on a property for their data context.  The expected result was blue if the value was zero, red if the value was greater than zero and yellow if something went wrong.

The Obvious Approach Doesn’t Work

I had my colours set in my UserControl.Resources so I would like to have used the following code:

<UserControl.Resources>
<SolidColorBrush x:Name="ZeroBrush" Color="SteelBlue" />
<SolidColorBrush x:Name="OverZeroBrush" Color="Red" />
</UserControl.Resources>

<Ellipse Fill="{Binding Path=Count,
Converter={converters:CountToBrushConverter
ZeroBrush={StaticResource ZeroBrush},
OverZeroBrush={StaticResource OverZeroBrush}}}" />
public class CountToBrushConverter : MarkupExtension, IMultiValueConverter
{
private static CountToBrushConverter _converter;
private readonly SolidColorBrush INCORRECT_USAGE_OF_CONVERTER = new SolidColorBrush(Color.FromArgb(255, 255, 255, 0));

public SolidColorBrush ZeroBrush { get; set; }
public SolidColorBrush OverZeroBrush { get; set; }

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int count;
if (!Int32.TryParse(values[0].ToString(), out count)) return INCORRECT_USAGE_OF_CONVERTER;

return count == 0 ? ZeroBrush : OverZeroBrush;
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
return _converter ?? (_converter = new CountToBrushConverter());
}
}

The problem with this approach is that the value converter properties are not dependency objects and therefore cannot have a binding assigned to them.


The Solution


Swap your IValueConverter for an IMultiValueConverter and use an implementation of the multi-binding approach from WPF. 

<Ellipse Fill="{xaml:MultiBinding Converter={converters:CountToBrushConverter}, 
Source1={Binding Path=Count},
Source2={StaticResource ZeroBrush},
Source3={StaticResource OverZeroBrush}}" />
public class CountToBrushConverter : MarkupExtension, IMultiValueConverter
{
private static CountToBrushConverter _converter;
private readonly SolidColorBrush INCORRECT_USAGE_OF_CONVERTER = new SolidColorBrush(Color.FromArgb(255, 255, 255,0));

/// <summary>
/// Returns a SolidBrushColour based on whether the count = 0 or not
/// </summary>
/// <param name="values">Array of objects: value[0] = Count, value[1] = ZeroBrush, value[2] = OverZeroBrush</param>
/// <param name="targetType">Not used.</param>
/// <param name="parameter">Not used.</param>
/// <param name="culture">Not used.</param>
/// <returns>SolidBrushColor</returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length != 3) return INCORRECT_USAGE_OF_CONVERTER;

int count;
if (!Int32.TryParse(values[0].ToString(), out count)) return INCORRECT_USAGE_OF_CONVERTER;

var zeroBrush = values[1] as SolidColorBrush;
if (zeroBrush == null) return INCORRECT_USAGE_OF_CONVERTER;

var overZeroBrush = values[2] as SolidColorBrush;
if (overZeroBrush == null) return INCORRECT_USAGE_OF_CONVERTER;

return count == 0
? zeroBrush
: overZeroBrush;
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
return _converter ?? (_converter = new CountToBrushConverter());
}
}

Multi Bindings for Silverlight


Henrik Jonsson has an implementation on Code Project:
http://www.codeproject.com/Articles/286171/MultiBinding-in-Silverlight-5

Colin Eberhardt has another option on his blog:
http://www.scottlogic.co.uk/blog/colin/2010/05/silverlight-multibinding-solution-for-silverlight-4/

You can download a zip of the version I used:
http://stevenhollidge.com/blog-source-code/MultiBinding.zip

Tuesday 24 April 2012

Silverlight Glimpse

Silverlight Glimpse

Silverlight Glimpse helps developers track down binding errors, highlight validation errors and unhandled exceptions in their application.

A small Silverlight Glimpse window sits on top of your Silverlight application displaying real time error counts for bindings, validations and exceptions as your application hits it's bugs.

To view the detail of each individual error just click the expand button on the window to show all the relevant information.

This debugging tool written in C# and compiles to a dll which can be referenced from your Silverlight application.

It’s an open source project, written C# and available on CodePlex:  https://silverlightglimpse.codeplex.com/

History

This tool is based upon the original Glimpse for Silverlight 3 written in VB.NET by Karl Shifflett.  For those that don’t know him, he’s a WPF disciple and one of the top guys in the XAML!

His blog post in 2009 on the original project can be found here:

http://karlshifflett.wordpress.com/2009/06/08/glimpse-for-silverlight-viewing-exceptions-and-binding-errors/

Adding Silverlight Glimpse to your App

Simply wrap the setting of your RootVisual with the following code:

private void Application_Startup(object sender, StartupEventArgs e)
{
try
{
this.RootVisual = new MainPage();
Glimpse.Service.Load(this);
}
catch (Exception ex)
{
Glimpse.Service.DisplayLoadFailure(this, ex);
}
}

On Start up


When your application loads Silverlight Glimpse will add a little window in the top left hand corner of the screen.


OneBrokenBinding


Exception on Application Start up


If an exception occurs during the loading of your root visual page, Glimpse will take over and display the relevant information for you to troubleshoot.


ExceptionOnStartup


User Interface


The three numbers relate to the three different types of errors that Silverlight Glimpse can detect:


image


Expanded View


When the user clicks on the Expand button you get to see the detail of the binding errors, validation errors and unhandled exceptions.


There is also a debug tab which I’ll explain shortly.


BindingError


In this example, the Silverlight screen had one binding in the tbDescription textbox as the Path was set to DescriptionWithATypeo.  This helps the developer track down and fix the bug.


ide


Polling vs Real Time


The validation errors and unhandled exceptions are updated in real time.


Unfortunately, due to the Silverlight and security restrictions I wasn’t able to hook into events for the binding errors (which are also output to the Output window in Visual Studio).


Therefore Silverlight Glimpse polls the root visual element and all it’s children to pick up the broken bindings.  The refresh rate defaults to five seconds but you can override that value.


The debug tab shows the length of time it took to traverse the VisualTree to locate the broken bindings.


timebindings


Validation Errors


This user has started entering data which has so far failed on all four of the controls.  Silverlight Glimpse detects these validations based on controls using NotifyOnValidationError:

<TextBox Text="{Binding Username,
Mode=TwoWay,
NotifyOnValidationError=True}" />

This can help developers that are working on custom styles and may not have visual feedback available.


validationerroorsvalidationdetail


Unhandled Exceptions


If your application fails to handle any exceptions these will be detected and displayed in real time.


error


errordetail


Current Known Restrictions


Binding errors and validation errors are not currently being detected in Child Windows (part of the Silverlight SDK).  Exceptions will still be tracked.


Open Source on CodePlex


https://silverlightglimpse.codeplex.com/

Monday 23 April 2012

PropertyPathHelper

Ever needed to get the value from a binding in code?   Well here you go.

public static class PropertyPathHelper
{
private static readonly Dummy _dummy = new Dummy();

public static object GetValue(object source, string propertyPath)
{
var binding = new Binding(propertyPath)
{
Mode = BindingMode.OneTime,
Source = source
};
BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
return _dummy.GetValue(Dummy.ValueProperty);
}

private class Dummy : DependencyObject
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof (object),
typeof (Dummy),
new PropertyMetadata(null));
}
}
Thanks to Thomas Levesque for his answer on Stack Overflow:
http://stackoverflow.com/questions/3577802/wpf-getting-a-property-value-from-a-binding-path

BoolToValueConverter<T>

Here is a great little generic BoolTo… converter.  In the example below I use it to define the visibility of a textblock.

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;

namespace SilverlightGlimpse.Converters
{
public class BoolToVisibilityConverter : BoolToValueConverter<Visibility> { }

public class BoolToValueConverter<T> : MarkupExtension, IValueConverter
{
public T FalseValue { get; set; }
public T TrueValue { get; set; }

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return FalseValue;
return (bool)value ? TrueValue : FalseValue;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && value.Equals(TrueValue);
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}
<TextBlock Visibility="{Binding Path=IsInDebugMode, 
Converter={local:BoolToVisibilityConverter
FalseValue=Collapsed,
TrueValue=Visible}}" />


Original author: http://geekswithblogs.net/codingbloke/archive/2010/05/28/a-generic-boolean-value-converter.aspx

Silverlight Stopwatch

There’s no stopwatch in Silverlight but there is nothing to stop you rolling your own.

public class Stopwatch
{
private long _start;
private long _end;

public void Start()
{
_start = DateTime.Now.Ticks;
}

public void Stop()
{
_end = DateTime.Now.Ticks;
}

public TimeSpan ElapsedTime { get { return new TimeSpan(_end – _start); }}

public static Stopwatch StartNew()
{
var stopwatch = new Stopwatch();
stopwatch.Start();
return stopwatch;
}
}

Saturday 21 April 2012

Rijndael

System.Security.Cryptography.RijndaelManaged

Namespace: System.Security.Cryptography
Assembly: mscorlib (in mscorlib.dll)

using System;
using System.IO;
using System.Security.Cryptography;

namespace RijndaelManaged_Example
{
class RijndaelExample
{
public static void Main()
{
try
{
string original = "Here is some data to encrypt!";

using (RijndaelManaged myRijndael = new RijndaelManaged())
{
byte[] encrypted = EncryptStringToBytes(original, myRijndael.Key, myRijndael.IV);
string roundtrip = DecryptStringFromBytes(encrypted, myRijndael.Key, myRijndael.IV);

Console.WriteLine("Original: {0}", original);
Console.WriteLine("Round Trip: {0}", roundtrip);
}
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}

static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
if (string.IsNullOrEmpty(plainText)) throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0) throw new ArgumentNullException("Key");

byte[] encrypted;

using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;

ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

using (MemoryStream msEncrypt = new MemoryStream())
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
swEncrypt.Write(plainText);

encrypted = msEncrypt.ToArray();
}
}

return encrypted;
}

static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
if (cipherText == null || cipherText.Length <= 0) throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0) throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0) throw new ArgumentNullException("Key");

string plaintext;

using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;

ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

using (MemoryStream msDecrypt = new MemoryStream(cipherText))
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}

return plaintext;
}
}
}

image


Taken from MSDN: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged.aspx

Source:  http://stevenhollidge.com/blog-source-code/RijndaelExample.zip

Data Protection API

System.Security.Cryptography.ProtectedData

Namespace: System.Security.Cryptography
Assembly: System.Security (in System.Security.dll)

using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text;

public class DataProtectionSample
{
// Create byte array for additional entropy when using Protect method
static byte[] _additionalEntropy = { 9, 8, 7, 6, 5 };

public static void Main()
{
//Original data
string secret = "Magic";
PrintValues("The original data is:", secret);

//Convert to bytes
byte[] secretInBytes = Encoding.UTF8.GetBytes(secret);
PrintValues("The original data in bytes is:", secretInBytes);

//Encrypt the bytes
byte[] encryptedSecret = Protect(secretInBytes);
PrintValues("The encrypted byte array is:", encryptedSecret);

// Decrypt the bytes
byte[] originalDataInBytes = Unprotect(encryptedSecret);
PrintValues("The original data in bytes is:", originalDataInBytes);

//Convert to string
string originalData = Encoding.UTF8.GetString(originalDataInBytes);
PrintValues("The original data is:", originalData);
}

public static byte[] Protect(byte[] data)
{
// Encrypt the data using DataProtectionScope.CurrentUser.
// The result can be decrypted only by the same current user.
return ProtectedData.Protect(data, _additionalEntropy, DataProtectionScope.CurrentUser);
}

public static byte[] Unprotect(byte[] data)
{
//Decrypt the data using DataProtectionScope.CurrentUser.
return ProtectedData.Unprotect(data, _additionalEntropy, DataProtectionScope.CurrentUser);
}

public static void PrintValues(string header, IEnumerable detail)
{
Console.WriteLine(header);
foreach (var item in detail) { Console.Write("\t{0}", item); }
Console.WriteLine();
}
}
image

Source:  http://stevenhollidge.com/blog-source-code/DataProtectionSample.zip

Friday 20 April 2012

Garbage Collector .NET 4

The Basics

The garbage collector is an automatic memory manager. It provides the following benefits:

  • Enables us to develop the application without having to free memory.
  • Allocates objects efficiently on the managed heap.
  • Reclaims no longer used objects, clears their memory, and keeps the memory for future allocations.
  • Provides memory safety by making sure that an object cannot use the content of another object.

Garbage collection is non deterministic and occurs based on following condition:

  • When system has low physical memory.
  • The threshold of acceptable memory usage of allocated object has been exceeded on the managed heap. This threshold is continuously adjusted as the process runs.
  • The GC.Collect method is called.

The Heap

Simplified model of the managed heap

The rules for this simplified model are as follows:

  • All garbage-collectable objects are allocated from one contiguous range of address space.
  • The heap is divided into generations so that it is possible to eliminate most of the garbage by looking at only a small fraction of the heap.
  • Objects within a generation are all roughly the same age.
  • Higher-numbered generations indicate areas of the heap with older objects—those objects are much more likely to be stable.
  • The oldest objects are at the lowest addresses, while new objects are created at increasing addresses.
  • The allocation pointer for new objects marks the boundary between the used (allocated) and unused (free) areas of memory.
  • Periodically the heap is compacted by removing dead objects and sliding the live objects up toward the low-address end of the heap. This expands the unused area at the bottom of the diagram in which new objects are created.
  • The order of objects in memory remains the order in which they were created, for good locality.
  • There are never any gaps between objects in the heap.
  • Only some of the free space is committed. When necessary, more memory is acquired from the operating system in the reserved address range.

Generations

Garbage collection organizes the managed heap into three generations to handle long live and short live objects:

  • Generation 0. This is the youngest generation and contains short-lived objects. For example temporary variable. Garbage collection occurs most frequently in this generation.

Newly allocated objects are implicitly generation 0 collections, unless they are large objects (85000 bytes and larger), in which case they go on the large object heap in a generation 2 collection.

Most objects are reclaimed for garbage collection in generation 0 and do not survive to the next generation.

  • Generation 1. This generation contains short-lived objects and serves as a buffer between short-lived objects and long-lived objects.
  • Generation 2. This generation contains long-lived objects. An example of a long-lived object is an object in a server application that contains static data that is live for the duration of the process.

Objects that are not reclaimed in a garbage collection are promoted to the next generation. Objects that are not reclaimed in generation 0 garbage collection are promoted to generation 1; objects that are not reclaimed in a generation 1 garbage collection are promoted to generation 2; and objects that survive a generation 2 garbage collection remain in generation 2.

Finalization Queue and Thread

When the GC encounters object with Finalizers that are otherwise “dead” instead of reclaiming the space the objects are added to the Finalization Queue (it’s generation number remains the same).  An aptly named Finalization Thread then works through this queue running the finalizers and marking the objects as dead, ready for the next GC.

GC modes

Workstation vs Server

Workstation GC: The default GC mode and on a single-proc machine it’s the only option. A single heap collected at normal priority.

Server GC: Only available on multi-proc machines it creates one GC heap (and thread) per processor, which are collected in parallel at highest priority.

<configuration>
<runtime>
<gcServer enabled=”true” />
</runtime>
</configuration>

Before .NET 4:  Concurrent (Workstation only)


A basic (non concurrent) GC uses a "stop-the-world" strategy. When the GC must run, all applicative (managed) threads stop. The last stopping thread runs the GC, and unblocks all the other threads when it has finished.


Concurrent GC is used on a multi-proc machine to perform full collections (generation 2) concurrently with the running program, therefore minimizing the pause time. This mode is particularly useful for applications with graphical user interfaces or applications where responsiveness is essential.


Remember, Concurrent GC is only used with generation 2; generations 0 and 1 are always non-concurrent because they finish very fast.


Enabled by default here is how to disable this behaviour:

<configuration>
<runtime>
<gcConcurrent enabled=”false” />
</runtime>
</configuration>

Introduced in .NET 4:  Background [and Foreground] (Workstation only)


Starting with the .NET Framework version 4, background garbage collection replaces concurrent garbage collection. 


In background garbage collection, ephemeral generations (0 and 1) are collected as needed while the collection of generation 2 is in progress. There is no setting for background garbage collection; it is automatically enabled with concurrent garbage collection. As with concurrent garbage collection, background garbage collection is performed on a dedicated thread and is applicable only to generation 2 collections.



So as to allow the background and foreground threads to play nicely with each other, a staging area is found as the alive (blue) objects move from up the generations.



Background GC will be introduced for the Server in .NET 4.5:


http://channel9.msdn.com/posts/Maoni-Stephens-CLR-45-Server-Background-GC


Notifications


Warning:  This code will cause you to run out of memory.

using System;
using System.Collections.Generic;
using System.Threading;

class Program
{
// Variable for continual checking in the
// While loop in the WaitForFullGCProc method.
static bool checkForNotify = false;

// Variable for suspending work (such servicing allocated server requests)
// after a notification is received and then
// resuming allocation after inducing a garbage collection.
static bool bAllocate = false;

// Variable for ending the example.
static bool finalExit = false;

// Collection for objects that simulate the server request workload.
static List<byte[]> load = new List<byte[]>();


public static void Main(string[] args)
{
try
{
// Register for a notification.
GC.RegisterForFullGCNotification(10, 10);
Console.WriteLine("Registered for GC notification.");

checkForNotify = true;
bAllocate = true;

// Start a thread using WaitForFullGCProc.
Thread thWaitForFullGC = new Thread(new ThreadStart(WaitForFullGCProc));
thWaitForFullGC.Start();

// While the thread is checking for notifications in
// WaitForFullGCProc, create objects to simulate a server workload.
try
{
int lastCollCount = 0;
int newCollCount = 0;

while (true)
{
if (bAllocate)
{
load.Add(new byte[1000]);
newCollCount = GC.CollectionCount(2);
if (newCollCount != lastCollCount)
{
// Show collection count when it increases:
Console.WriteLine(
"Gen 2 collection count: {0}",
GC.CollectionCount(2).ToString());
lastCollCount = newCollCount;
}

// For ending the example (arbitrary).
if (newCollCount == 500)
{
finalExit = true;
checkForNotify = false;
break;
}
}
}
}
catch (OutOfMemoryException)
{
Console.WriteLine("Out of memory.");
}

finalExit = true;
checkForNotify = false;
GC.CancelFullGCNotification();
}
catch (InvalidOperationException invalidOp)
{
Console.WriteLine(
"GC Notifications are not supported while concurrent GC is enabled.\n"
+ invalidOp.Message);
}
}

public static void OnFullGCApproachNotify()
{
Console.WriteLine("Redirecting requests.");

// Method that tells the request queuing
// server to not direct requests to this server.
RedirectRequests();

// Method that provides time to
// finish processing pending requests.
FinishExistingRequests();

// This is a good time to induce a GC collection
// because the runtime will induce a full GC soon.
// To be very careful, you can check precede with a
// check of the GC.GCCollectionCount to make sure
// a full GC did not already occur since last notified.
GC.Collect();
Console.WriteLine("Induced a collection.");
}


public static void OnFullGCCompleteEndNotify()
{
// Method that informs the request queuing server
// that this server is ready to accept requests again.
AcceptRequests();
Console.WriteLine("Accepting requests again.");
}

public static void WaitForFullGCProc()
{
while (true)
{
// CheckForNotify is set to true and false in Main.
while (checkForNotify)
{
// Check for a notification of an approaching collection.
GCNotificationStatus s = GC.WaitForFullGCApproach();
if (s == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC Notifiction raised.");
OnFullGCApproachNotify();
}
else if (s == GCNotificationStatus.Canceled)
{
Console.WriteLine("GC Notification cancelled.");
break;
}
else
{
// This can occur if a timeout period is specified for
// WaitForFullGCApproach(Timeout) or WaitForFullGCComplete(Timeout)
// and the time out period has elapsed.
Console.WriteLine("GC Notification not applicable.");
break;
}

// Check for a notification of a completed collection.
s = GC.WaitForFullGCComplete();
if (s == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC Notifiction raised.");
OnFullGCCompleteEndNotify();
}
else if (s == GCNotificationStatus.Canceled)
{
Console.WriteLine("GC Notification cancelled.");
break;
}
else
{
// Could be a time out.
Console.WriteLine("GC Notification not applicable.");
break;
}
}

Thread.Sleep(500);
// FinalExit is set to true right before the main thread cancelled notification.
if (finalExit)
break;
}
}

private static void RedirectRequests()
{
// Code that sends requests to other servers.

// Suspend work.
bAllocate = false;
}

private static void FinishExistingRequests()
{
// Code that waits a period of time for pending requests to finish.

// Clear the simulated workload.
load.Clear();
}

private static void AcceptRequests()
{
// Code that resumes processing requests on this server.

// Resume work.
bAllocate = true;
}
}

Performance Counters


PerfMon lets you track the Garbage Collectors activity:


image


image


image


Profiler


http://memprofiler.com/

Weak Event Handlers

Why would you need to use a Weak Event handler?  Take a look at this example of normal events and handlers:

image

Notice how the Console window is empty.  The Subscriber finalizer ~Subscriber() which writes to the console window has not been called meaning our subscriber is still in memory. This is due to the Publisher event holding a strong reference to the Subscribers event handler (see the watch window).

image

Why not just implement IDispose and unsubscribe from the event?

image

This is the ideal scenario but you are relying upon our Publisher being considerate enough to call dispose on the Subscriber.  This means we can remove the event handler, notice how the publishers event is now null, which means the GarbageCollector can collect our subscriber. 

But what happens if the Publisher forgets to “dispose” of the subscriber?  We still have a memory leak.

Weak Reference Events to the rescue!

image

We can see here in the watch window that the publishers event still has a reference to the subscribers event handler.  But the subscriber has been disposed…. cool!

This is all possible that to the use of a WeakReference.

Special thanks to Distin Campbell, Joe Duffy, Xavier Musy and Gregor R. Peisker.

using System;
using System.Reflection;

public delegate void UnregisterCallback<E>(EventHandler<E> eventHandler)
where E : EventArgs;

public interface IWeakEventHandler<E>
where E : EventArgs
{
EventHandler<E> Handler { get; }
}

public class WeakEventHandler<T, E> : IWeakEventHandler<E>
where T : class
where E : EventArgs
{
private delegate void OpenEventHandler(T @this, object sender, E e);

private WeakReference _targetRef;
private OpenEventHandler _openHandler;
private EventHandler<E> _handler;
private UnregisterCallback<E> _unregister;

public WeakEventHandler(EventHandler<E> eventHandler, UnregisterCallback<E> unregister)
{
_targetRef = new WeakReference(eventHandler.Target);
_openHandler = (OpenEventHandler) Delegate.CreateDelegate(
typeof(OpenEventHandler), null, eventHandler.Method);
_handler = Invoke;
_unregister = unregister;
}

public void Invoke(object sender, E e)
{
T target = (T) _targetRef.Target;

if (target != null)
_openHandler.Invoke(target, sender, e);
else if (_unregister != null)
{
_unregister(_handler);
_unregister = null;
}
}

public EventHandler<E> Handler
{
get { return _handler; }
}

public static implicit operator EventHandler<E>(WeakEventHandler<T, E> weakEventHandler)
{
return weakEventHandler._handler;
}
}

public static class EventHandlerUtils
{
public static EventHandler<E> MakeWeak<E>(this EventHandler<E> eventHandler, UnregisterCallback<E> unregister)
where E : EventArgs
{
if (eventHandler == null)
throw new ArgumentNullException("eventHandler");
if (eventHandler.Method.IsStatic || eventHandler.Target == null)
throw new ArgumentException("Only instance methods are supported.", "eventHandler");

Type weakEventHandlerType = typeof(WeakEventHandler<,>).MakeGenericType(
eventHandler.Method.DeclaringType, typeof(E));

ConstructorInfo weakEventHandlerConstructor = weakEventHandlerType.GetConstructor(
new Type[] { typeof(EventHandler<E>), typeof(UnregisterCallback<E>) });

IWeakEventHandler<E> weakEventHandler = (IWeakEventHandler<E>)weakEventHandlerConstructor.Invoke(
new object[] { eventHandler, unregister });

return weakEventHandler.Handler;
}
}

Performance


Who wants to see a shoot out between strong event handlers and our weak event handler solution?
Ok, so first up here’s the code for our benchmarks:

using System;
using System.Diagnostics;

class Performance
{
const int iterations = 1000000;

public static void Main()
{
Console.WriteLine("Running tests with {0} iterations", iterations);

RunTest("StrongEvent", new StrongEvent.Publisher());
RunTest("StrongEventDisposable", new StrongEventDisposable.Publisher());
RunTest("WeakEvent", new WeakEvent.Publisher());
}

public static void RunTest(string name, IPublisher publisher)
{
GC.Collect();
GC.WaitForPendingFinalizers();

var stopwatch = Stopwatch.StartNew();

for (int i = 0; i < iterations; i++)
{
publisher.CreateSubscriber();
publisher.DestroySubscriber();
}

stopwatch.Stop();
Console.WriteLine("{0} took {1} ms", name, stopwatch.ElapsedMilliseconds);
}
}

Results


image


Ouch!  You win some, you lose some Smile

The results are very interesting, in conclusion it really does pay to be a good .NET citizen, removing event handlers and disposing of an IDisposable wins here.

Microsoft have come up with the Weak Event Pattern for WPF: http://msdn.microsoft.com/en-us/library/aa970850.aspx
I wonder if Microsoft will come up with a better and more generic solution for weak event handlers in .NET 4.5.


Source


http://stevenhollidge.com/blog-source-code/WeakEventReference.zip

Note:  The finalizers have been commented out in the code so as not to affect the performance tests.

Thursday 19 April 2012

Silverlight 5 Validation

Moving forward with .NET 4.5 the validation story will be played out through the INotifyDataErrorInfo interface.

http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifydataerrorinfo(v=vs.110).aspx

Here is an example of a Silverlight 5 grid and form using INDEI for its validation:

This demo can also be accessed online here.

Tricks

This example shows the following tricks when validating user input in Silverlight:

  • MVVM with independent validation rules allowing for contextual validation with injection into view model via the constructor
  • No validation is applied when first loading up the form
  • Validation is applied when each control is updated, with form wide validation being applied when the user clicks the Ok button
  • DatePicker control is loaded with no date and also prevents user textual input along with its validation
  • Fluent Validation uses length, email and regex rules to validate properties
  • View model implements INotifyPropertyChanged, INotifyDataErrorInfo and IEditableObject interfaces
  • INotifyPropertyChanged and INotifyDataErrorInfo implementations are stored within an abstract view model base class
  • Model implements bespoke generic ICloneable interface
  • Cancel (by pressing escape) on grid reverts the data and controls back to original state
  • RelayCommand has been used for the command buttons

Class diagram

image

Styles

This example explicitly contains all styles used in this solution, within the app.xaml file.

Screenshots

Description viewer

Used to show if a field is required and/or any of other relevant info, when the user hovers over the little circle with the i for information to the right of the control.

image

Validation Summary

Lists all validation errors on the grid and form.

image

image

Validation Tooltips

image

image

image

image

Validator code

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 < DateTime.Today.AddYears(-100))
return false;
return true;
}
}
}


ViewModelBase code

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
}
}


UserListViewModel code

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;
using SilverlightValidation.Commands;
using SilverlightValidation.Models;
using SilverlightValidation.Validators;
using SilverlightValidation.Views;
using GalaSoft.MvvmLight.Messaging;
using SilverlightValidation.Messages;

namespace SilverlightValidation.ViewModels
{
public class UserListViewModel
{
UserView window;

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);

Messenger.Default.Register<UserViewResponseMessage>(this, UserViewResponseMessageReceived);
}

private void UserViewResponseMessageReceived(UserViewResponseMessage userViewResponseMessage)
{
if (userViewResponseMessage.UserViewModel != null)
Data.Add(userViewResponseMessage.UserViewModel);
window.Close();
}

#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)
{
window = new UserView();
window.Show();
}

private void DeleteCommandExecute(object obj)
{
if (SelectedItem!=null)
Data.Remove(SelectedItem);
}

#endregion
}
}


UserViewModel

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;
using GalaSoft.MvvmLight.Messaging;
using SilverlightValidation.Messages;

namespace SilverlightValidation.ViewModels
{
public class UserViewModel : ViewModelBase, IUserModel, 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)
{
_data.Username = source.Username;
_data.Password = source.Password;
_data.Email = source.Email;
_data.DateOfBirth = source.DateOfBirth;
_data.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)
{
RefreshToViewErrors();

if (IsChanged && !HasErrors)
{
// save here
Messenger.Default.Send<UserViewResponseMessage>(
new UserViewResponseMessage() { UserViewModel = this });
}
}

// in case user hasn't touched the form
private void RefreshToViewErrors()
{
Username = _data.Username;
Password = _data.Password;
Email = _data.Email;
DateOfBirth = _data.DateOfBirth;
}

private void CancelCommandExecute(object obj)
{
Messenger.Default.Send<UserViewResponseMessage>(
new UserViewResponseMessage() { UserViewModel = null });
}

#endregion

private void ResetFormData()
{
SetProperties(_backup);
ClearAllErrors();
IsChanged = false;
}

public bool IsChanged { get; private set; }

#region IEditableObject for datagrid

private bool inEdit;
public void BeginEdit()
{
if (inEdit) return;
inEdit = true;
}

public void CancelEdit()
{
if (!inEdit) return;
inEdit = false;
ResetFormData();
}

public void EndEdit()
{
if (!inEdit) return;
}

#endregion
}
}


Model code

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();
}
}
}

UserListView code

<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="{Binding AddCommand}"
Content="Add"
Style="{StaticResource ButtonStyle}" />
<Button Width="60"
Command="{Binding DeleteCommand}"
Content="Delete"
Style="{StaticResource ButtonStyle}" />
</StackPanel>

<controls:DataGrid Grid.Row="2"
Grid.Column="1"
AutoGenerateColumns="False"
ItemsSource="{Binding Data}"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<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>


UserView code

<c:ChildWindow x:Class="SilverlightValidation.Views.UserView"
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: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"
Title="Add User"
Width="500"
Height="400">

<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="120" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="300" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>

<TextBlock Grid.Row="1"
Grid.Column="1"
Style="{StaticResource LabelStyle}"
Text="Username:" />

<TextBox x:Name="tbUsername"
Grid.Row="1"
Grid.Column="2"
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"
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"
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"
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>
</c:ChildWindow>

Download the source


http://stevenhollidge.com/blog-source-code/SilverlightValidation.zip