Introduction
By now we have discussed the preparations around the project, how to add a DomainService that contains our business logic and combine it with the entity framework. If you have missed one of the previous articles you can find them here:
Creating applications with .NET RIA Services Part 1 - Introduction
Creating applications with .NET RIA Services Part 2 - Creating the project
Creating applications with .NET RIA Services Part 3 - Adding a DomainService class
As you know from the previous articles in order to demonstrate the features of the .NET RIA Services I am creating the Web Administration Tool from ASP.NET in Silverlight. In this article I am going to create the Manage Users page and add the functionality needed to visualize Data. For that purpose I will need the DataGrid control, the new DataPager control and the DomainDataSource that is a part of the .NET RIA Services framework.
Here is a link to the live demo at this stage and the source code. Note that they will be updated with each article! ;)
Adding the DomainDataSource control
The DomainDataSource control can be found in the Systems.Web.Ria.Controls assembly and we have to declare the proper namespace in order to use it:
<navigation:Page x:Class="WebAdministrationTool.ManageUsers"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
xmlns:ria="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
...
Title="ManageUsers Page">
<Grid x:Name="LayoutRoot">
...
<ria:DomainDataSource x:Name="UsersDataSource"></ria:DomainDataSource>
...
</Grid>
</navigation:Page>
The next step is to configure the DomainDataSource control to work with our DomainService class named UsersManager, which we have created in the previous article. The only thing we have to do is to set the DomainContext property:
<navigation:Page x:Class="WebAdministrationTool.ManageUsers"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
xmlns:ria="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
xmlns:local="clr-namespace:WebAdministrationTool"
...
Title="ManageUsers Page">
<Grid x:Name="LayoutRoot">
...
<ria:DomainDataSource x:Name="UsersDataSource" LoadMethodName="LoadAspnet_Users">
<ria:DomainDataSource.DomainContext>
<local:UsersManager />
</ria:DomainDataSource.DomainContext>
</ria:DomainDataSource>
...
</Grid>
</navigation:Page>
As the UsersManager class can be accessed through the generated code on the client we declare the namespace of our project in the XAML. We also specify the method that the DomainDataSource should use in order to load the data from the data base. Note that on the server our method is named GetAspnet_Users, but on the client it’s LoadAspnet_Users (see in the previous article how to use the DomainService on the client by using the managed code).
Adding a DataGrid control to the scene
Now as we have our DomainDataSource control prepared let’s add a Grid and bind them together:
<data:DataGrid x:Name="UsersGrid"
IsReadOnly="True"
AutoGenerateColumns="False"
HeadersVisibility="None"
GridLinesVisibility="None">
<data:DataGrid.ItemsSource>
<Binding ElementName="UsersDataSource" Path="Data" />
</data:DataGrid.ItemsSource>
<data:DataGrid.Columns>
<data:DataGridTextColumn Binding="{Binding UserName}" />
</data:DataGrid.Columns>
</data:DataGrid>
Using the Element-to-Element binding that came with Silverlight 3 we bind the ItemsSource of the DataGrid to the Data property of the DomainDataSource control.
Now let’s add a row details to our DataGrid, which will give more information about the selected user. First let’s take a look at the GetAspnet_Users of the UsersManager DomainService on the server:
public IQueryable<aspnet_Users> GetAspnet_Users()
{
return this.Context.aspnet_Users;
}
It returns the content of the aspnet_Users table. In this table the only information we have is the username. The email, the comment and if the user is active can be found in the aspnet_Membership table and the information about the roles, that the user is in, is located in the aspnet_UsersInRoles table. These two tables are in relation with the aspnet_Users table and the associations between them are defined in the data model, so we can modify the get method in the following way:
public IQueryable<aspnet_Users> GetAspnet_Users()
{
return this.Context.aspnet_Users.OrderBy( "it.UserName" )
.Include( "aspnet_Membership" )
.Include( "aspnet_UsersInRoles" );
}
In the previous article we configured the metadata for the aspnet_Users, so we can now use the following properties on the client:
internal sealed class aspnet_UsersMetadata
{
...
[Include]
public aspnet_Applications aspnet_Applications;
[Include]
public aspnet_Membership aspnet_Membership;
[Include]
public EntityCollection<aspnet_UsersInRoles> aspnet_UsersInRoles;
...
}
When the query is executed they are populated with the proper data. Now back on the client we have the following row details template for the DataGrid:
<data:DataGrid x:Name="UsersGrid"
IsReadOnly="True"
AutoGenerateColumns="False"
HeadersVisibility="None"
GridLinesVisibility="None">
...
<data:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="IsActiveLabel" Text="Is active: " FontWeight="Bold" Grid.Column="0" Grid.Row="0" />
<TextBlock x:Name="IsActiveBlock"
Text="{Binding aspnet_Membership.IsApproved,Converter={StaticResource BooleanConverter}}"
Grid.Column="1" Grid.Row="0" />
<TextBlock x:Name="EmailLabel" Text="E-mail: " FontWeight="Bold"
Grid.Column="0" Grid.Row="1" />
<TextBlock x:Name="EmailBlock" Text="{Binding aspnet_Membership.Email}"
Grid.Column="1" Grid.Row="1" />
<TextBlock x:Name="DescriptionLabel" Text="Description: " FontWeight="Bold"
Grid.Column="0" Grid.Row="2" />
<TextBlock x:Name="DescriptionBlock" Text="{Binding aspnet_Membership.Comment}"
Grid.Column="1" Grid.Row="2" />
</Grid>
</DataTemplate>
</data:DataGrid.RowDetailsTemplate>
...
</data:DataGrid>
That’s it!
Paging the data
There is one last thing left to do – to add paging to the DomainDataSource. That can be easily accomplished by using the DataPager control (introduced with Silverlight 3). The control is located in the System.Windows.Controls.Data.DataForm assembly, so once again we have to declare a namespace in our XAML:
<navigation:Page x:Class="WebAdministrationTool.ManageUsers"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
xmlns:validation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm"
...
Title="ManageUsers Page">
<Grid x:Name="LayoutRoot">
...
<validation:DataPager x:Name="UsersPager"
PageSize="10"
Source="{Binding Data, ElementName=UsersDataSource}" />
</Grid>
</navigation:Page>
Once bound to the Data property of the DomainDataSource control it handles the paging itself, we just have to set its page size. The most important thing to mention is that the DataPager control can page collections which implement the IPagedCollectionView interface. The Data property is such a collection.
Also here the LoadSize property of the DomainDataSource control can be found useful. It determines the count of the items to be loaded in the Data property of the control. For example I can set it to 10 and each time the page is changed only 10 items will be loaded. In the common case we set it to a number multiple to the page size. Another option is to set it to the count of the items, if it is known, so all items will be loaded and no other requests to the server will be made.
Note: The DataPager doesn't work with unsorted collections, because the Skip and Take extension methods, that the control uses, are unavailable for such them. By default the EntityFrameowrk returns unsorted data and that's why I have added an OrderBy to my query. The other way is to sort the data on the client via the DomainDataSource control, but that will be the subject of my next article.
Conclusion
In this article I explained some basics about the DomainDataSource, the DataGrid and the DataPager controls. Thanks to them the loading and the visualization of data is not as tough as it was before. In the next article we’ll see how they allow us to easily apply sorting and filtering to the loaded data.
If you have any questions, I’ll be glad to answer them. :)