Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

Archive for ASP.NET RIA Services

October 25, 2013

Who Moved My Cheese: RIA Services

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 9:22 am

WCF RIA Services was (past tense) an awesome technology that allowed developers to quickly and super easily build domain classes and WCF services for Silverlight applications.

In VS 2012 (and VS 2010 SP1), you could use Add | New Item and get a template for a WCF RIA Domain Service Class.

image

(If you are still using VS 2012 or VS 2010 SP1+ with RIA Services, see this set of blog posts for samples and tips.)

In VS 2013, attempting to find the Domain Service Class template gives you this:

image

As of July 3, 2013, WCF RIA Services was open sourced and renamed Open RIA Services. The project lead is my friend, Colin Blair, whose name you know if you have ever had a question about WCF RIA Services.

To ensure your Silverlight/WCF RIA Services applications still run in Visual Studio 2013, Microsoft included WCF RIA Services V1.0 "in the box". This provides the compile-time domain class and service code generation as well as the runtime functionality that makes the domain service work. So you don’t have to do anything extra to run or debug your existing VS 2012 Silverlight applications in Visual Studio 2013.

However, the following features are gone:

  • The Business Application project template
  • The Domain Service Class template and associated wizard
  • Toolbox items for the Silverlight designer surface
  • Data Sources window integration for the Silverlight designer surface

See this post for more information on the above changes.

Microsoft has published several NuGet packages for support of Entity Framework 5, Windows Azure, and the SOAP and JSON endpoints in WCF RIA Services. See this post for more information on the NuGet packages.

To follow the Open RIA Services project, see this blog. (Click on "Navigation" to open the menu and navigate the blog.)

Enjoy!

EDIT: Clarified that the VS 2013 RIA Services features don’t require changes when opening VS 2012 Silverlight projects.

January 21, 2013

Tips for Building a BO for Silverlight and RIA

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 2:13 pm

The following are some tips when building a business object class for use by Silverlight and RIA services.

NOTE: This assumes you are not using Entity Framework (EF).

Use Class-Level Validation

Specifying class-level validation allows you to perform validation on the entire class. This is a good location for cross-field validation.

For example, say you are creating a Discount class that defines discounts available to your customers. A percentage discount must be less than 100% where a dollar discount can be any amount. In the class-level validation, you can check the discount type against the amount to determine if the fields are valid.

Use the CustomValidation attribute to define class-level validation:

In C#:

[CustomValidation(typeof(DiscountValidation), "ValidateDiscount")]
public class Discount

In VB:

<CustomValidation(GetType(DiscountValidation), "ValidateDiscount")>
Public Class Discount

Define a Key

For WCF RIA Services to recognize your objects, they must have a unique key. In most business objects, this is easy because every object relates to a database row that already has a unique key.

In the above Discount example, the discount already has a unique DiscountId associated with it.

Use the Key attribute to define the key field.

In C#:

private int _DiscountId;
[Key()]
public int DiscountId {
    get { return _DiscountId; }
    set 
   
{
        if (!_DiscountId.Equals(value))
        {
            // Set the value
            _DiscountId = value;
        }
    }
}

In VB:

Private _DiscountId As Integer
<Key>
Public Property DiscountId() As Integer
    Get
        Return _DiscountId
    End Get
    Set(ByVal value As Integer)
        If Not _DiscountId.Equals(value) Then
            ‘ Set the value
            _DiscountId = value
        End If
    End Set
End Property

NOTE: If you ever have an issue whereby your WCF RIA Service is returning one row when you know you have many rows, chances are that the Key attribute is missing. Without this attribute, RIA views all rows as the same row and therefore only returns one.

Add a String Property for each Numeric Property

If you define a numeric property in your business object, such as an Integer or Decimal, and bind it in Silverlight, Silverlight will automatically convert any entered value to a number. If the user enters "one", it won’t generate a validation error. Silverlight instead converts it to a number. Any string converted to a number is 0.

This may not seem likely, but consider the possibility that the user enters 50%. Because of the "%" sign, Silverlight will convert the value to 0 and consider it a valid number.

To have full control over numeric entry, create a string version of any numeric property. Define a Regular Expression to validate that string as an integer or decimal value. And bind that string value to the Silverlight field.

In C#:

[RegularExpression("^([0-9]*\\,?[0-9]+|[0-9]+\\,?[0-9]*)?$",
errormessage = "Enter a dollar amount or percent value.")]
public string AmountString {
    get { return Amount.ToString("N2"); }
    set { decimal.TryParse(value, Amount); }
}

In VB:

<RegularExpression("^([0-9]*\,?[0-9]+|[0-9]+\,?[0-9]*)?$", errormessage:="Enter a dollar amount or percent value.")>
Public Property AmountString() As String
    Get
        Return Amount.ToString("N2")
    End Get
    Set(value As String)
        Decimal.TryParse(value, Amount)
    End Set
End Property

Add a Property for any Related Data

When updating data, RIA is much easier to work with if it manages related child data as a part of the parent data.

For example, say a discount is only applied to certain product types: downloadable software products allow one set of discounts, while sending CDs has a different set of available discounts. So a single discount has a set of products that it relates to.

To more easily edit this related data, create a property for the child data in the parent business object. Mark the relationship with the Association and Include attributes.

In C#:

[Include()]
[Association("Discount_Product", "DiscountId", "DiscountId")]
public List<Product> ProductList { get; set; }

In VB:

