Friday, July 31, 2009

Integrating Validation Application Block with WPF

Business objects can be directly validated in WPF using the IDataErrorInfo interface provided in WPF 3.5. With the help of the VAB and IDataErrorInfo we can implement a very easy and convenient Attribute based validation of the business objects in WPF. In this post, I’ll show an example how to achieve VAB + WPF integration to validate the business objects.

I am going to use the IDataErrorInfo implementation as in my previous article.

Using IDataErrorInfo requires you to implement the methods

public string Error{ get; }

public string this[string columnName]{ get; }

My base entity implements the IDataErrorInfo and INotifyPropertyChanged interfaces

public abstract class BaseEntity : INotifyPropertyChanged, IDataErrorInfo

{

[NotNullValidator(MessageTemplate="Id cannot be null", Tag="Id")]

public T Id { get; set; }

public abstract ValidationResults SelfValidate();

public abstract string GetError(ValidationResults results);

public abstract bool IsValid { get; }

#region IDataErrorInfo Members

public string Error

{

get

{

var __ValidationResults = SelfValidate();

return GetError(__ValidationResults);

}

}

public string this[string columnName]

{

get

{

var __ValidationResults = SelfValidate();

if (__ValidationResults != null)

{

var __ColumnResults = __ValidationResults.FirstOrDefault<ValidationResult>(x => string.Compare(x.Tag, columnName, true) == 0);

return __ColumnResults != null ? __ColumnResults.Message : string.Empty;

}

return string.Empty;

}

}

#endregion

public event PropertyChangedEventHandler PropertyChanged;

public void OnPropertyChanged(string propertyName)

{

if (PropertyChanged != null)

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

}

}

Every business class inherits the base entity class and implements the abstract methods

[HasSelfValidation]

public class User : BaseEntity<Guid>

{

private string ___name;

private decimal ___cost;

private DateTime ___dateOfJoining;

[ValidatorComposition(Microsoft.Practices.EnterpriseLibrary.Validation.CompositionType.And)]

[StringLengthValidator(

0, RangeBoundaryType.Exclusive,

45, RangeBoundaryType.Inclusive,

MessageTemplate="Name should be in the range 0 - 45", Tag="Name")]

[NotNullValidator(MessageTemplate="Name cannot be null")]

public string Name

{

get { return ___name; }

set

{

___name = value;

OnPropertyChanged("Name");

}

}

[RangeValidator(

typeof(decimal),

"0.00", RangeBoundaryType.Inclusive,

"999.99", RangeBoundaryType.Inclusive,

MessageTemplate="Cost should be in the range 0 - 999.99", Tag="Cost")]

public decimal Cost

{

get { return ___cost; }

set

{

___cost = value;

OnPropertyChanged("Cost");

}

}

public DateTime DateOfJoining

{

get { return ___dateOfJoining; }

set

{

___dateOfJoining = value;

OnPropertyChanged("DateOfJoining");

}

}

[SelfValidation]

public void Validate(ValidationResults results)

{

if (DateOfJoining < new DateTime(2003, 10, 1) || DateOfJoining > DateTime.Now)

results.AddResult(new ValidationResult("Date of joining should be between 01-Oct-2003 and today", this, string.Empty, "DateOfJoining", null));

}

public override ValidationResults SelfValidate()

{

return EntityValidator<User>.ValidateEntity(this);

}

public override string GetError(ValidationResults results)

{

return EntityValidator<User>.GetValidationErrors(results);

}

public override bool IsValid

{

get{ return SelfValidate().IsValid; }

}

}

I have created an EntityValidator class that encapsulates my VAB method calls to validate the entity.

Finally the WPF Code

<Style x:Key="__TextBoxStyle" TargetType="{x:Type TextBox}">

<Setter Property="Width" Value="160" />

<Setter Property="Height" Value="30" />

<Setter Property="HorizontalAlignment" Value="Left" />

<Setter Property="VerticalAlignment" Value="Center" />

<Setter Property="Validation.ErrorTemplate">

<Setter.Value>

<ControlTemplate>

