Silverlight 4.0 application with MVVM Basics and Data Manipulation


Subject

How do we create Silverlight application that follows MVVM Web Application Pattern and Performing CRUD operations in Silverlight Data Grid?
 
What we will be using  
 
We will be using the ADO.NET Entity and Domain Services for creating Model classes for Employee, Thereby creating the Custom Validation class for validating the class, which will be *.shared.cs. To implement Button operations we will be using ICommand interface to implement the CRUD command operations and thereby for binding with UX.
 
A. Creating a Silverlight solution with a RIA Services Link between the Projects
  1. Create Silverlight Application Project, enter project as MVVM_RIA_ICOMMAND_CUSTOMVALIDATION and solution name as MVVMRIAICOMMANDCUSTOMVALIDATION. Make sure that Enable RIA Service Link check box is selected.
  2. Create folder in Server application, with name Model.
  3. Before actually we start working with our example we will create a sample Data base and a table. Data base name will be MVVMDomainData and Table name is Employee. I am using SQL Server 2008 Express. Now we have environment ready for the development.
B. Creating a ADO.NET Entity Model for Employee table             
  1. Add a new ADO.NET Entity Data Model item to the model folder in the server project. Name it as EmployeeModel. Click on Add
  2. Select Generate from database and click on Next.
  3. Add new connection, as we create in ADO.NET. Select the database that we created. And click on Next. Select the check box "save entity connection string in We.Config as".
  4. The Dialog box comes up with all the Tables, View and SP that we have created in our DB, MVVMDomainData. Select the table that we created, Employee. And click on Finish.
  5. Let us look at the class that generated for us. This has Context and Entities. Let us see the Entities where we have to understand what exactly it is. The class inherits from the EntityObject. EntityObject class inherits from the StructuralObject. StructuralObject implements INotifyPropertyChanging, INotifyPropertyChanged interfaces. Out of all, we need to understand purpose of the following methods. These methods will track the change make to the data and in UX and keep update the Context object so that the changes reflects in all the places where the context object is being shared.

    protected virtual void OnPropertyChanged(string property);
    protected virtual void OnPropertyChanging(string property);
    protected virtual void ReportPropertyChanged(string property);
    protected virtual void ReportPropertyChanging(string property);

    Going back to the generated entityonject which will have a Factory method that can used as to create new entity object and Properties for the each filed in the data base.

    /// <summary>
    /// No Metadata Documentation available.
    /// </summary>
    [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
    [DataMemberAttribute()]
    public global::System.String FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            OnFirstNameChanging(value);
            ReportPropertyChanging("FirstName");
            _FirstName = StructuralObject.SetValidValue(valuefalse);
            ReportPropertyChanged("FirstName");
            OnFirstNameChanged();
        }
    }
    private global::System.String _FirstName;
    partial void OnFirstNameChanging(global::System.String value);
    partial void OnFirstNameChanged(); 
  1. EntityKeyProperty --> tell that it is autogenerated or not.
  2. IsNullable --> tell us that is allows null in the DB or Not.
  3. DataMemberAttribute --> Each is field is decorated and mapped to DB table field.
  4. ReportPropertyChanged("FirstName"); OnFirstNameChanged(); ReportPropertyChanged("FirstName"); OnFirstNameChanged(); are used for tracking the changes happening to the field value.