<Include>
<Association("Discount_Product", "DiscountId", "DiscountId")>

Public Property ProductList As List(Of Product)

Enjoy!

May 14, 2011

Tech Ed 2011: Source Code for My Talk

Filed under: ASP.NET RIA Services,General,Silverlight @ 2:00 pm

I am presenting "Microsoft Silverlight, WCF RIA Services, and Your Business Objects" at the Tech Ed 2011 conference in Atlanta on May 18, 2011.

You can get the code for my talk from my company’s Web site. Look for the Tech Ed 2011 entry.

If you are at Tech Ed, come by and say hi. I will be at the Silverlight booth on Monday night and on Wednesday afternoon.

Enjoy!

May 4, 2011

Silverlight/WCF RIA Validation: Metadata Class

Filed under: ASP.NET RIA Services,C#,Silverlight @ 1:05 am

Data annotation attributes are a great way to add single property validation to your Silverlight/WCF RIA application. But what if you don’t want to inject attributes all over your business classes? The purpose of a metadata class is to have a place for all of these attributes external from the business class.

NOTE: This post is part of a set of validation posts which starts here and it uses the sample code from this prior post.

If your Silverlight application uses WCF RIA services and your own business objects, then you can set attributes on your business object properties to define single-attribute validation.

In this prior post, the code example demonstrated how to use the single property validation attributes available to validate that a last name is entered and does not exceed 20 characters, the attributes look like this:

[Required(ErrorMessage = "Please enter a last or family name.")]
[StringLength(20, ErrorMessage = "Last name cannot exceed 20 characters.")]
public string LastName { get; set; }

If you don’t want these attributes in your business classes, you can instead create a metadata file and store all of the attributes there.

1) Right-click on your business object’s Class Library project and add a new class (Add | New Item | Class).

In this example, the class was called Student. So the metadata class is named StudentMetadata.cs.

2) In  this new class, add any of the business object properties and associated attributes.

Note that the property names defined in the metadata class must exactly match the property names in the original class.

In C#:

using System.ComponentModel.DataAnnotations;

namespace InStepValidationExample.BL
{
internal sealed class StudentMetadata
{

   /// <summary>
   /// Private to ensure this class is not instantiated.
   /// </summary>
   private StudentMetadata()
   {}

  [Required(ErrorMessage = "Please enter a last or family name.")]
  [StringLength(20, ErrorMessage = "Last name cannot exceed 20 characters.")]
  public string LastName { get; set; }
}
}

In VB:

TBD

3) Associate the metadata class with its original class using a MetadataType attribute on the original class definition. This attribute associates the original business object class with the metadata class.

In C#:

namespace InStepValidationExample.BL
{
    [MetadataType(typeof(StudentMetadata))]
    public class Student
    {
     …
    }
}

In VB:

TBD

When you run the application, the validation should work as before:

image

Use this technique any time you want attributes on your business object properties, but want to define these attributes external from the business object classes.

Enjoy!

May 3, 2011

Silverlight Validation: Resource File

Filed under: ASP.NET RIA Services,C#,Silverlight @ 2:04 am

Resource files are a great way to manage strings, especially if you need to support multiple languages. This post demonstrates how to use a resource file with the validation attributes used by Silverlight and WCF RIA Services.

NOTE: This post is part of a set of validation posts which starts here and it uses the sample code from this prior post.

In this prior post, the code example demonstrated how to use the single property validation attributes available in Silverlight and WCF RIA Services. For example, to validate that a first name is entered and does not exceed 20 characters, the attributes look like this:

[Required(ErrorMessage="Please enter a first name.")]
[StringLength(20, ErrorMessage = "First name cannot exceed 20 characters.")]
public string FirstName { get; set; }

Notice how the validation error messages are hard-coded strings. This poses a challenge if you need to localize your application.

A better option is to add the strings to a resource file. The trick here is that the resource file needs to exist both on the server (in the Class Library project if you are using WCF RIA with your own business layer) and on the client (in the Silverlight project). This is done by a process called linking.

1) In the Class Library project containing your business layer, add a resource file (Add | New Item | Resources File).

I added mine to a Resources folder in the project.

NOTE: If you are not using your own business layer on the server, you can add the resource file directly to the Web project instead.

2) Enter a name for each string along with the string values. Optionally add a comment.

3) Set the Access Modifier for each resource string to Public.

The result should look something like this:

image

4) In the Silverlight project, link to that resource file by selecting Add | Existing Item and then clicking on Add As Link in the Add Existing Item dialog.

image

5) In the Silverlight project, link also to the associated Designer.cs file.

NOTE: The Add Existing Item dialog allows multiple selection. So you could link both the rex file and the Designer/cs file in one step.

6) Modify the attribute parameters in the business layer Class Library project to reference the resource file instead of using hard-coded strings. Be sure to add a using for the resource file’s namespace:

using InStepValidationExample.BL.Resources;

[Required(ErrorMessageResourceType = typeof(ValidationErrorResources),
ErrorMessageResourceName = "FirstNameRequired")]
[StringLength(20,
    ErrorMessageResourceName = "FirstNameLength",
    ErrorMessageResourceType = typeof(ValidationErrorResources))]
public string FirstName { get; set; }

Instead of setting the ErrorMessage parameter to a hard-coded string, this code sets the ErrorMessageResourceType parameter to the type of the resource file (which is the resource file name). The ErrorMessageResourceName parameter specifies the name that you defined for the associated resource string in the resource file.

