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
- 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.
- Create folder in Server application, with name Model.
- 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
- Add a new ADO.NET Entity Data Model item to the model folder in the
server project. Name it as EmployeeModel. Click on Add
- Select Generate from database and click on Next.
- 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".
- 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.
- 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(value, false);
ReportPropertyChanged("FirstName");
OnFirstNameChanged();
}
}
private global::System.String _FirstName;
partial void OnFirstNameChanging(global::System.String value);
partial void OnFirstNameChanged();
- EntityKeyProperty --> tell that it is autogenerated or
not.
- IsNullable --> tell us that is allows null in the DB or Not.
- DataMemberAttribute --> Each is field is decorated and mapped to DB
table field.
- 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
- 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.
- Build the Solution (F6).
- 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
- Build the solution
- 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);
}
}
- 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.
- GetEmployees() --> is of type IQueryable<Employee> which allows to
apply LINQ queries in the client.
- Also we can see the methods for CRUD.
D. Create View Model
- Create ViewModel folder in Client Project. And then add
EmployeeViewModel.cs class.
- Add reference System.ServiceModel.DomainServices.Client dll.
- 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
- 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 (this, new PropertyChangedEventArgs (propertyName));
}
}
- 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());
}
- 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;
}
}
-
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.
- Create a folder 'View' in the Client Application and add a new
Silverlight User Control to It. Name it as EmployeeGrid.xaml
- 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>
- 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>
- 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>
- Coming back to the MainPage.xaml, Add the following
code to the MainPage.xaml.cs.
employeeGrid.DataContext =new MVVM_RIA_ICOMMAND_CUSTOMVALIDATION.ViewModel.EmployeeViewModel();
- That's it, Build and run your application. I know
that you will get many questions now. Let us some of them.
Question
- 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
{ get; set;
}
[Display(Order = 2)]
public string FirstName
{ get; set;
}
public DateTime HireDate
{ get; set;
}
[Display(Order=0)]
public int ID
{ get; set;
}
[Display(Order = 1)]
public string LastName
{ get; set;
}
[Display(Order = 4)]
public string Phone
{ get; set;
}
[Display(Order = 5)]
public string SSN
{ get; set;
}
[Display(Order = 6)]
public string URL
{ get; set;
}
}
}
-
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 ;}
-
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.
- 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();
}
- 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.
- Declare a public property, InsertCommand, to
the EmployeeViewModel.cs that return EmplyeeInsertCommand
public ICommand InsertCommand
{
get
{
return new InsertCommand(this);
}
}
- 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>
- How do we save the newly add row to the Grid?
- In EmployeeViewMode class add a method called Save
public void Save()
{
if (_employeeDomainContext.HasChanges)
{
_employeeDomainContext.SubmitChanges();
RaisePropertyChanged("Employees");
}
}
- 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();
}
- 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);
}
}
- 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>
- Build and Run the application. Take a
chance to add and save the new row.
- 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
- 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
{ get; set;
}
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