Silverlight 2 Beta 2: Doing Data Part II

This is the second in a series of articles focusing on working with Data and Databinding with Silverlight 2 Beta 2. Each article in this series focuses on a simple "task" in an easy-to-understand progression of working with Data via ASMX and WCF web service, WebClient, and other modalities, and using Silverlight Controls with DataBinding.

In this second part of the series, we're going to "flesh out" the Silverlight UI a bit with a bound ListBox to select the desired quotation Subject, and a Button to initiate the call. We are still using the Quotations sample database from the first article, so if you haven't installed that, you may wish to visit that article first and download the bits. In this exercise, instead of using a simple ASMX webservice, we will switch to a WCF service and use the LINQ to SQL DataClasses designer to create our objects from the stored procedures GetSubjects and GetRandomQuote respectively.

First, lets get everything set up.  Create a new C# Silverlight Application, and choose "Web Application Project" as your web project option. Call the Solution SilverlightData2.  I recommend having the web app project run under IIS. Now, lets add our LINQ to SQL classes. Right click on the SilverlightData2Web project and choose "Add New Item". Select the "Linq to Sql Classes" item. This will add a DataClasses1.dbml designer with associated codebehind files.

Now, in Server Explorer, make a new connection to the QUOTES database if you do not already have one. Expand the Quotes database node in Server Explorer, and expand the Stored Procedures node. Drag the two stored procs "GetRandomQuote" and "GetSubjects" over to the extreme right hand pane of the designer. Click Save and all your "stuff" is autogenerated. At this point, there is literally nothing else to do in order to use the LINQ to SQL class you've created!

Now, let's add a WCF Service to the web project.  Right click on the Web Project, select Add New Item, and select the "Silverlight Enabled WCF Service". Now let's add a service Interface. Add an Interface to the project, and call it IService1. The interface code should look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;

namespace SilverlightData2Web
{
  
    [ServiceContract]
 public interface IService1
 {
        [OperationContract]
        List GetRandomQuote(int numQuotes, string subject);


        [OperationContract]
        List GetSubjects(string subject);
}
    
}
Now let's implement our interface. In Service1.svc.cs, you can right - click on the IService1 inherited member and choose "Implement Interface". Your completed code should look like this:
namespace SilverlightData2Web
{
   [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   public class Service1: IService1{
#region IService1 Members

public List  GetRandomQuote(int numQuotes, string subject)
{
    SilverlightData2Web.DataClasses1DataContext ctx = new DataClasses1DataContext();
    return ctx.GetRandomQuote(numQuotes, subject).ToList();
}

public List GetSubjects(string subject)
{
    SilverlightData2Web.DataClasses1DataContext ctx = new DataClasses1DataContext();
    return ctx.GetSubjects(subject).ToList();
}

#endregion
}

Note that it only takes two lines of code to complete the body of each method. We create a DataContext, and return the results of the method, calling ToList() on the LINQ query result.

Now we are ready to complete the Silverlight Application portion of our solution. In the SilverlightData2 application, right - click on ServiceReferences node and choose "Add Service Reference". You should be able to discover your new service in the same solution, and add the ServiceReference1 reference.

Now we need to set up our new Silverlight Page. Here is the XAML Markup you should insert into Page.xaml:

<UserControl x:Class="SilverlightData2.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
    Width="900" Height="300">
    <Grid x:Name="grdLayoutRoot" Background="White" Width="900" Height="300" ShowGridLines="False">
        <Grid.RowDefinitions>
            <RowDefinition Height="120"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250"></ColumnDefinition>
            <ColumnDefinition Width="100"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="50"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Button x:Name="btnTest" Click="btnTest_Click"  Width="50" Height="20" Content="GO" Grid.Row="0" Grid.Column="1"></Button>
        <ListBox x:Name="Subject" Width="250" Height="100" DisplayMemberPath="SUBJECT"  ScrollViewer.VerticalScrollBarVisibility="Visible" ></ListBox>

        <Controls:DataGrid x:Name="Grid1"  Width="900" AutoGenerateColumns="True" Margin="0,0,0,0"  Grid.Row="2" Grid.Column="0"  Grid.ColumnSpan="5" RowBackground="LightSteelBlue"
            AlternatingRowBackground="Azure">
        </Controls:DataGrid>
    </Grid>

</UserControl>

The above defines a Grid with 2 rows, the first of which at size 120 is for the ListBox of Subjects and the "GO" button. The bottom row is for the DataGrid. The ColumnDefinitions define placement for the controls. You can see how each control has a Grid.Row= and a Grid.Column= attribute for placement.

Now let's take care of our codebehind and we'll be done:

public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            var c = new ServiceReference1.Service1Client();
            c.GetSubjectsCompleted += new EventHandler(c_GetSubjectsCompleted);
            c.GetSubjectsAsync("");
        }

        void c_GetSubjectsCompleted(object sender, GetSubjectsCompletedEventArgs e)
        {
          var s = e.Result;
            this.Subject.ItemsSource =s;
        }

        void c_GetRandomQuoteCompleted(object sender, GetRandomQuoteCompletedEventArgs e)
        {
          ObservableCollection q = e.Result;
            this.Grid1.ItemsSource = q;
        }

        private void btnTest_Click(object sender, RoutedEventArgs e)
        {
            var c = new ServiceReference1.Service1Client();
            c.GetRandomQuoteCompleted += new EventHandler(c_GetRandomQuoteCompleted);
            string subj = ((ServiceReference1.GetSubjectsResult) this.Subject.SelectedItem).SUBJECT;
          c.GetRandomQuoteAsync(4, subj);
        } 
    }
In the Page constructor, we want to populate our Subject Listbox, so we create a ServiceClient instance, set the GetSubjectsCompleted callback method, and call the GetSubjectsAsync method, passing in a null string to get "all" subjects. In the callback, we grab the result from the GetSubjectsCompletedEventArgs instance, and assign it to the Subject Listbox's ItemsSource property.  The other method is implemented in a similar manner, this is in response to selecting an item from the populated ListBox and pressing the "GO" button.  When you return a generic List through a Silverlight Webservice or WCF service reference, it is available to you as an ObservableCollection.

Silverlight provides the ObservableCollection<(Of <(T>)>) class, which is a provided base class data collection that implements the INotifyCollectionChanged interface, as well as the INotifyPropertyChanged interface. It also has the expected collection support, defined by deriving from the Collection<(Of <(T>)>) class.

The result in the UI should look like this:

 

You can download the complete Visual Studio 2008 solution here. Be sure to adjust the connection string in web.config to suit your environment. The SQL Script to generate the QUOTES database can be found in the download for the first article of this series.
By Peter Bromberg   Popularity  (2720 Views)