7) Now for the tricky part. By default, Visual Studio requires that the two resource files be in the same namespace. If they aren’t, the application won’t find the resources.

For example, if you use the Silverlight Business Application template, you can see that it puts  the resource file in the BizApp.Web project under a Resources folder. The resources are then in the BizApp.Web.Resources namespace.

To access the resources from the BizApp Silverlight project, the file is put in a folder hierarchy with a Web folder and then a Resources folder so the resulting namespace is again BizApp.Web.Resources.

image

In the example for this post, however, the resources are in a Class Library in a business layer separate from the Web project:

image

The namespace of the resources in the business layer is InStepValidationExample.BL.Resources. The namespace of the resources in the Silverlight project is InStepValidatoinExample.SL.Resources.

So if you run the application at this point, it will crash with a "TargetInvocationException occurred" message. If you look at the exception detail it says "Could not find any resources appropriate for the specified culture or the neutral culture…" Basically, it cannot find the resource file.

Luckily, we know the secret handshake required to get this to work:

7a) Right-click on the Silverlight project and select Unload Project.

7b) Right-click on the Silverlight project again and select to edit the project file.

7c) Add a <LogicalName> element to the <EmbeddedResource> element in the project file and set it to the namespace and resource name. Mine looked like this:

<ItemGroup>
  <EmbeddedResource Include="..\InStepValidationExample.BL\Resources\ValidationErrorResources.resx">
    <Link>Resources\ValidationErrorResources.resx</Link>
    <LogicalName>
  InStepValidationExample.BL.Resources.ValidationErrorResources.resources
     </LogicalName>

  </EmbeddedResource>
</ItemGroup>

7d) Close the project file.

7e) Right-click on the Silverlight project and select Reload Project.

Now when you run, it should find your resource file.

image

Use a resource file any time you want better management of your strings, especially if you plan to localize your application.

Enjoy!

May 1, 2011

Silverlight/WCF RIA Single Property Validation

Filed under: ASP.NET RIA Services,C#,Silverlight @ 9:01 pm

When you identify the validation scenarios for your application, you will find that some only require basic single property validation. Examples:

  • Ensure that a first name is entered.
  • Ensure that the first name is not longer than 20 characters
  • Ensure that the age is between 16 and 120.
  • Ensure that the email address is valid.

NOTE: This post is part of a set of validation posts which starts here and it uses the sample code from this prior post.

Each of these requirements can be handled by Silverlight’s single field validation using property attributes.

When you use WCF RIA Services and your own set of business objects, these attributes are set in the business layer on the business object properties. WCF RIA Services then automatically generates a copy of your business object properties with their attributes on the client for use with Silverlight.

Silverlight performs the validation when the user leaves the field AND before the data is submitted to the server. If there is a validation error, the data is not submitted to the server and a validation error appears.

Each of the simple validation attributes are discussed below.

Required Field Validation

Use the Required attribute to mark a property as required. For example, if the first name is required, the first name property is adorned as follows:

[Required()]
public string FirstName { get; set; }

If you make this change and run the sample application (from this prior post), the first name field is automatically validated. Clear the field and you will see this:

image

Silverlight’s built-in validation automatically kicks in and you see the default required validation error message.

NOTE: You can modify the Silverlight XAML to specify the ValidatesOnNotifyDataErrors property as part of the binding expression. However, this is not necessary. The default value of this property is true, so the validation errors are reported by default.

If you don’t like the error message, you can change it by setting one of the named parameters of the Required attribute:

[Required(ErrorMessage="Please enter a first name.")]
public string FirstName { get; set; }

The message then appears as shown below:

image

Use required field validation any time you want to ensure that the user enters a value into a Silverlight control.

String Length Validation

Often times the data entered on a page in Silverlight will get stored to a database where a maximum field size is defined. It is helpful for the users to be aware of this maximum before attempting to submit the data to the server.

For example, if the first name is set to a maximum of 20 characters in the database, you can set the StringLength attribute as follows:

[Required(ErrorMessage="Please enter a first name.")]
[StringLength(20, ErrorMessage = "First name cannot exceed 20 characters.")]
public string FirstName { get; set; }

Use string length validation any time you want to ensure that the user does not enter more data than can be stored in the database table or any other time that a string length cannot exceed a maximum value.

Range Validation

When entering numbers, often times the value must be limited to a specific range of values. For example, in this application the age must be between 16 and 120.

[Range(16, 120,
ErrorMessage="Please enter your age. You must be 16 or older.")]
public int Age { get; set; }

This results in the following:

image

NOTE: The Range attribute has overloads with parameters that are integers or doubles. To validate other types (such as decimal), you need to use the Range overload that takes a type as the first parameter and string values to convert to the defined type. For example:

[Range(typeof(Decimal),"0", "40000")]
public decimal TuitionPaid{ get; set; }

Use range validation any time you want to validate user entry to a range of numeric values.

Regular Expression Validation

Regular expression validation allows you to perform pattern matching on the entered value. For example, an email address must match a specific pattern with a local part of the address followed by an at sign (@) followed by a domain name. The Regular Expression attribute could look like this:

[Regular(@"^(?("")("".+?""@)|" +
      @"(([0-9a-zA-Z]((\.(?!\.))|" +
      @"[-!#\$%&’\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])@))" +
      @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|" +
      @"(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$",
      ErrorMessage="Please enter a valid email address")]

public string Email { get; set; }

