I have had a great time so far with this series..  I hope you have gotten something out of it as well.  Some readers have asked me if RIA Services is required for the client validation goodness.   Nope, the .NET RIA Services bits are not required… you can do this sort of cool client UI (validation, sorting, filtering, etc) with all client data or data that you got via whatever mechanism.   

So the title is a bit misleading – we are not going to use ANY .NET RIA Services in this section…

image image On the design team for this work, we said we wanted it to be Ice Cubes not Ice Burgs..  that is bits of technology that work really well together and mixed-n-matched.  Rather than a monolithic ice burg that you have to take all or nothing from. 

The hard part of this pattern is getting your data into the Silverlight app…  You’d only really want to use this pattern if you had another good way to get data  in… this demo punts on that problem and just shows in memory data. 

The demo requires (all 100% free and always free):

  1. VS2008 SP1
  2. Silverlight 3 RTM
  3. .NET RIA Services July '09 Preview <--- Not actually required for this demo!  But good to have none the less ;-)

Also, download the full demo files and check out the running application.

For this demo, we will look only at the client side.. we are getting no data from the server.  The only thing that comes from the server is an HTML page containing the XAP.  This could be hosted on any web server.. 

On the client, I created a  DataAccess folder and defined my SuperEmployee class in there

public  class SuperEmployee : INotifyPropertyChanged, IEditableObject
{
 
    private SuperEmployee cache; 
    private int _employeeID;
    private string _gender;
    private Nullable<int> _issues;
    private Nullable<DateTime> _lastEdit;
    private string _name;
    private string _origin;
    private string _publishers;
    private string _sites;
    private static int empIDCt = 0;
 
    public SuperEmployee()
    {
        this._employeeID = empIDCt++;
    }

Notice it implements a couple of interfaces to help with the binding… we will look at how those are implemented shortly.    In the constructor, i auto increment the employeeID key.. you can do that however you want of course.

Then for each property we implement the following pattern:

 
        [DataMember()]
        [Key()]
        [ReadOnly(true)]
        public int EmployeeID
        {
            get
            {
                return this._employeeID;
            }
            set
            {
                if ((this._employeeID != value))
                {
                    ValidationContext context = new ValidationContext(this, null, null);
                    context.MemberName = "EmployeeID";
                    Validator.ValidateProperty(value, context);
                    this._employeeID = value;
                    this.OnPropertyChanged("EmployeeID");
                }
            }
        }

Notice all the fun happens in the setter.  There, we create a ValidationContext and call ValidateProperty.. that is the thing that goes and looks at the attributes on this member and does the validation work.  It throws an exception  when there is a problem which is how Silverlight client deals with validation issues.   Notice here we are also raising property changed notifications, again to help with binding.  Each one of the properties looks just like this..

Then, for DataForm support, we need to implement IEditableObject  basically supporting cancelablity.  I choose a pretty simple pattern for this.  Notice I just save off the value on Begin edit and copy it back if CancelEdit is called. 

public void BeginEdit()
 {
     this.cache = new SuperEmployee();
     this.cache.EmployeeID = this.EmployeeID;
     this.cache.Gender = this.Gender;
     this.cache.Issues = this.Issues;
     this.cache.LastEdit = this.LastEdit;
     this.cache.Name = this.Name;
     this.cache.Origin = this.Origin;
     this.cache.Publishers = this.Publishers;
     this.cache.Sites = this.Sites;
 
 }
 
 public void CancelEdit()
 {
     this.EmployeeID = this.cache.EmployeeID;
     this.Gender = this.cache.Gender;
     this.Issues = this.cache.Issues;
     this.LastEdit = this.cache.LastEdit;
     this.Name = this.cache.Name;
     this.Origin = this.cache.Origin;
     this.Publishers = this.cache.Publishers;
     this.Publishers = this.cache.Publishers;
     this.Sites = this.cache.Sites;
     this.cache = null;
 }
 
 public void EndEdit()
 {
     this.cache = null;
 }

Then I just raise my property change notifications

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propName)
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

Next, we need a whole bunch of these SuperEmployees… For this demo, I just create them from in memory objects. You’d need to think about how you are going to get your data.. WCF services?  Astroria? Generic REST?  RIA Services of course ;-)  Anyway, here is what that looks like in the demo:

public class SuperEmployeeList
{
    List<SuperEmployee> list = new List<SuperEmployee>()
    {       
         
        new SuperEmployee() {
        Gender="Male",
        Issues=982,
        Name = "Alfred",
        Origin="Human",
        Publishers="DC",
        Sites="first appears in Batman #16"},
                
 
        new SuperEmployee() {
        
        Gender="Male",
        Issues=518,
        Name = "Alfred E. Neuman",
        Origin="Human",
        Publishers="Ec",
        Sites="first appears in MAD #21"},
  

Then I just add a few methods to help me access the this data from the UI:

public string OrginFilter { get; set; }
public IEnumerable<SuperEmployee> GetEmployees()
{
    if (OrginFilter != null && OrginFilter != String.Empty)
    {
        return list.Where(emp=>emp.Origin.Contains(OrginFilter)).ToArray();
    }
    else return list.ToArray();
}

The UI looks pretty much like we have seen it before, but I set the data source in code to make it a little more clear..

SuperEmployeeList Context = new SuperEmployeeList();
  
public Home()
{
    InitializeComponent();
    LoadData();
}
 
void LoadData()
{
    PagedCollectionView pcv = new PagedCollectionView(Context.GetEmployees());
 
    dataGrid1.ItemsSource = pcv;
    pager1.Source = pcv;
 
    originFilterBox.ItemsSource = Context.GetOrigins();
 
}

Those are really the highlights..  Here is what you end up getting.. exactly what we have seen before! Validation works, sorting, filtering, etc.  All client side. 

image