Silverlight and RIA Services: Binding to a DataForm
Posted
Wed, Nov 25 2009 14:24
by
Deborah Kurata
When building line of business (LOB) applications, a common feature is create, review, update, and delete (CRUD) operations on the data in the application. An easy way to provide this feature is using the Silverlight DataForm.
[For an introduction to Silverlight and RIA Services, start here.]
NOTE: The example used here is a continuation of the example in this prior post.
Updating the Domain Service Class
The first step in building a DataForm is to ensure that your Domain Service classes support create, update, and delete operations. The Domain Service class for this example is a CustomerService and resides in the Web project created when the Silverlight project was created.
NOTE: If you don't add these methods, you will get exceptions when trying to work with the DataGrid such as "Unhandled Error in Silverlight Application Code: 4004" with a message "Editing items is not supported by the IEditableCollection."
In C#:
namespace SLCSharp.Web
{
using BoCSharp;
using System.Collections.Generic;
using System.Web.Ria;
using System.Web.DomainServices;
[EnableClientAccess()]
public class CustomerService : DomainService
{
public IEnumerable<Customer> GetCustomers()
{
return Customers.Retrieve();
}
public void InsertCustomer(Customer currentPerson)
{
}
public void UpdateCustomer(Customer currentPerson)
{
}
public void DeleteCustomer(Customer currentPerson)
{
}
}
}
In VB:
Imports System.Web.DomainServices
Imports System.Web.Ria
<EnableClientAccess()> _
Public Class CustomerService
Inherits DomainService
Public Function GetCustomers() As IEnumerable(Of Customer)
Return Customers.Retrieve()
End Function
Public Sub InsertCustomer(ByVal currentPerson As Customer)
End Sub
Public Sub UpdateCustomer(ByVal currentPerson As Customer)
End Sub
Public Sub DeleteCustomer(ByVal currentPerson As Customer)
End Sub
End Class
Notice that the Insert, Update, and Delete operations don't contain any code. The code for these will be provided in a later post. In this post, the focus is on binding to the DataForm and trying out the DataForm features.
Having the methods in place, even though they are empty, allow the in-memory data to be updated and retained. This allows you to fully try out the user interface. But since there is no code within these methods, no changes are retained in the database.
Building the XAML
The XAML for building the DataForm requires two steps:
1) Defining the DomainDataSource for the data.
2) Defining the DataForm.
In XAML:
<UserControl x:Class="SLVB.CustomerSummaryUC"
xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
xmlns:domain="clr-namespace:SLVB.Web">
<Grid x:Name="LayoutRoot" Background="BlanchedAlmond">
<riaControls:DomainDataSource x:Name="CustomerSource"
QueryName="GetCustomers" AutoLoad="True">
<riaControls:DomainDataSource.DomainContext>
<domain:CustomerContext/>
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
<StackPanel>
<dataFormToolkit:DataForm
ItemsSource=
"{Binding Data, ElementName=CustomerSource}">
</dataFormToolkit:DataForm>
</StackPanel>
</Grid>
</UserControl>
Using the DomainDataSource requires adding two namespaces and the DataForm requires adding a third namespace to the UserControl:
1) xmlns:riaControls is needed for the RIA DomainDataSource control.
NOTE: You won't find the DomainDataSource control in the toolbox. You need to type it into the XAML manually.
2) xmlns:domain is the namespace for your Web project that launches your Silverlight application.
3) xmlns:dataFormToolkit contains the DataForm control.
Define a name for the DomainDataSource using the x:Name property. This is the name used in the binding. Also set the QueryName property to the name of the Domain Service class method that gets the data for this data source. In this case, it is the GetCustomers method.
The DomainDataSource also requires a DomainContext. This is where you define the name of the data context used by the data source. If you don't see an appropriate context using intellisense, try rebuilding your application. Otherwise you can type in the name. It will be the same name as your Domain Service class but replacing "Service" with "Context". Since this example Domain Service class is CustomerService, the context is CustomerContext.
The next set of XAML builds a DataForm inside a StackPanel. The ItemsSource property of the DataForm defines the binding to the name of the DomainDataSource.
The result is as follows:
That could look a little nicer. It would be easier to use if the label names were correct words, if there was some help text, and the fields were in a more natural order. And does the user really need to see the CustomerId?
Updating the Business Objects
There are several ways to adjust the layout of the DataForm. The most unique technique is to update the business objects themselves. The idea behind this technique is that as the business objects change over time, they can ensure that the associated UI also changes over time. Regardless of whether you agree with this idea, here's how you do it.
Simply add attributes to the associated properties.
In C#:
using System.ComponentModel.DataAnnotations;
namespace BoCSharp
{
public class Customer
{
[Display(AutoGenerateField=false)]
[Key()]
public int CustomerId { get; set; }
[Display(Name="Customer Type",
Description="Select the type of customer",
Order=3)]
public int CustomerTypeId { get; set; }
[Display(Name = "First Name",
Description = "Enter the customer's first name",
Order = 1)]
public string FirstName { get; set; }
[Display(Name = "Last Name",
Description = "Enter the customer's last name",
Order = 2)]
public string LastName { get; set; }
[Display(Name = "Email",
Description = "Enter the customer's primary email address",
Order = 4)]
public string EmailAddress { get; set; }
public Customer()
{
}
}
}
In VB:
Imports System.ComponentModel.DataAnnotations
Public Class Customer
Private _CustomerId As Integer
<Display(AutoGenerateField:=False)> _
<Key()> _
Public Property CustomerId() As Integer
Get
Return _CustomerId
End Get
Set(ByVal value As Integer)
_CustomerId = value
End Set
End Property
Private _CustomerTypeId As Integer
<Display(Name:="Customer Type", _
Description:="Select the type of customer", _
Order:=3)> _
Public Property CustomerTypeId() As Integer
Get
Return _CustomerTypeId
End Get
Set(ByVal value As Integer)
_CustomerTypeId = value
End Set
End Property
Private _FirstName As String
<Display(Name:="First Name", _
Description:="Enter the customer's first name", _
Order:=1)> _
Public Property FirstName() As String
Get
Return _FirstName
End Get
Set(ByVal value As String)
_FirstName = value
End Set
End Property
Private _LastName As String
<Display(Name:="Last Name", _
Description:="Enter the customer's last name", _
Order:=2)> _
Public Property LastName() As String
Get
Return _LastName
End Get
Set(ByVal value As String)
_LastName = value
End Set
End Property
Private _EmailAddress As String
<Display(Name:="Email", _
Description:="Enter the customer's primary email address", _
Order:=4)> _
Public Property EmailAddress() As String
Get
Return _EmailAddress
End Get
Set(ByVal value As String)
_EmailAddress = value
End Set
End Property
End Class
Notice that each property now has a Display attribute. The key named parameters within this attribute are:
- AutoGenerateField: Set to false, this will hide the field so it does not appear on the data form. If not set, the value is true.
- Description: This is the text that will appear as a tooltip (see screen shot below). If not set, no tooltip appears.
- Name: This is the name that will appear as the label for the field. If not set, the field name is used.
- Order: This defines the order of the fields on the data form.
The result is as follows:
MUCH nicer!
NOTE: I also added a Margin ="5,5,200,5" to the StackPanel to ensure there was space to the right to see the information tooltip. No other changes were made to the XAML.
NOTE: When I made the changes to the business object and ran the application, the DataForm layout did not change. I recompiled the application and ran again before I saw the changes.
The only thing left to do is change the Customer Type text field to a ComboBox. But amazing, that is a significant amount of work. Better left for a future post.
Enjoy!