NOTE: Replace "Regular" above with "RegularExpression". For whatever reason, the entire string was blocked in this post if the word "Expression" appeared above.

Other properties that could use a regular expression include phone numbers, social security numbers, and any other entry that must match a specific pattern.

You can find more information on regular expressions here.

Use regular expressions any time you want to validate a user’s entry based on pattern matching.

For more complex validation scenarios, see this prior post.

Enjoy!

EDIT 5/2/11: Corrected typographical errors. Corrected the regular expression, which did not paste into the post correctly.

Silverlight MVVM, WCF RIA Services Simple Sample Application

Filed under: ASP.NET RIA Services,C#,Silverlight @ 5:52 pm

This post provides a simple Silverlight MVVM, WCF RIA Services sample application that is used to demonstrate validation techniques. The sample application is provided in this separate post so it can be readily used with any of the presented validation techniques.

NOTE: This post defines a sample application that is used in the validation scenarios presented in this prior post.

The application is a simple data entry form for entering basic student information.

image

To create this application, follow these steps.

1) Build four projects:

  1. C# or VB Class Library project defining the business layer. (This is NOT a Silverlight class library.)
  2. C# or VB Silverlight Application project. (Be sure to check the Enable WCF RIA Services checkbox.)
  3. C# or VB ASP.NET Web project. (This project is automatically created when creating the Silverlight Application project).
  4. C# or VB Silverlight Class Library project. (For the library functions, such as commanding.)

2) In the first Class Library project (the one that is NOT a Silverlight class library), build a Student business object.

In C#:

using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;

namespace InStepValidationExample.BL
{
    public class Student
    {
        public int Age { get; set; }

        public string Email { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public DateTime? RegistrationDate { get; set; }

        [KeyAttribute]
        public int StudentId { get; set; }

        public DateTime? VaccinationDate { get; set; }

        public bool? VaccinationOptOut { get; set; }

        public static Student Retrieve(int id)
        {
            var student = new Student
            {
                StudentId = id,
                LastName = "Baggins",
                FirstName = "Bilbo",
                Age = 111,
                Email =
testemail@live.com,
                VaccinationOptOut = true,
                RegistrationDate = DateTime.Now
            };
            return student;
        }

        public bool Save()
        {
            // Code that saves the changes to the database;
            Debug.WriteLine("Saving: " + LastName + ", " + FirstName);
            return true;
        }
    }
}

In VB:

TBD

This code defines a set of simple properties for a student. The StudentId property is marked with the KeyAttribute, which defines a unique Id for the entity. This attribute is required for this entity to work with WCF RIA Services.

The Retrieve and Save methods in this example are "fake" methods. You would need to replace the code in these methods with code to retrieve and save the student from a data store such as a database. By using the fake code for this, you can try out the validation techniques  without needing to build or access a database.

3) In the ASP.NET Web application, build a Student Domain Service class.

In C#:


namespace InStepValidationExample.SL.Web
{
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;
    using InStepValidationExample.BL;

    [EnableClientAccess()]
    public class StudentService : DomainService
    {

        public Student GetStudentById(int id)
        {
            return Student.Retrieve(id);
        }

        public void UpdateStudent(Student student)
        {

        }

        protected override bool PersistChangeSet()
        {
            Student changedStudent;
            bool success = true;

            foreach (var item in ChangeSet.ChangeSetEntries)
            {
                if (item.Entity.GetType() == typeof(Student))
                {
                    changedStudent = (Student)item.Entity;
                    success = changedStudent.Save();
                    if (!success)
                        break;
                }
            }
            return success;
        }

    }
}

In VB:

TBD

This code provides the following methods:

  • GetStudentById to get the student from the business layer.
  • UpdateStudent method to enable updating of the student.
  • PersistChangeSet method to parse through the changeSet and save any changed students.

You can add Insert and Delete methods as desired.

4) Build an MVVM base class in the Silverlight project as demonstrated in this prior post.

5) Build a Commanding class in the Silverlight Class Library project as demonstrated in this prior post.

6) Build the ViewModel in the Silverlight Application project.

In C#:

using System;
using System.Linq;
using System.ServiceModel.DomainServices.Client;
using System.Windows.Input;
using InStepValidationExample.BL;
using InStepValidationExample.Library;
using InStepValidationExample.SL.Web;

namespace InStepValidationExample.SL.ViewModels
{
    public class StudentsViewModel : ViewModelBase
    {
        private StudentContext StudentContextInstance;

        private ICommand _CancelCommand;
        public ICommand CancelCommand
        {
            get
            {
                return _CancelCommand;
            }
        }

        private Student _CurrentStudent;
        public Student CurrentStudent
        {
            get
            {
                return _CurrentStudent;
            }
            set
            {
                if (_CurrentStudent != value)
                {
                    _CurrentStudent = value;
                    OnPropertyChanged("CurrentStudent");
                }
            }
        }

        private ICommand _SaveCommand;
        public ICommand SaveCommand
        {
            get
            {
                return _SaveCommand;
            }
        }

        public StudentsViewModel()
        {
            LoadData();
            DefineCommands();
        }

       private void DefineCommands()
       {
         _CancelCommand = new DelegateCommand<Object>
                         (OnCancelCommand);
         _SaveCommand = new DelegateCommand<Object>
                    (OnSaveCommand);
       }