C. Creating Domain Service class of the Model
  1. Right on the Model folder and click on add new item. Select the Visual C# on the left side and select Domain Service class on the left side. Name the class as EmployeeDomain.cs, and click add. 
  2. Build the Solution (F6).
  3. The Model that we created will automatically picked up by the Visual Studio. Show all the tables that we have selected during the model creation. Select the Entities and Enable Editing Check boxes. By selecting Enable editing check box will generated the methods required for Delete, Update and Insert. Also select Generate associated class for metadata, which we will see later part of the example. And click On
  4. Build the solution
  5. Let us see the generated domain class. 

    [EnableClientAccess()]
    public
     class EmployeeDomain : LinqToEntitiesDomainService<MVVMDomainDataEntities>
    {
        public IQueryable<Employee> GetEmployees()
        {
            return this.ObjectContext.Employees;
        }
        public void InsertEmployee(Employee employee)
        {
            if ((employee.EntityState != EntityState.Detached))
            {
            this.ObjectContext.ObjectStateManager.ChangeObjectState(employee, EntityState.Added);
            }
            else
            {
                this.ObjectContext.Employees.AddObject(employee);
            }
        }
        public void UpdateEmployee(Employee currentEmployee)
        {
            this.ObjectContext.Employees.AttachAsModified(currentEmployee,this.ChangeSet.GetOriginal(currentEmployee));
        }
        public void DeleteEmployee(Employee employee)
        {
            if ((employee.EntityState == EntityState.Detached))
            {
                this.ObjectContext.Employees.Attach(employee);
            }
            this.ObjectContext.Employees.DeleteObject(employee);
        }

  1. EnableClientAccess --> Marks Domain Service class as accessible to clients. We must apply theEnableClientAccessAttribute attribute to a domain service to make it accessible to the client application. A DomainContext class is generated for each domain service that is marked with theEnableClientAccessAttribute attribute. When you create a domain service by using the Add New Domain Service Class wizard, the wizard contains a check box called Enable client access that is selected by default. The EnableClientAccessAttribute is applied to the domain service when this check box is selected.
  2. GetEmployees() --> is of type IQueryable<Employee> which allows to apply LINQ queries in the client.
  3. Also we can see the methods for CRUD.
D. Create View Model
  1. Create ViewModel folder in Client Project. And then add EmployeeViewModel.cs class.
  2. Add reference System.ServiceModel.DomainServices.Client dll.
  3. Add following using statements 

    using System.ComponentModel; --> we will be sing INotifyPropertyChanged interface
    using System.ServiceModel.DomainServices.Client;
    using MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.Web.Model; -- > Through RIA services the link will be available to Mode in the Server project

     
  4. Implement the INotifyPropertyChanged interface in the view model class. With that we will have following code in the Employee View Model class 

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged (string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged (thisnew PropertyChangedEventArgs (propertyName));
        }
    }
     

  5. Declare a class scope variable for EmployeeDomain, is located in the Generated_Code folder. This is hidden folder in the client project. EmployeeDomain class provides access to its instance members that are generated in the EmployeeDomainService class. 

    private EmployeeDomain _employeeDomainContext =  new EmployeeDomain();
    public EmployeeViewModel()
    {
        _employeeDomainContext.Load<Employee>(_employeeDomainContext.GetEmployeesQuery());
    }
     

  6. What Next? remember anything that we need to bind to the View from View Model should be a property in the corresponding View Model class. So, first thing we need to bind is all employees to the grid. So this, Employees, property will the data context for the view. 

    public EntitySet<Employee> Employees
    {
        get
        {
            return _employeeDomainContext.Employees;
        }
    }
     

  7. Provide a property for the Selected Employee Recode in the View Model so that, this will help in pointing to the selected in the Grid.

    private Employee _employee = null;
    public Employee SelectedEmployee
    {
         get
         {
              return _employee;
         }
         set
         {
             _employee = value;
             RaisePropertyChanged("SelectedEmployee");
         }
E. Create View
 
With the all the above details let us create a view and see the code is status. What we will do is we will create a View folder with Employee DataGrid added to it. And then add this to the main page.
  1. Create a folder 'View' in the Client Application and add a new Silverlight User Control to It. Name it as EmployeeGrid.xaml
  2. Add Referance of the View Mode to the User Controls and then add the DataGrid to the root Grid. 

    <UserControl x:Class="MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.View.EmployeeGrid"
        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:ViewModel="clr-namespace:MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.ViewModel"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400"xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
        <
    Grid x:Name="LayoutRoot" Background="White">
           <
    sdk:DataGrid Name="employeeGridView"/>
        </Grid>
    </
    UserControl>
     

  3. Go to main page and add View Reference and then add the Emplyee Grid view user control that we created. 

    <UserControl x:Class="MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.MainPage"
        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:Views="clr-namespace:MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.View"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400">
        <
    Grid x:Name="LayoutRoot" Background="White">
            <
    Views:EmployeeGrid x:Name="employeeGrid" />
        </
    Grid>
    </
    UserControl>

  4. Now we will bind to the Employee Grid with the Employee View Model. Bind the Property "Employees" that we created in Employee View Model, in the View using ItemSource.

    <UserControl x:Class="MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.View.EmployeeGrid"
        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:ViewModel="clr-namespace:MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.ViewModel"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400"xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
        <
    Grid x:Name="LayoutRoot" Background="White">
            <
    sdk:DataGrid Name="employeeGridView" ItemsSource="{Binding Employees}"/>
        </
    Grid>
    </
    UserControl>

  5. Coming back to the MainPage.xaml, Add the following code to the MainPage.xaml.cs. 

    employeeGrid.DataContext =new MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.ViewModel.EmployeeViewModel();
     

  6. That's it, Build and run your application. I know that you will get many questions now. Let us some of them.
Question 
  1. How do we get the fields displayed in a specific order?

    The Answer is "EmployeeDomain.metadata.cs" which is generated along with the Domain Service class.

    Set the [[Display(Order=0)] attribute to the fields. So, with this our EmployeeDomain.metadata.cs class will become as follows. Build and Run the application. 

    public partial class Employee
    {
        internal sealed class EmployeeMetadata
        {
            private EmployeeMetadata()
            {
            }
            [Display(Order = 3)]
            
    public string Email { getset; }
            [Display(Order = 2)]
            
    public string FirstName { getset; }
            
    public DateTime HireDate { getset; }
            [Display(Order=0)]
            
    public int ID { getset; }
            [Display(Order = 1)]
            
    public string LastName { getset; }
            [Display(Order = 4)]
            
    public string Phone { getset; }
            [Display(Order = 5)]
            
    public string SSN { getset; }
            [Display(Order = 6)]
            
    public string URL { getset; }
        }
    }
     
     
  2. How do make the ID filed non-editable, since it is Auto generated primary key.

    Answer is The Answer is "EmployeeDomain.metadata.cs" which is generated along with the Domain Service class.  Set the [Editable(false)] attribute to any field that is non-editable. Then double click on the ID column at run time it will not allow you edit it.

    [Editable (false)]
    [Display (Order=0)]
    public int ID {get; set ;} 
     
  3. How do we Add new Row it the Grid?

    When we want to add new Row to the Grid it is nothing but adding a new employee record to our EmployeeDomainContext. Now what we need to do is Add a new button, called 'Add New' to the Employee Grid user control.

    On click of the button we need to call come property in the Employee View Model to create a new Employee object and then add to the EmployeeDomainContext object. And then somehow we need to tell the UX that EmployeeDomainContext has been changed and the new added empty now should appear.

    All this is taken care by the ICommand and INotifyPropertyChanged interfaces. Follow the steps. 
     
    1. Add class file called 'EmplyeeInsertCommand' to ViewModel and Implement the ICommand Interface; ICommand is available in System.Windows.Input namespace. Add the following code to it.
       
      private EmployeeViewModel _employeeViewModel;
      public EmployeeViewModel (EmployeeViewModel employeeViewModel)
      {
          this._employeeViewModel = employeeViewModel;
      }
      public bool CanExecute(object parameter)
      {
          return true;
      }
      public event EventHandler CanExecuteChanged;
      public void Execute(object parameter)
      {
          _employeeViewModel.Insert();
      }
    2. We need to Add the following code to the 'Insert' method in the EmployeeViewModel.cs
       
      public void Insert()
      {
          Employee employee = new Employee();
          //employee.ID = 0;
          _employeeDomainContext.Employees.Add(employee);
          RaisePropertyChanged(ed("Employees");
      }

      What happens here is when we add new Employee object to the EmployeeDomainContext, RaisePropertyChanged even will be fired and saying whichever element is binded to the Employees property needs to updated. And will be updated.

    3. Declare a public property, InsertCommand, to the EmployeeViewModel.cs that return EmplyeeInsertCommand 
       
      public ICommand InsertCommand
      {
          get
          {
             return new InsertCommand(this);
          }
      }
    4. Bind this 'InsertCommand' property to the 'Add New'. Build the application and Run. Also make sure that DataGrid SelectedItem is binded to SelectedEmployee
       
      <UserControlx:Class="MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.View.EmployeeGrid"
          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:ViewModel="clr-namespace:MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.ViewModel"
          mc:Ignorable="d"
          d:DesignHeight="300" d:DesignWidth="400"xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
          <Grid x:Name="LayoutRoot" Background="White" Grid.Row="0">
              <sdk:DataGrid Name="employeeGridView" ItemsSource="{BindingEmployees}"
                            SelectedItem="{Binding SelectedEmployee,Mode=TwoWay}" />
                         <Button Content="Insert" Name="btnInsert" Grid.Row="1"
                          Command="{Binding InsertCommand}" Margin="12,7,247,164" Width="140"/>
          </Grid>
      </UserControl>

       
  4. How do we save the newly add row to the Grid?
     
    1. In EmployeeViewMode class add a method called Save 
       

      public void Save()

      {

          if (_employeeDomainContext.HasChanges)

          {

              _employeeDomainContext.SubmitChanges();

              RaisePropertyChanged("Employees");

          }

      }

    2. Add new class item to ViewModel folder with the EmployeeUpdateCommand. Add the following code. Similar how we did it for EmplyeeInsertCommand. 
       
      private EmployeeViewModel _employeeViewModel;
      public EmployeeUpdateCommand(EmployeeViewModel employeeViewModel)
      {
          this._employeeViewModel = employeeViewModel;
      }
      public bool CanExecute(object parameter)
      {
          return true;
      }
      public event EventHandler CanExecuteChanged;
      public void Execute(object parameter)
      {
          _employeeViewModel.Save();
    3. Add new property called SameCommand to the EmployeeViewModel class. This will bind to the Save button in the Employee Grid View. 
       
      public ICommand SaveCommand
      {
          get
          {
              return new EmployeeUpdateCommand (this);
          }
      }
    4. Add a Save button to Employee Grid View and bind SaveCommand property to it. 
       
      <Grid x:Name="LayoutRoot" Background="White">
              <Grid.RowDefinitions >
                  <RowDefinition />
                  <RowDefinition />
              </Grid.RowDefinitions>
      <sdk:DataGrid Name="employeeGridView" ItemsSource="{BindingEmployees}" Grid.Row="0"/>
                <Button Content="Add new" Name="btnInsert" Grid.Row="1"
      Command="{Binding InsertCommand}" Margin="46,60,214,60" Width="140"  Height="30"  />
              <Button Content="Save" Name="btnSave" Grid.Row="1"
      Command="{Binding SaveCommand}" Margin="225,60,35,60" Width="140"Height="30"/>
      </Grid
    5. Build and Run the application. Take a chance to add and save the new row.
       
  5. How to validate the data when we add new row?

    By default when we bind to the DataGrid, all the SQL Table field attributes are applied to the grid. But Still if you want to enforce the same. Add the following attribute in the EmployeeDomain.metadata.cs class wherever relevant.  

    • DataTypeAttribute
    • RangeAttribute
    • RegularExpressionAttribute
    • RequiredAttribute
    • StringLengthAttribute
       
  6. How do we add custom validations to validate the data? Like SSN, Phone and email validation. 

    The answer is by using RegularExpressionAttribute to the fields in theEmployeeDomain.metadata.cs class

    For example, SSN format: Valid format is XXX-XX-XXXX.
     
    [RegularExpression(@"^\d{3}-\d{2}-\d{4}$", ErrorMessage = "Valid SSN format is 123-12-1234")]
    [Display(Order = 5)]
    public string SSN { getset; } 
Similarly we can apply any validation format. Run the application edit and click on Save. Check you DB table.
 
Now you can try with the Delete Command


Similar Articles