<DockPanel DockPanel.Dock="Right" >

<AdornedElementPlaceholder />

<Image Source="{StaticResource Error}"

Width="15" Height="15"

ToolTip="{Binding Path=AdornedElement.ToolTip,

RelativeSource={RelativeSource Mode=FindAncestor,

AncestorType={x:Type Adorner}}}" />

DockPanel>

ControlTemplate>

Setter.Value>

Setter>

<Style.Triggers>

<Trigger Property="Validation.HasError" Value="True">

<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" />

Trigger>

Style.Triggers>

Style>

<TextBox Style="{StaticResource __TextBoxStyle}"

Text="{Binding Path=User.Cost,

Mode=TwoWay,

ValidatesOnDataErrors=True, Converter={StaticResource __DecimalConverter},

UpdateSourceTrigger=PropertyChanged}"

Grid.Column="1" Grid.Row="1" />

I have my SaveCommand implemented in the ViewModel as

public ICommand SaveCommand

{

get

{

if (___SaveCommand == null) ___SaveCommand = new DelegateCommand

(

() => Save(User),

() => User.IsValid

);

return ___SaveCommand;

}

}

<Button Grid.Row="3" Grid.Column="1" Width="120"

Grid.ColumnSpan="2"

HorizontalAlignment="Left"

VerticalAlignment="Center"

Content="Save" Margin="20 0 0 0"

Command="{Binding SaveCommand}" />

The IsValid method checks whether the entity is valid for save.

Output

Thursday, July 23, 2009

Generic implementation of memento pattern in C#

The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).

The memento pattern is used by two objects: the originator and a caretaker. The originator is some object that has an internal state. The caretaker is going to do something to the originator, but wants to be able to undo the change. The caretaker first asks the originator for a memento object. Then it does whatever operation (or sequence of operations) it was going to do. To roll back to the state before the operations, it returns the memento object to the originator.

In this post, I will show how to create a Generic implementation of the memento pattern that can be used to store and retrieve objects that can be serialized.

Originator

public class Originator

{

public T CurrentState { get; set; }

public T Store()

{

return Serialize(CurrentState);

}

public T Retrieve(T storedInstance)

{

CurrentState = storedInstance;

return CurrentState;

}

private Func Serialize = x =>

{

MemoryStream memoryStream = new MemoryStream();

BinaryFormatter binaryFormatter = new BinaryFormatter();

binaryFormatter.Serialize(memoryStream, x);

memoryStream.Seek(0, SeekOrigin.Begin);

T objectCopy = (T)binaryFormatter.Deserialize(memoryStream);

memoryStream.Close();

return objectCopy;

};

}

Caretaker

public class Caretaker

{

public T StoredInstance { get; private set; }

public Caretaker(T instance)

{

StoredInstance = instance;

}

}

The test class

[TestClass()]

public class When_Object_Has_State : ArrangeAssertActTearDown<Originator<User>>

{

User ___UserUnderTest;

Caretaker<User> ___UserCareTaker;

Guid ___OriginalGuid;

Guid ___IdToChange;

public override void Arrange()

{

___UserUnderTest = new User("Prajeesh", "Prathap", 1);

___OriginalGuid = ___UserUnderTest.Guid;

___IdToChange = new Guid("{900F1521-D3BD-4f60-A5F9-8548A098B61B}");

}

public override void Act()

{

SystemUnderTest.CurrentState = ___UserUnderTest;

}

[TestMethod]

public void Must_Restore_To_Old_State_On_Undo()

{

___UserCareTaker = new Caretaker<User>(SystemUnderTest.Store());

User __User = SystemUnderTest.CurrentState;

__User.Guid = ___IdToChange;

Assert.AreEqual<Guid>(___IdToChange, __User.Guid);

__User = SystemUnderTest.Retrieve(___UserCareTaker.StoredInstance);

Assert.AreEqual<Guid>(___OriginalGuid, __User.Guid);

}

public override void TearDown()

{

___UserUnderTest = default(User);

___UserCareTaker = default(Caretaker<User>);

}

}