        /// <summary>
        /// Loads the data for the application.
        /// </summary>
        private void LoadData()
        {
            if (IsInDesignModeStatic)
            {
                LoadDesignData();
            }
            else
            {
                // Load the student data asynchronously
                StudentContextInstance = new StudentContext();
                var loadop = StudentContextInstance.Load
                                          (StudentContextInstance.
                                                GetStudentByIdQuery(1),
                                                OnStudentLoaded, null);
            }
        }

        /// <summary>
        /// Loads temporary data for use in the designer.
        /// </summary>
        /// <remarks></remarks>
        private void LoadDesignData()
        {
            //Design mode data
            var student = new Student
            {
                LastName = "Baggins",
                FirstName = "Bilbo",
                Age = 111,
                Email = "testemail@live.com",
                VaccinationOptOut = true,
                RegistrationDate = DateTime.Now
            };

            CurrentStudent = student;
        }


        private void OnCancelCommand(object o)
        {
            StudentContextInstance.RejectChanges();
        }

        private void OnStudentLoaded(LoadOperation lo)
        {
            CurrentStudent =
                StudentContextInstance.Students.FirstOrDefault();
        }

        private void OnSaveCommand(object o)
        {
             StudentContextInstance.SubmitChanges(OnChangesSubmitted,
                                                      null);
        }

        private void OnChangesSubmitted(SubmitOperation so)
        {
             if (so.HasError)
             {
                  so.MarkErrorAsHandled();
             }
        }

    }
}

In VB:

TBD

This code defines two commands: Save and Cancel. It then loads design-time data or at run-time it calls the GetStudentById query to retrieve the student from the business layer.

The OnChangesSubmitted code ensures that any validation errors are correctly marked as handled and don’t cause an unhandled exception in your application (error 4004).

7) Build the View.

<UserControl x:Class="InStepValidationExample.SL.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"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" 
xmlns:sdk="
http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
xmlns:vms="clr-namespace:InStepValidationExample.SL.ViewModels">

    <UserControl.DataContext>
        <vms:StudentsViewModel />
    </UserControl.DataContext>
   
    <Grid x:Name="LayoutRoot"
          Margin="2">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid HorizontalAlignment="Center"
              VerticalAlignment="Top"
              DataContext="{Binding CurrentStudent}"
              Margin="10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <sdk:Label Content="First Name:"
                       Grid.Column="0"
                       Grid.Row="0"
                       HorizontalAlignment="Left"
                       Margin="3"
                       VerticalAlignment="Center" />
            <TextBox Grid.Column="1"
                     Grid.Row="0"
                     Height="23"
                     HorizontalAlignment="Left"
                     Margin="3"
                     Name="firstNameTextBox"
                     Text="{Binding Path=FirstName,
                            Mode=TwoWay,
                            TargetNullValue=”}"
                     VerticalAlignment="Center"
                     Width="130" />
            <sdk:Label Content="Last Name:"
                       Grid.Column="0"
                       Grid.Row="1"
                       HorizontalAlignment="Left"
                       Margin="3"
                       VerticalAlignment="Center" />
            <TextBox Grid.Column="1"
                     Grid.Row="1"
                     Height="23"
                     HorizontalAlignment="Left"
                     Margin="3"
                     Name="lastNameTextBox"
                     Text="{Binding Path=LastName,
                            Mode=TwoWay,
                            TargetNullValue=”}"
                     VerticalAlignment="Center"
                     Width="130" />
            <sdk:Label Content="Email:"
                       Grid.Column="0"
                       Grid.Row="2"
                       HorizontalAlignment="Left"
                       Margin="3"
                       VerticalAlignment="Center" />
            <TextBox Grid.Column="1"
                     Grid.Row="2"
                     Height="23"
                     HorizontalAlignment="Left"
                     Margin="3"
                     Name="emailTextBox"
                     Text="{Binding Path=Email,
                            Mode=TwoWay,
                            TargetNullValue=”}"
                     VerticalAlignment="Center"
                     Width="130" />
            <sdk:Label Content="Age:"
                       Grid.Column="0"
                       Grid.Row="3"
                       HorizontalAlignment="Left"
                       Margin="3"
                       VerticalAlignment="Center" />
            <TextBox Grid.Column="1"
                     Grid.Row="3"
                     Height="23"
                     HorizontalAlignment="Left"
                     TextAlignment="Right"
                     Margin="3"
                     Text="{Binding Path=Age,
                            Mode=TwoWay,
                            TargetNullValue=”}"
                     VerticalAlignment="Center"
                     Width="130" />
            <CheckBox Grid.Column="1"
                      Grid.Row="4"
                      IsChecked="{Binding VaccinationOptOut,
                                Mode=TwoWay,
                                TargetNullValue=false}"
                      Content="Vaccination Opt Out"/>
            <sdk:Label Content="Vaccination Date:"
                       Grid.Column="0"
                       Grid.Row="5"
                       HorizontalAlignment="Left"
                       Margin="3"
                       VerticalAlignment="Center" />
            <sdk:DatePicker Grid.Column="1" 
                        Grid.Row="5" 
                        Height="23" 
                        HorizontalAlignment="Left" 
                        Margin="3" 
                        SelectedDate="{Binding Path=VaccinationDate,
                                        Mode=TwoWay,
                                        TargetNullValue=”}" 
                        VerticalAlignment="Center" 
                        Width="130" />
            <sdk:Label Content="Registration Date:"
                       Grid.Column="0"
                       Grid.Row="6"
                       HorizontalAlignment="Left"
                       Margin="3"
                       VerticalAlignment="Center" />
            <sdk:DatePicker Grid.Column="1"
                        Grid.Row="6"
                        Height="23"
                        HorizontalAlignment="Left"
                        Margin="3"
                        SelectedDate="{Binding Path=RegistrationDate,
                                        Mode=TwoWay,
                                        TargetNullValue=”}"
                        VerticalAlignment="Center"
                        Width="130" />
        </Grid>

        <StackPanel Grid.Row="1"
                    Orientation="Horizontal"
                    HorizontalAlignment="Right">
        <Button x:Name="SaveButton"
                Content="Save"
                Command="{Binding SaveCommand}"
                Width="75"
                Height="24"
                Margin="5"/>
        <Button x:Name="CancelButton"
                Content="Cancel"
                Command="{Binding CancelCommand}"
                Width="75"
                Height="24"
                Margin="5" />
        </StackPanel>
    </Grid>
