|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Table of Contents
IntroductionI started using Silverlight a few weeks ago, and it looks really amazing. Combining the presentation powers of WPF with the strong capabilities of C#, Silverlight looks very powerful. The main focus of this article will be to retrieve data from a database and consume it in a Silverlight application. If you are an ASP.NET developer and want to start exploring Silverlight, then perhaps this is the right place to start since we will also be looking at similarities between Silverlight and ASP.NET. What We Will be CoveringWe will be developing our first business application in Silverlight. Since almost every business application has something to do with data and databases, we will be looking at how data (from a database) can be displayed inside a Silverlight application. Taking the Northwind database as an example, we will be generating some LINQ classes, a WCF service for retrieving data, and finally, a Running the Sample ApplicationA small tweak is needed to run the sample project. By default, there will be no startup project for the solution, so we must manually set Beware ASP.NET ProgrammersIf you are an ASP.NET programmer, it may be important for you to note that the C# code in the Silverlight project runs on the "client" and not on the server. Perhaps, you can think of it as JavaScript. Thus, the good thing with Silverlight is that you now have full control on the client side without knowing or writing any JavaScript (at least, I am not good at writing JavaScript). Why WCF, Can't I Access the DB Directory?Well, the simple reason is that since the C# code runs on the client, our server databases will not be accessible directly. Note that we do not have items like Let us StartTo start creating Silverlight applications using Visual Studio, we need to install Silverlight tools for Visual Studio that can be downloaded from here. After installing the add-on, two new project types, Silverlight Application and Silverlight Class Library, will be added. We will start by creating a new "Silverlight Application" project named DataApplication.
Visual Studio will then ask how we want to deploy our Silverlight application; we are going to select "ASP.NET Web Application Project" in which we will create the LINQ classes and the WCF service.
If all things go correctly, we will be able to see two projects in our solution: Generating LINQ ClassesOur lightweight all-rounder Northwind (available from this link) is always one of the best databases to start with. Note that the MDF is also included in the sample project, and is connected via SQLExpress. To start generating LINQ data classes, we need to add a new LINQ Data Classes object in our ASP.NET application.
Now, using the Server Explorer, create a new data connection to the Northwind database (either using SQLExpress, or SQL Server if you have it), and then drag the tables
Creating a Silverlight WCF ServiceNow, we are about to add a service to our server project for retrieving data. Before Silverlight 2 Beta 2, we needed some tweaks with the standard WCF service template to use in a Silverlight project, but fortunately, Beta 2 and newer versions give us the "Silverlight-Enabled WCF service" template that handles all things itself. We are going to add a new Silverlight-enabled WCF Service named DataService to our ASP.NET project.
We will write three methods in our service, one that returns all the customers, one that returns orders of a customer, and finally, one that returns order details of a particular order. Note that the methods must be marked with the attribute [OperationContract]
public List<Customer> GetCustomers()
{
DataClasses1DataContext datacontext = new DataClasses1DataContext();
return datacontext.Customers.ToList();
}
[OperationContract]
public List<Order> GetOrders(string customerID)
{
DataClasses1DataContext datacontext = new DataClasses1DataContext();
return (from order in datacontext.Orders
where order.CustomerID == customerID
select order).ToList();
}
[OperationContract]
public List<Order_Detail> GetOrderDetails(int orderID)
{
DataClasses1DataContext datacontext = new DataClasses1DataContext();
return (from orderdetail in datacontext.Order_Details
where orderdetail.OrderID == orderID
select orderdetail).ToList();
}
Adding a Service Reference to the Silverlight ProjectThat's all that was required on the server-side ASP.NET project. We have created LINQ data classes to get data from the database and a WCF service to return those LINQ objects. Now, we are ready to consume the service in our client-side Silverlight project. For this, we need to add a service reference in the
Creating the UIA Silverlight page/control consists of a layout XAML file and a code-behind xaml.cs file. Typically, similar to an ASP.NET page, the XAML file contains the layout definition (like a *.aspx file where we define our UI), while the xaml.cs file contains the logic and event handlers (like our aspx.cs file). Let us start by creating some basic layout for our application. Adding the Assembly to Use a DataGrid ControlWe will display our data using the
After this reference is added, we need to assign a namespace to this assembly in our XAML markup. To do this, add the following to the namespace declaration of the file Page.Xaml. xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
The Visual Studio IDE assists us here as the following screenshot shows:
UI LayoutHere's an outline of what to do: Create a <UserControl xmlns:basics="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls"
x:Class="DataApplication.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Data"
Width="Auto" Height="Auto">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="55" x:Name="HeaderRow" />
<RowDefinition Height="*" x:Name="ContentRow"/>
<RowDefinition Height="20" x:Name="FooterRow"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Heading -->
<TextBlock x:Name="txtHeader" Grid.Row="0"
FontSize="20" Margin="5,5" Foreground="Blue"
Text="My First Data Application in Silverlight">
</TextBlock>
<!-- A textblock in the footer to be used as an Status bar -->
<TextBlock x:Name="txtStatus" Grid.Row="2"
FontSize="10" Margin="5,0" Foreground="Red">
</TextBlock>
<!-- Content Holder -->
<Grid x:Name="ContentGrid" Grid.Row="1" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height=".6*" />
<RowDefinition Height=".4*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Listbox for displaying customers -->
<ListBox x:Name="lstCustomers" Grid.Column="0" Grid.RowSpan="2"
DisplayMemberPath="ContactName"
Loaded="lstCustomers_Loaded"
SelectionChanged="lstCustomers_SelectionChanged">
</ListBox>
<!-- DataGrid for displaying orders of a customer
(with autogenerated columns) -->
<data:DataGrid x:Name="dgOrders" Grid.Row="0" Grid.Column="1"
AutoGenerateColumns="True"
SelectionChanged="dgOrders_SelectionChanged">
</data:DataGrid>
<!-- DataGrid for displaying orderdetais for an order -->
<data:DataGrid x:Name="dgOrderDetails" Grid.Row="1" Grid.Column="1"
AutoGenerateColumns="True"
AutoGeneratingColumn="dgOrderDetails_AutoGeneratingColumn">
</data:DataGrid>
</Grid>
</Grid>
</UserControl>
I will not be explaining the WPF layout in much detail in this article as there are a lot of resources on The Code Project as well as outside; e.g., this one from Sacha Barber. We will rather have a quick look on the markup for the ListBoxThe listbox
Since we want the DataGridsCurrently, we are not doing anything fancy with Writing Some CodePopulating the ListBoxWe want to load all the customers into the listbox private void lstCustomers_Loaded(object sender, RoutedEventArgs e)
{
DataServiceClient svc = new DataServiceClient();
this.txtStatus.Text = "Loading customers...";
svc.GetCustomersCompleted += new
EventHandler<GetCustomersCompletedEventArgs>(svc_GetCustomersCompleted);
svc.GetCustomersAsync();
}
void svc_GetCustomersCompleted(object sender, GetCustomersCompletedEventArgs e)
{
if (e.Error == null)
{
this.lstCustomers.ItemsSource = e.Result;
this.txtStatus.Text = string.Empty;
}
else
{
this.txtStatus.Text =
"Error occurred while loading customers from database";
}
}
Displaying the Orders of a CustomerNow, we will write some code to display the orders when a customer in the listbox is selected. In the private void lstCustomers_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Customer selectedCustomer = this.lstCustomers.SelectedItem as Customer;
if (selectedCustomer != null)
{
DataServiceClient svc = new DataServiceClient();
this.txtStatus.Text = "Loading orders...";
svc.GetOrdersCompleted +=
delegate(object eventSender, GetOrdersCompletedEventArgs eventArgs)
{
if (eventArgs.Error == null)
{
this.dgOrders.ItemsSource = eventArgs.Result;
this.txtStatus.Text = string.Empty;
}
else
{
this.txtStatus.Text =
"Error occurred while loading orders from database";
}
};
svc.GetOrdersAsync(selectedCustomer.CustomerID);
}
}
Displaying the Order Details when an Order is SelectedSimilar to the listbox private void dgOrders_SelectionChanged(object sender, EventArgs e)
{
Order selectedOrder = this.dgOrders.SelectedItem as Order;
if (selectedOrder != null)
{
DataServiceClient svc = new DataServiceClient();
this.txtStatus.Text = "Loading order details...";
svc.GetOrderDetailsCompleted +=
(eventSender, eventArgs) =>
{
if (eventArgs.Error == null)
{
this.dgOrderDetails.ItemsSource = eventArgs.Result;
this.txtStatus.Text = string.Empty;
}
else
{
this.txtStatus.Text =
"Error occurred while loading order details from database";
}
};
svc.GetOrderDetailsAsync(selectedOrder.OrderID);
}
}
Removing Some Auto-generated Columns from dgOrderDetailsNote that in XAML, we set the private void dgOrderDetails_AutoGeneratingColumn(object sender,
DataGridAutoGeneratingColumnEventArgs e)
{
if (e.Column.Header.ToString() == "OrderID")
e.Column.Visibility = Visibility.Collapsed;
}
Checkpoint Reached..Run the ProjectNow, our small application is ready to be viewed. Run it.. Select some customers, view orders, edit the data displayed in the
Defining ColumnsColumns in a Silverlight
If you want to get more, Scott Morris has a nice blog entry on column types here. So, let's use this knowledge in our application. For the sake of simplicity, we will define only four columns: we will use a Here's the code that should replace the <!-- DataGrid for displaying orders of a customer -->
<data:DataGrid x:Name="dgOrders" Grid.Row="0" Grid.Column="1"
AutoGenerateColumns="False"
SelectionChanged="dgOrders_SelectionChanged">
<data:DataGrid.Columns>
<!-- OrderID text column -->
<data:DataGridTextColumn Header="Order ID" Binding="{Binding OrderID}" />
<!-- EmployeeID text column -->
<data:DataGridTextColumn Header="Employee ID" Binding="{Binding EmployeeID}" />
<!-- OrderDate template column -->
<data:DataGridTemplateColumn Header="Order Date" Width="150">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding OrderDate}" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
<data:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<basics:DatePicker SelectedDate="{Binding OrderDate, Mode=TwoWay}" />
</DataTemplate>
</data:DataGridTemplateColumn.CellEditingTemplate>
</data:DataGridTemplateColumn>
<!-- Freight template column -->
<data:DataGridTemplateColumn Header="Freight" Width="150">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Freight}"></TextBlock>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
<data:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Freight}" Width="50" />
<Slider Value="{Binding Freight, Mode=TwoWay}" Width="100"
Minimum="0" Maximum="500" />
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellEditingTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
</data:DataGrid>
In a similar way, we can use <ListBox x:Name="lstCustomer">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding NameProperty}"></TextBlock>
<Image Source="{Binding PictureProperty}"></Image>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note that the above code is just a sample. Since we do not have pictures in our objects, we cannot use this snippet in our application. Second RunRun the project again. Double click on an
Writing Data Back to the DBWell, this will be too much for this introductory article. But, if you have followed how the WCF service is used to communicate between the client and the server, you can easily write data back to the database by creating some functions in the service and calling them from the Silverlight application. Notice that the data bindings in this article are two way, that is, changing the value inside a ConclusionThat's all. I wrote this article to demonstrate how easy it is to build the foundation of any data application. Let us revise what we learnt: we created a data access layer using LINQ, and exposed it using a WCF service in our service side ASP.NET project. We retrieved data using the service client in our Silverlight application, and finally, used some data templates to have more control over data presentation. I hope this article created some motivation to start building your future applications in Silverlight. Happy Silverlighting... History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||