Sunday, November 2, 2008

Silverlight 2.0 - Using Silverlight DataGrid to consume ASMX Web Service

Microsoft released Silverlight 2.0 on October 13, 2008. Silverlight 2 provides cross-browser rich UI experience which developers can use to author media rich applications using .NET language (C#, Visual Basic etc) of their choice.

For more details on setting up your Visual Studio 2008 development environment and Silverlight 2.0 visit: Silverlight web-site

The example uses NorthWind database. You can download and install NorthWind database from the following location.

The article will demonstrate working of Silverlight 2.0 enabled UI by creating an ASP.NET web-form with Silverlight DataGrid control consuming ASMX Web Service.

Silverlight DataGrid control allows structured data to be displayed without requiring any major programming effort, some of the rich UI features like column resizing, column reordering, frozen columns, sorting for data which supports ILIST interface are provided Out-of-the box.

Let us start with our example, we are going to build an ASMX Web Service which will extract data from NorthWind database using LINQ to SQL (
see my earlier post), later we will build Silverlight client with DataGrid which will consume this Web Service using LINQ to XML for parsing the Web Service response.


ASMX Web Service Setup:

a.
Use O/R designer to map the Database, Tables and Stored procedures, I have added 'Ten_Most_Expensive_Products' Stored procedure to the designer, this will be exposed as a Web Method by the ASMX Web Service:



b. Web Method which exposes Stored procedure to be consumed by Silverlight client application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Xml.Linq;

namespace NorthWindWS
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class DBWebService : System.Web.Services.WebService
{

[WebMethod]
public string GetTenMostExpensiveProducts()
{
//use DataContext
using (NorthWindDataContext DBContext = new NorthWindDataContext())
{
//LINQ to SQL query
var Categories = from cat in DBContext.Ten_Most_Expensive_Products()
where cat.UnitPrice > 0
select cat;

//LINQ to XML query
XDocument xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Categories",
from cat in Categories
select new XElement("Cat",
new XAttribute("ProductName", cat.TenMostExpensiveProducts),
new XAttribute("UnitPrice", cat.UnitPrice.ToString()))));
//return
return xDoc.ToString();
}
}
}
}

c. Consideration to make Web Service available across Domain Boundaries



Notice clientaccesspolicy.xml & crossdomain.xml files, these 2 files are required for Silverlight client application to access Web Service, for more details refer this article.

Silverlight Client Application:

a. Create New project -> Visual C# -> Silverlight -> Silverlight Application



b. Choose 'Add a new ASP.NET Web project to the solution to host Silverlight', this will give us an ASP.NET application to host and test Silverlight client



c. ASP.NET Web project added to the solution




d. Add ASMX reference to the project


e.
Web Service reference added to the DBWebService




f. Add StackPanel, DataGrid & Button controls to the Page.xaml file
<UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  x:Class="MySilver.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="800">

<StackPanel Background="AliceBlue" Width="Auto" Height="Auto">
<data:DataGrid Name="myGrid" AutoGenerateColumns="False" GridLinesVisibility="Horizontal" HeadersVisibility="Column"
RowBackground="Cornsilk" AlternatingRowBackground="LemonChiffon"
Width="500" Height="250" CanUserReorderColumns="True" CanUserSortColumns="True"
IsReadOnly="True" CanUserResizeColumns="True" Visibility="Collapsed"
>
<data:DataGrid.Columns>
<data:DataGridTextColumn Binding="{Binding ProductName}"
Width="300" Header="Product Name"/>
<data:DataGridTextColumn Binding="{Binding UnitPrice}"
Width="200" Header="Unit Price"/>
</data:DataGrid.Columns>
</data:DataGrid>
<TextBlock Text=" "></TextBlock>
<Button Name="myButton" Content="Call Web Service" Click="Button_Click" Width="100" Height="25"></Button>
</StackPanel>
</UserControl>

g. Wire the Button event handler to call the Web Service
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.Xml.Linq;

namespace MySilver
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();

}

private void Button_Click(object sender, RoutedEventArgs e)
{
MyDBWebService.DBWebServiceSoapClient myService = new MySilver.MyDBWebService.DBWebServiceSoapClient();
myService.GetTenMostExpensiveProductsCompleted += new EventHandler<MySilver.MyDBWebService.GetTenMostExpensiveProductsCompletedEventArgs>(myService_GetTenMostExpensiveProductsCompleted);
myService.GetTenMostExpensiveProductsAsync();
}

private void myService_GetTenMostExpensiveProductsCompleted( object sender, MyDBWebService.GetTenMostExpensiveProductsCompletedEventArgs e )
{
//show data
ShowData(e.Result);
}

void ShowData(string xmlData)
{
XDocument xmlCategories = XDocument.Parse(xmlData);
//LINQ to XML query, to extract response from Web Service
var categories = from cat in xmlCategories.Descendants("Cat")
where cat.Attribute("ProductName") != null
select new Product
{
ProductName = (string)cat.Attribute("ProductName"),
UnitPrice = (string)cat.Attribute("UnitPrice")
};
//bind to DataGrid
myGrid.Visibility = Visibility.Visible;
//attach to DataGrid, ToList() is used to enable DataGrid sorting which needs ILIST interface
myGrid.ItemsSource = categories.ToList();
}
}
}

h. Product.cs class which uses C# 'Automatic properties' to map the Web Service response
using System;

namespace MySilver
{
public class Product
{
public string ProductName { get; set; }
public string UnitPrice { get; set; }
}
}
i. Silverlight DataGrid output



Consideration:


Silverlight is a Client-side technology, when calling ASMX Web Service make sure it is invoked 'Asynchronously' and not 'Synchronously'. This is the reason 'ShowData' method (method which binds DataGrid to the Web Service response) is called from 'Completed' event handler.

I will try to provide more working examples of Silverlight controls in the future posts!


kick it on DotNetKicks.com