</UserControl>

When you have the above code in place, you should see the design-time data appear in the designer.

8) Run and ensure the runtime data appears.

The code displays the form as shown at the top of this post. As you edit the data on the form, note that there is no validation in place. You can type "aaa" into the age. You can delete the first or last name. And you can put in "aaa" into the email address.

See the other posts in this series for information on adding the validation.

Enjoy!

Silverlight MVVM, RIA, and Validation

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 5:43 pm

One of the biggest challenges of implementing validation in Silverlight is finding the appropriate technique based on your architecture and goals. There are lots of choices and lots of examples, but not all examples work in all scenarios. Plus many examples demonstrate techniques that are no longer necessary or valid in the current version of Silverlight or when using WCF RIA services.

Below are a set of posts on techniques for performing validation with Silverlight 4.0 using MVVM, WCF RIA Services, and custom business objects.

A few things to keep in mind if you are using Silverlight and WCF RIA Services:

  1. In most cases, the code should not need to throw exceptions for validation errors.
  2. The latest version of Silverlight/RIA provides automatic validation and a built-in validation error dictionary, so you do not have to write any code to build or populate validation errors.

The last point is an important one because there are videos and posts that go through the pages and pages of code required to manage your own validation and validation error dictionary. While this code is necessary and useful if you are using WCF directly, it is not necessary if you are using WCF RIA Services.

Here are the links:

Click on a link to navigate to the desired post.

Enjoy!

February 7, 2011

Simple Silverlight MVVM Base Class

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 12:54 am

As you start to add more Views and associated View/Models to your Silverlight line of business (LOB) application, you will notice that the classes include some common and repeated code. Building a simple base class for your View/Models provides a single place for that common code.

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

NOTE: If you are new to MVVM, this prior post provides an introduction.

To keep things simple, the ViewModelBase class defined here supports two features that you will need in most of your View/Model classes:

  • Implementing INotifyPropertyChanged

When building an MVVM application, the View/Model includes a set of properties that expose the data for your user interface. The View (xaml) connects the user interface element properties, such as a TextBox property, to the View/Model properties using binding.

When the View/Model property is updated, the user interface must be notified that the property is changed so that the user interface is updated. This is done using the PropertyChanged event that is part of INotifyPropertyChanged.

  • Determining whether the view is running from the designer.

If you reference the View/Model in the View’s xaml, the Visual Studio designer creates an instance of the View/Model when you open the View in the designer. Any code you define in the View/Model constructor is then executed. This makes it easy to define design-time data and populate the controls, improving your design-time experience.

But you only want the design-time data at design time. Define a flag accessible in the constructor to determine whether to load the design-time data or the actual data.

NOTE: Code in your constructor that accesses data using WCF RIA Services must not execute at design-time or Visual Studio won’t open the designer.

The ViewModelBase class code is shown below in both VB and C# and then described in detail.

In C#:

using System;
using System.ComponentModel;

namespace InStepSM.SL.ViewModels
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        private static bool? _IsInDesignMode;
        public static bool IsInDesignModeStatic
        {
            get
            {
                if (! _IsInDesignMode.HasValue)
                    _IsInDesignMode =
                       DesignerProperties.IsInDesignTool;
                return _IsInDesignMode.Value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            // Send an event notification that the property changed
            // This allows the UI to know when one of the items changes
            if (!String.IsNullOrEmpty(propertyName) &&
                    PropertyChanged != null)
                PropertyChanged(this,
                     new PropertyChangedEventArgs(propertyName));
        }
    }
}

In VB:

Imports System.ComponentModel

Namespace ViewModels
    Public Class ViewModelBase
        Implements INotifyPropertyChanged

        Private Shared _IsInDesignMode As Boolean?
        Public Shared ReadOnly Property IsInDesignModeStatic As Boolean
            Get
                If Not _IsInDesignMode.HasValue Then
                    _IsInDesignMode = DesignerProperties.IsInDesignTool
                End If
                Return _IsInDesignMode.Value
            End Get
        End Property

        Public Event PropertyChanged(ByVal sender As Object, 
            ByVal e As System.ComponentModel.PropertyChangedEventArgs) 
         Implements
         System.ComponentModel.INotifyPropertyChanged.PropertyChanged

        Protected Sub OnPropertyChanged(ByVal propertyName As String)
            ‘ Send an event notification that the property changed
            ‘ This allows the UI to know when one of the items changes
            If Not String.IsNullOrEmpty(propertyName) Then
                RaiseEvent PropertyChanged(Me,
                    New PropertyChangedEventArgs(propertyName))
            End If
        End Sub
    End Class
End Namespace

The IsInDesignModeStatic property defines whether the application is currently in design mode. If this flag is true, then the View is currently open in the Visual Studio (or Expression Blend) designer. If it is false, then the View is opened at runtime.

[For more information on detecting design mode, see this post from Laurent Bugnion.]

The PropertyChanged event defines the event for the INotifyPropertyChanged interface. The OnPropertyChanged method raises the PropertyChanged event. Code in the View/Model calls OnPropertyChanged in the setter for any property that could be bound to the View.

For example of how to use this ViewModelBase class, here is a StudentsViewModel for a Students View that inherits from the ViewModelBase.

In C#:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Client;
using InStepSM.SL.Web;

namespace InStepSM.SL.ViewModels
{
    public class StudentsViewModel : ViewModelBase
    {
        private StudentContext StudentContextInstance;

        private ObservableCollection<Student> _StudentsList;
        public ObservableCollection<Student> StudentsList
        {
            get
            {
                return _StudentsList;
            }
            set
            {
                if (_StudentsList != value)
                {
                    _StudentsList = value;
                    OnPropertyChanged("StudentsList");
                }
            }
        }

        public StudentsViewModel()
        {
            LoadData();
        }

        /// <summary>
        /// Loads the data for the application.
        /// </summary>
        private void LoadData()
        {
            if (IsInDesignModeStatic)
            {
                LoadDesignData();
            }
            else
            {
                // Load the student data asynchronously
                StudentContextInstance = new StudentContext();
                var loadop = StudentContextInstance.Load
                       (StudentContextInstance.GetStudentsQuery(),
                                         OnStudentsLoaded, null);
            }
        }

        /// <summary>
        /// Loads temporary data for use in the designer.
        /// </summary>
        /// <remarks></remarks>
        private void LoadDesignData()
        {
            //Design mode data
            ObservableCollection<Student> temp =
                               new ObservableCollection<Student>();
            temp.Add(new Student {LastName = "Baggins",
                                  FirstName = "Bilbo",
                                  Age = 111,
                                  Email = "testemail@live.com",
                                  RegistrationDate = DateTime.Now});
            temp.Add(new Student {LastName = "Baggins",
                                  FirstName = "Frodo",
                                  Age = 32,
                                  Email = "testemail@yahoo.com",
                                  RegistrationDate = DateTime.Now});
            StudentsList = temp;
        }


        private void OnStudentsLoaded(LoadOperation lo )
        {
            StudentsList = 
   new ObservableCollection<Student>(StudentContextInstance.Students);
        }
    }
}

In VB:

Imports System.Collections.ObjectModel
Imports System.ServiceModel.DomainServices.Client
Imports InStepSM.SL.Web

Namespace ViewModels
    Public Class StudentsViewModel
        Inherits ViewModelBase

        Private StudentContextInstance As StudentContext

        Private _StudentsList As ObservableCollection(Of Student)
        Public Property StudentsList() As
                                 ObservableCollection(Of Student)
            Get
                Return _StudentsList
            End Get
            Set(ByVal value As ObservableCollection(Of Student))
                If _StudentsList IsNot value Then
                    _StudentsList = value
                    OnPropertyChanged("StudentsList")
                End If
            End Set
        End Property

        Public Sub New()
            LoadData()
        End Sub

        ”’ <summary>
        ”’ Loads the data for the application.
        ”’ </summary>
        Private Sub LoadData()
            If IsInDesignModeStatic Then
                LoadDesignData()
            Else
                ‘ Load the student data asynchronously
                StudentContextInstance = New StudentContext
                Dim loadop =
                  StudentContextInstance.Load(StudentContextInstance.
                             GetStudentsQuery(),
                             AddressOf OnStudentsLoaded, Nothing)
            End If
        End Sub

        ”’ <summary>
        ”’ Loads temporary data for use in the designer.
        ”’ </summary>
        Private Sub LoadDesignData()
            ‘ Design mode data
            Dim temp As New ObservableCollection(Of Student)
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Bilbo",
                                       .Age = 111,
                                       .Email = "testemail@live.com",
                                       .RegistrationDate = Now()})
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Frodo",
                                       .Age = 32,
                                       .Email = "testemail@yahoo.com",
                                       .RegistrationDate = Now()})
            StudentsList = temp
        End Sub

        Private Sub OnStudentsLoaded(ByVal lo As LoadOperation)
            StudentsList = New ObservableCollection(Of Student)
                                  (StudentContextInstance.Students)
        End Sub
    End Class
End Namespace

This code begins by declaring an instance of a StudentContext. The StudentContext, Student, and Students class referenced by the above code were all generated by Visual Studio as part of WCF RIA Services. See this prior post for details on setting up the database, Entity Framework (EF) model, and associated WCF RIA Services code.

NOTE: This same code also works if you use WCF RIA Services with something other than EF, such as LinqToSql or your own business objects.

The StudentsList property of the ViewModel class defines the list of students. The View binds to this property to display the list of students in a DataGrid. Notice the OnPropertyChanged call. This ensures that the user interface is notified when the StudentsList property changes.

The constructor calls the LoadData method to load the data. The LoadData method uses the IsInDesignModeStatic property from the base class to determine whether the View is in design mode or run mode.

If in design mode, the application loads some hard-coded sample data. This data appears in the designer as shown below:

image

