A buddy of mine keeps bugging me about putting together something about Silverlight. So, I thought I'd indulge him and put together an end to end example showing the use of ADO.NET Data Services as our data access layer, which in turn will publish a RESTful web service, that will be queried with LINQ and displayed in a Silverlight application.
Hopefully, this will be the first in many Silverlight posts, as the more and more I use the technology, the more and more it's potential strikes me.
To start, you're going to need a few things to execute this example:
- The usual suspects, Visual Studio 2008, SQL Server
- The AdventureWorks sample database
- Silverlight 2.0
- Silverlight Tools CTP from MIX 08
- ASP.NET Extensions
First thing we'll do is create a Silverlight application in Visual Studio. Go to File -> New Project, and select Silverlight Application and click OK. When Visual Studio prompts you about creating a test site or html page, select Web Site.
After creating our project, the next thing we'll do is create our LINQ to SQL entities. To do so, right click on the web project (not the Silverlight application project) and select Add New Item, then select LINQ to SQL Classes. Name the class AdventureWorks.dbml and click OK.
Create a connection to your AdventureWorks database in server explorer, and drag the Product table on to the design surface. You should have a simple surface that looks like this:
Next we'll create our ADO.NET Data Service. Right-click on your web project, choose Add New Item and select ADO.NET Data Service and name it ProductsDataService.svc. Open the service code behind, and edit the class definition to include your LINQ to SQL DataContext like so, and then un-comment out the lines that restrict access to your entities. You should have a service code behind that looks like the following:
{
public static void InitializeService(IWebDataServiceConfiguration config)
{
config.SetResourceContainerAccessRule("*", ResourceContainerRights.AllRead);
}
}
\When you run the service, you should see a page that looks like this:
Now that we have our REST service, we can move on to our Silverlight control. I'd recommend going through Scott Guthrie's series of tutorials on Silverlight 2.0 if you need an introduction to using Silverlight. The following goes on the assumption that you've gone through that article.
Our Silverlight application is going to be a simple control that lists out the top 20 products in the products table. I'm going to use a grid layout with one column, two rows. The top row will have a header, and the bottom row will have our list of products. First we'll layout our grid, which will result in the following XAML markup in our Page.xaml file in our Silverlight project:
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
</Grid>
</UserControl>
Next thing we'll do is add our title to the control, which will make the markup look like so:
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" >
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" FontSize="20" Text="Will's Product Catalog" HorizontalAlignment="Center" />
</Grid>
</UserControl>
Now, if we run the Silverlight project, we should get a test page that looks like this:
Next thing we'll do is add our ListBox. Your final XAML markup for the page should look like this:
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" FontSize="20" Text="Will's Product Catalog" HorizontalAlignment="Center" />
<ListBox Grid.Row="1" x:Name="productListBox" />
</Grid>
</UserControl>
Now we can write our code that calls the RESTful service, and binds this data to the ListBox. To do this, we'll open up the code behind for our Page.xaml, create a simple structure representing our product, and then in the constructor, we'll use the built in Silverlight networking and make the call to our service, hydrate our structure, and bind it to our list box.
using System.Collections.Generic;
using System.Linq;
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.Net;
using System.Xml.Linq;
namespace WillsProductCatalog
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
webClient.DownloadStringAsync(
new Uri("http://localhost:50492/WillsProductCatalog_Web/ProductDataService.svc/Products?$top=20&$filter=ListPrice%20gt%200"));
}
void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
XDocument xLinqDoc = XDocument.Parse(e.Result);
var products = from product in xLinqDoc.Descendants("{http://www.w3.org/2005/Atom}content")
select new Product
{
ID = Convert.ToInt32(product.Element("{http://schemas.microsoft.com/ado/2007/08/dataweb}ProductID").Value),
Name = product.Element("{http://schemas.microsoft.com/ado/2007/08/dataweb}Name").Value,
ProductNumber = product.Element("{http://schemas.microsoft.com/ado/2007/08/dataweb}ProductNumber").Value,
ListPrice = Convert.ToDecimal(product.Element("{http://schemas.microsoft.com/ado/2007/08/dataweb}ListPrice").Value)
};
productListBox.ItemsSource = products;
}
}
}
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public string ProductNumber { get; set; }
public decimal ListPrice { get; set; }
}
}
Its unfortunate that we can't use the ADO.NET Data Services classes (3.6 version of System.Web.Extensions) from Silverlight at this point, but I can understand that limiting the libraries in the Silverlight download keep it nice and small. Obviously, if it were there, we could really cut down on the amount of code that we need to write here.
So, now that we've got our Listbox bound, you can see how this renders out in the browser:
Now, the last thing we need to do is use the Silverlight data binding syntax to put something other than the class name in the Listbox. You can do that by changing your XAML syntax to the following:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ProductNumber}" Margin="5"/>
<TextBlock Text="{Binding Name}" Margin="5"/>
<TextBlock Text="{Binding ListPrice}" Margin="5"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And this will produce the following result:
Obviously, you can go much further with the formatting, and I'm hoping to be able to put together some smaller posts that highlight some of those formatting abilities.