Search EggHeadCafe's Job Board
EggHeadCafe Silverlight WPF ASP.NET VB.NET C# Excel SQL Server SharePoint
search
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

WebArticlesForumsFAQs
JavaScript
ASP
ASP.NET
WCF

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

Operating SysArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Lounge
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

Silverlight 3 RIA Services: DataForm, Validation


By Peter Bromberg
Printer Friendly Version
View My Articles
323 Views
    

Silverlight 3 and RIA Services enables many typical business application functions that in Silverlight 2, you had to write "by hand". In this sample we provide a Silverlight Navigation Application that requires the user to enter their user data as a condition of being able to navigate. A DataForm is used which is bound with 2-way databinding and a UserSettings class with Validation Attributes. DataContractSerializer is used with IsolatedStorage.


I've been working on the middleware and the UI for Wally McClure's Azure "TimedTweet" application that sends scheduled twitter tweets, along with David Silverlight (no, they didn't name it for him) and a couple of other developers. It's given me a chance to really get into some of the new features that Silverlight 3 offers.

In Silverlight 3, if you choose the new Silverlight Navigation Application, you get navigation functionality "out of the box". What I have done here is to add an additional page, "Profile" that has a DataForm which is bound to a UserSettings Class that enables users to enter their Username, Password, Email and Phone and have them stored in Isolated Storage so that these settings will be  read automatically each time the application is loaded.

Silverlight 3 introduces a set of new controls that are specifically aimed at making the creation of data-centric RIAs easier. These include DataGrid, DataForm, DataPager, FieldLabel, DescriptionViewer, ErrorSummary, and ChildWindow. The RIA Services May 2009 Preview is out.

I started out with a UserSettings class:

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SLUserSettings
{
    [Bindable(true, BindingDirection.TwoWay)]
    public class UserSettings
    {
        [Required (ErrorMessage="UserName is required.")]
        [Bindable(true, BindingDirection.TwoWay)]
        public string UserName
        {
            get; set;
        }
        [Required(ErrorMessage = "Password is required.")]
        [Bindable(true, BindingDirection.TwoWay)]
        public string Password { get; set; }

        [Required(ErrorMessage = "Email is required.")]
        [RegularExpression( @"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")]
        [Bindable(true, BindingDirection.TwoWay)]
        public string Email { get; set; }

        [Required(ErrorMessage = "Phone Number is required.")]
        [Bindable(true, BindingDirection.TwoWay)]
        public string Phone { get; set; }


        public UserSettings()
        {
        }

        public UserSettings(string userName, string password, string email, string phone)
        {
            this.UserName = userName;
            this.Password = password;
            this.Email = email;
            this.Phone = phone;
        }

    }
}

Because of the System.ComponentModel.DataAnnotations namespace, we are able to apply Validation Attributes directly on the public fields of our class, and even include appropriate error messages. The Silverlight controls such as DataForm pick these up automatically; they even take care of displaying the validation errors. The developer doesn't have to write a single line of code (although you can if you want to).  In this case I've made all the fields [Required] and for the Email, I've provided a [RegularExpression] validator as well. Note also that each field carries the [Bindable(true, BindingDirection.TwoWay)] attribute. This will enable my Dataform to handle not only automatic display of each field name and value, but also for it to be enabled to edit and save the form data. I don't have to create any form fields at all. That is a whole bunch of code that I never enjoyed writing, and now I don't have to do it anymore!

Let's switch over to the App.xaml.cs class and see how that got wired up: 

 

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml;

namespace SLUserSettings
{
    public partial class App : Application
    {
        public const string FormDataDirectory = "UserSettings";
        public const string UserSettingsFileName = "settings.xml";
        public static bool NeedsProfileData = false;
        public static UserSettings MyUserSettings = new UserSettings("", "", "", "");
        public static string UserName;
        public static string Password;
        public static string Email;
        public static string Phone;
        

        public App()
        {
            this.Startup += this.Application_Startup;
            this.UnhandledException += this.Application_UnhandledException;
            InitializeComponent();
            LoadUserSettings();
        
        }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            this.RootVisual = new MainPage();
        }


        public static void LoadUserSettings()
        {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                string filePath = System.IO.Path.Combine(FormDataDirectory, UserSettingsFileName);
                //Check to see if file exists before proceeding
                if (store.FileExists(filePath))
                {
                    using (XmlReader xr = XmlReader.Create(
                        store.OpenFile(filePath, FileMode.Open, FileAccess.Read)))
                    {
                        DataContractSerializer ser = new DataContractSerializer(typeof(UserSettings));
                        MyUserSettings = (UserSettings)ser.ReadObject(xr);
                        UserName = MyUserSettings.UserName;
                        Password = MyUserSettings.Password;
                        Email = MyUserSettings.Email;
                        Phone = MyUserSettings.Phone;
                        xr.Close();
                        NeedsProfileData = false;
                    }

                    if (UserName.Length == 0)
                    {
                        NeedsProfileData = true;
                    }
                }
                else
                {
                    NeedsProfileData = true;
                }
            }
        }

        public static void SaveUserSettings()
        {
            if(MyUserSettings.UserName.Length==0) NeedsProfileData = true;
            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    store.CreateDirectory(FormDataDirectory);
                    IsolatedStorageFileStream fileStream = store.CreateFile(System.IO.Path.Combine(FormDataDirectory, UserSettingsFileName));
                    DataContractSerializer ser = new DataContractSerializer(typeof(UserSettings));
                    ser.WriteObject(fileStream, MyUserSettings);
                    fileStream.Close();
                }
            }
            catch
            {
                throw;
            }
        }
        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (!System.Diagnostics.Debugger.IsAttached)
            {
                //  e.Handled = true;
                ChildWindow ErrorWin = new ErrorWindow(e.ExceptionObject);
                ErrorWin.Show();
            }
        }
    }
}

The logic mostly just attempts to load the UserSettings from IsolatedStorage, using the DataContractSerializer to give us an instance of the UserSettings class, and if it isn't there or the data is not valid, it sets the boolean "NeedsProfileData" field. This is checked later in the application to direct the user to the Profile Page:

 void MainPage_Loaded(object sender, RoutedEventArgs e)
{
if (App.NeedsProfileData)
  {
    this.Frame.Navigate(new Uri("/Views/Profile.xaml", UriKind.Relative));
   
return;
  }
}

Now let's have a look at how the DataForm on Profile.xaml works. Here is the Xaml. As you can see, it is quite simple:

<navigation:Page xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm" x:Class="SLUserSettings.Views.Profile"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" Title="Profile Page">

<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
<
TextBlock Text="Profile" Style="{StaticResource HeaderTextStyle}"/>
<
StackPanel Style="{StaticResource ContentTextPanelStyle}" Orientation="Vertical">
<
TextBlock Text="Configure User Settings" Style="{StaticResource ContentTextStyle}"/>
<
my:DataForm Header="User Settings" Width="400" Height="350" x:Name="Form1" DeletingItem="Form1_DeletingItem" ItemEditEnded="Form1_ItemEditEnded">
</my:DataForm>
</StackPanel>
</
StackPanel>
</
Grid>
</
navigation:Page>

And here is all that is needed in the codebehind:


using
System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Windows.Navigation; namespace SLUserSettings.Views { public partial class Profile : Page { public Profile() { InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { List<UserSettings> settings = new List<UserSettings>(); settings.Add(App.MyUserSettings); this.Form1.ItemsSource = settings; } private void Form1_ItemEditEnded(object sender, DataFormItemEditEndedEventArgs e) { App.SaveUserSettings(); MessageBox.Show("Settings Saved."); } private void Form1_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e) { App.MyUserSettings.Email = ""; App.MyUserSettings.UserName = ""; App.MyUserSettings.Phone = ""; App.MyUserSettings.Password = ""; App.SaveUserSettings(); App.NeedsProfileData = true; } } }
You can download the Silverlight 3 Visual Studio Solution here.

Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites.
Please post questions at forums, not via email!

button
Article Discussion: Silverlight 3 RIA Services: DataForm, Validation
Peter Bromberg posted at Tuesday, May 12, 2009 1:23 PM
Original Article