Having design-time data makes it easier to see what the user interface will look like. This is especially useful when you style the DataGrid.

If the View is not in design mode, the code calls the Load method of the StudentContext. This method is generated as part of the WCF RIA Services feature. The first parameter to the Load method is the name of the query to call, which is also in the generated code. Since Silverlight data access is asynchronous, this method call will not wait for the data to be retrieved.

The second parameter to the Load method is the callback function. This callback function is executed when the load is complete, allowing you to work with the data as needed.

In this case, the OnStudentsLoaded method simply assigns the resulting data to the StudentsList property. The Setter of the StudentsList property then raises the OnPropertyChanged event and the grid is updated to display the list of students.

Here is the associated xaml:

<navigation:Page x:Class="InStepSM.SL.Views.Students"       
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"
mc:Ignorable="d"
xmlns:navigation=
       "clr-namespace:System.Windows.Controls;
       assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="Students Page" 
xmlns:riaControls=
       "clr-namespace:System.Windows.Controls;
        assembly=System.Windows.Controls.DomainServices"
xmlns:sdk=
    "
http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
 xmlns:vms="clr-namespace:InStepSM.SL.ViewModels">

    <!– Reference the View/Model –>
    <navigation:Page.DataContext>
        <vms:StudentsViewModel />
    </navigation:Page.DataContext>

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <TextBlock x:Name="HeaderText"
                   Grid.Row="0"
                   Grid.Column="0" Grid.ColumnSpan="2"
                    Style="{StaticResource HeaderTextStyle}"
                    Text="Students" />
             
        <sdk:DataGrid AutoGenerateColumns="False"
                Grid.Row="1" Grid.Column="1"
                Height="200"
                HorizontalAlignment="Stretch"
                ItemsSource="{Binding StudentsList}"
                Name="studentDataGrid"
                RowDetailsVisibilityMode="VisibleWhenSelected"
                VerticalAlignment="Top"
                Width="457">
        <sdk:DataGrid.Columns>
        <sdk:DataGridTextColumn x:Name="firstNameColumn"
                                Binding="{Binding Path=FirstName}"
                                Header="First Name"
                                Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="lastNameColumn"
                                Binding="{Binding Path=LastName}"
                                Header="Last Name"
                                Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="ageColumn"
                                Binding="{Binding Path=Age}"
                                Header="Age" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="emailColumn"
                                Binding="{Binding Path=Email}"
                                Header="Email" Width="120" />
        <sdk:DataGridTemplateColumn x:Name="registrationDateColumn"
                                    Header="Registration Date"
                                    Width="SizeToHeader">
            <sdk:DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <sdk:DatePicker SelectedDate="{Binding
                        Path=RegistrationDate,
                        Mode=TwoWay,
                        NotifyOnValidationError=true,
                        ValidatesOnExceptions=true,
                        TargetNullValue=”}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellEditingTemplate>
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding
               Path=RegistrationDate, StringFormat=\{0:d\}}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
        </sdk:DataGrid.Columns>
        </sdk:DataGrid>

        <Grid Grid.Row="2"
              Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>

        <Button x:Name="SaveButton"
                Grid.Column="1"
                Margin="10,10,0,10"
                Content="Save"
                HorizontalAlignment="Right"
                Width="60" />
        </Grid>
    </Grid>
</navigation:Page>

The result looks something like this:

image

NOTE: The resulting page is currently read-only because there is no Save implementation defined in the above code. Handling the Save command is information for a later post.

The ViewModelBase class defined in this post provides a starting point for building your View/Model base class.

Enjoy!

February 5, 2011

Updating Data in a Silverlight Application: EF

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 4:16 pm

This prior post covered the details of setting up a database, defining an entity model using Entity Framework, building WCF RIA Services to access that entity model, and hooking it all up to a Silverlight DataGrid. That is all well and good. But most line of business (LOB) applications also need to update data. That’s the purpose of this post.

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

NOTE: This post assumes you already have the database, entity model, and WCF RIA Service classes in place. If you need information on how to perform these prerequisite steps, see this prior post.

The first step in adding update functionality is to modify the UI. Add a Save button to a page containing a DataGrid.

The xaml for the Save button looks like this:

<Button x:Name="SaveButton"
        Grid.Column="1"
        Margin="10,10,0,10"
        Content="Save"
        HorizontalAlignment="Right"
        Width="60"
        Click="SaveButton_Click" />

Then add the following to the code behind:

In C#:

private void SaveButton_Click(object sender, RoutedEventArgs e)
{
    studentDataGrid.CommitEdit();
    studentDomainDataSource.SubmitChanges();
}

In VB:

Private Sub SaveButton_Click(ByVal sender As System.Object,
                   ByVal e As System.Windows.RoutedEventArgs) _
                   Handles SaveButton.Click
    studentDataGrid.CommitEdit()
    StudentDomainDataSource.SubmitChanges()
End Sub

This code first completes any current edit operation on the grid. Then it uses the DomainDataSource that was defined in the xaml to submit the changes to the database.

When you run the application, the result will look something like this:

image

Edit the data in the grid and click Save to save the changes.

If you are happy with using a DomainDataSource and with saving the data in the code behind, you are done. If you want to handle the data retrieval without a DomainDataSource and follow a more MVVM approach, you still have some work to do. But that is another post.

Click here to download the associated source code.

Enjoy!

EDIT 2/8/2011: Added the link to the source code.

Next Page »

© 2014 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs