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:

image

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:

image

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!

Comments

# Silverlight and RIA: Adding a ComboBox to a DataForm

Wednesday, November 25, 2009 5:44 PM by Deborah's Developer MindScape

Building a DataForm is quick and easy and is detailed in this prior post . Even customizing it is a breeze

# re: Silverlight and RIA Services: Binding to a DataForm

Wednesday, November 25, 2009 6:04 PM by Robin McNeil

I am just starting to work with Silverlight and I have found your website VERY helpful. Thanks for the efforts you are making.

As a developer of business apps., I frequently have the situation where data input through a form requires complex validation, with inter-relationships among the fields on the form. Normally, I would put this logic in the middle tier of my application and validate the resulting object before saving it to the database. If there is a problem with the validation, I would send the appropriate information to the view, perhaps by raising an exception.

Do you have any thoughts about how this would be handled in a Silverlight app.? I guess that I could share the validation logic between my middle tier and the Silverlight app., but is there another / better way?

# re: Silverlight and RIA Services: Binding to a DataForm

Monday, November 30, 2009 5:49 PM by Hemant

The Data Annotations showed up after I aligned them on one line, otherwise when I just pasted the Data Annotations, the Data Form was ignoring them.

# Social comments and analytics for this post

Sunday, December 06, 2009 10:54 AM by uberVU - social comments

This post was mentioned on Twitter by LKWave: Silverlight and RIA Services: Binding to a DataForm - Deborah's ... http://bit.ly/6Fagkb #SL #RIA

# re: Silverlight and RIA Services: Binding to a DataForm

Friday, April 23, 2010 6:40 PM by Scott Leckie

Good post - thanks. Regarding the Error 4004, I think the answer to this is to handle e.HasError in {domaincontext}_SubmittedChanges. Please see www.scottleckie.com/.../code-4004-unhandled-error-in-silverlight-application for more information. Cheers Scott

# re: Silverlight and RIA Services: Binding to a DataForm

Tuesday, February 08, 2011 6:52 PM by Dale

I've found your posts to be quite useful and have just started using RIA services.  Perhaps you can help me with something I am hung up on.  I want to put a combobox that gets data from table 1 above a dataform that enables adding a new record to table 2.  I need the Customer name and ID from Table 1 to populate the CustomerID in table 2.  A new Salesman (Table 2) can't be added to a customer that doesn't exist...  My question is - How do I bind the Table 1 ID and name to the combobox?  Once I get past that I'll have to figure out how to get the ID in the TextBox in the dataform.        

# re: Silverlight and RIA Services: Binding to a DataForm

Thursday, March 31, 2011 8:32 AM by Santosh

I am trying to add a Combo box in different data forms dymanically using the custom dataform using the custom Annotations.

i am not able to set the Itemsource for those Combo box.

Can any one help me to solve this issues.

# re: Silverlight and RIA Services: Binding to a DataForm

Wednesday, July 20, 2011 3:00 PM by Jay

Where can I find teh later post...I am having issues with the Code that is needed in the InsertCustomer Sub in teh DomainService.vb page.

Thanks

# re: Silverlight and RIA Services: Binding to a DataForm

Monday, July 25, 2011 10:11 AM by Deborah Kurata

Hi Jay -

You can download the example with the insert feature from here:

www.insteptech.com/.../AcmeCustomerManagementRiaVB.zip

Hope this helps.

# re: Silverlight and RIA Services: Binding to a DataForm

Wednesday, December 14, 2011 5:12 AM by hari

I placed Check box inside ListBox inside the Dataform. I have 3 classes(Class1, Class2,Class3). If i need to add some more Classes, i can add by clicking the + icon on the Dataform. I have 10 Items(Item1, Item2, Item3... Item10 as checkboxes in ListBox). If I select the Item2 and Item3 for Class1, the remaining 8 Items should only avail for Class2. How I could I do in MVVM.?

Pls Anyone give solution for this...

Thanks in Advance...

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above: