Efficient Paging In Silverlight 2.0

Posted by: Malcolm Sheridan , on 4/1/2009, in Category Silverlight 2, 3, 4 and 5
Views: 18367
Abstract: The following article demonstrates how to create and consume a WCF service in Silverlight 2.0 and use efficient server side paging using LINQ to page through event log data.
Efficient Paging In Silverlight 2.0
 
When it comes to ASP.NET, I am a huge fan of efficient code, and one of the most efficient ways to retrieve data from the server is to use paging. Paging has been around for a long time now, but it is not available out of the box in Silverlight 2.0. I thought this was a good time to demonstrate how to consume a WCF service in Silverlight, and to create efficient server side paging using LINQ.
Before we begin you need to have the Silverlight 2 Tools installed. You can go here to download it. 
To begin with open Visual Studio 2008 and choose File > New > Project > Silverlight > Silverlight Application:

 

New Project
When you click OK a new dialog will appear. Accept the default settings. The rules of this application are to create a WCF service that returns records from the Windows event log. Some computers store event log entries that can contain hundreds of entries, so it is important to page through this data efficiently.   Add a new class to the web application and name it EventEntry. Add the following code to the class:
C#
public class EventEntry
{
public string Message { get; set; }      
}
 
public class TotalInfo
{
public List<EventEntry> Entries { get; set; }
      public int Total { get; set; }
}
VB.NET
Public Class EventEntry
Private privateMessage As String
Public Property Message() As String
      Get
            Return privateMessage
      End Get
      Set(ByVal value As String)
            privateMessage = value
      End Set
End Property
End Class
 
Public Class TotalInfo
Private privateEntries As List(Of EventEntry)
Public Property Entries() As List(Of EventEntry)
      Get
            Return privateEntries
      End Get
      Set(ByVal value As List(Of EventEntry))
            privateEntries = value
      End Set
End Property
       Private privateTotal As Integer
       Public Property Total() As Integer
             Get
                   Return privateTotal
             End Get
             Set(ByVal value As Integer)
                   privateTotal = value
             End Set
       End Property
End Class
 
The code above is self explanatory, but we will use it later. Next we need to create a Silverlight enabled WCF service. Right click the project and choose Add > New Item > Silverlight > Silverlight-enabled WCF service:
WCF Template
It is important to create a Silverlight enabled WCF service because the only binding supported by Silverlight 2.0 is basicHttpBinding. Once the WCF service is created, add the following code:
C#
[OperationContract]
public TotalInfo FetchEventLogEntries(int skip, int take)
{
      TotalInfo info = new TotalInfo();
      List<EventEntry> entries = new List<EventEntry>();
 
      using (EventLog log = new EventLog("Application"))
      {
            info.Total = log.Entries.Count;
            var query = log.Entries.Cast<EventLogEntry>()
                  .OrderByDescending(o => o.TimeWritten)
                  .Skip(skip).Take(take);
 
            foreach (var item in query)
            {
                  entries.Add(new EventEntry()
                  {
                        Message = item.Message
                  });
            }
            info.Entries = entries;               
      }
      return info;
}      
VB.NET
<OperationContract> _
Public Function FetchEventLogEntries(ByVal skip As Integer, ByVal take As Integer) As TotalInfo
       Dim info As New TotalInfo()
       Dim entries As New List(Of EventEntry)()
 
       Using log As New EventLog("Application")
             info.Total = log.Entries.Count
                  Dim query = log.Entries.Cast(Of EventLogEntry)().OrderByDescending(Function(o) o.TimeWritten).Skip(skip).Take(take)
 
                  For Each item In query
                        entries.Add(New EventEntry() With {.Message = item.Message})
                  Next item
                  info.Entries = entries
       End Using
       Return info
End Function
In the code above one method is decorated with the OperationContract attribute. This means that it is a public method that can be consumed by the Silverlight application. The method takes two arguments, skip and take. We use these two values in the LINQ query to page through the event log. The code uses the EventLog class which is in System.Diagnostics namespace. This gives you access to the Windows event log. I am using LINQ to query the event log and return an ordered list of event log entries and limit the amount of entries that are returned by the Skip and Take methods.  
That’s the server code taken care of. Next is to build a Silverlight XAML file to display the data. Open the Silverlight project and view the Page.xaml file. Add the following XAML to the file:
<Grid x:Name="LayoutRoot" Background="Azure" HorizontalAlignment="Left" Width="Auto">
        <StackPanel Width="800" Height="800">
            <ListBox x:Name="lstEvent" Width="750" Height="500" Margin="10" BorderThickness="2" BorderBrush="Black">
                <ListBox.ItemTemplate>
                    <DataTemplate>                       
                        <Border BorderBrush="BurlyWood" BorderThickness="1" CornerRadius="4">
                            <TextBlock Text="{Binding Message}" x:Name="txtMessage" Width="710" TextWrapping="Wrap" />   
                        </Border>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
           
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <ComboBox MaxDropDownHeight="150" UseLayoutRounding="True" x:Name="cboPaging" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Width="50" Height="25" SelectionChanged="cboPaging_SelectionChanged"></ComboBox>               
            </StackPanel>           
        </StackPanel>
    </Grid>
In the above code, I am going to display the event log entry in a ListBoxItemTemplate. ItemTemplate’s are a great way to change the layout of controls. For this example I am inserting a TextBlock with a Border control surrounding it to give it a snappier appearance that just a line of text. The TextBlock text property is using Binding to display the Message property as the text.   Paging through the event log entries will be managed by the ComboBox. The ComboBox will display a list of pages to page through. But before we can go any further, we need to add a Reference to the WCF service we created earlier. Right click on the Silverlight project and choose Add Service Reference. The service reference dialog will appear. Click the Discover button and Visual Studio will automatically locate WCF services in the solution:
Add Service Reference
Now that we have the reference, we can go ahead and add code to the Page.xaml file. Open the Page.xaml.cs file and add the following code:
C#
private int PageSize
{
get;
      set;
}
       
public Page()
{
      InitializeComponent();
      PageSize = 10;
      FetchData(0);           
}
VB.NET
 
Private privatePageSize As Integer
Private Property PageSize() As Integer
      Get
            Return privatePageSize
      End Get
      Set(ByVal value As Integer)
            privatePageSize = value
      End Set
End Property
 
Public Sub New()
       InitializeComponent()
       PageSize = 10
       FetchData(0)
End Sub
 
The PageSize property sets the maximum number of records to display at any time.   The FetchData method requires one argument. This argument will be used in the LINQ query in the ECF service to determine where to start retrieving records. The PageSize value limits the number of rows returned. Add the following FetchData method:
 
C#
 
private void FetchData(int start)
{
ServiceReference1.ViewLogServiceClient client = new EventLogViewerApp.ServiceReference1.ViewLogServiceClient();
client.FetchEventLogEntriesCompleted += new EventHandler<FetchEventLogEntriesCompletedEventArgs>(client_FetchEventLogEntriesCompleted);
      client.FetchEventLogEntriesAsync(start, PageSize);
}
 
VB.NET
 
Private Sub FetchData(ByVal start As Integer)
Dim client As ServiceReference1.ViewLogServiceClient = New EventLogViewerApp.ServiceReference1.ViewLogServiceClient()
AddHandler client.FetchEventLogEntriesCompleted, AddressOf client_FetchEventLogEntriesCompleted
       client.FetchEventLogEntriesAsync(start, PageSize)
End Sub
 
Silverlight performs calls to WCF services asynchronously. In the code above, we are creating a new event handler for the FetchEventLogEntriesCompleted event.   Add the following code for the event handler:
 
C#
 
void client_FetchEventLogEntriesCompleted(object sender, EventLogViewerApp.ServiceReference1.FetchEventLogEntriesCompletedEventArgs e)
{
lstEvent.ItemsSource = e.Result.Entries;
      if (cboPaging.Items.Count == 0)
      {
            for (int i = 1; i < (e.Result.Total / PageSize) + 1; i++)
            {
                  cboPaging.Items.Add(i);
            }
}           
}
 
VB.NET
 
Private Sub client_FetchEventLogEntriesCompleted(ByVal sender As Object, ByVal e As EventLogViewerApp.ServiceReference1.FetchEventLogEntriesCompletedEventArgs)
       lstEvent.ItemsSource = e.Result.Entries
       If cboPaging.Items.Count = 0 Then
             For i As Integer = 1 To (e.Result.Total / PageSize)
                        cboPaging.Items.Add(i)
             Next i
       End If
End Sub
 
The e.Result will return the TotalInfo class which we defined as the return type. Binding the data to the ListBox is by settings the ItemsSource property. To add paging functionality, the TotalInfo class also returns a Total property, which is the count of all the records in the event log. I use this total and divide by the value in the PageSize property to determine the number of pages the user can choose from, but still the only data being displayed is the number of records in the PageSize property. Very efficient!
 
Finally you need to add the following code to handle the event when the user pages through the data:
 
C#
 
private void cboPaging_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int i = (int)cboPaging.SelectedItem;
 
      int start = 0;
      if (i > 1)
      {
            start = (i * PageSize) - PageSize;
}
      FetchData(start);
}
 
 
VB.NET
 
Private Sub cboPaging_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
       Dim i As Integer = CInt(Fix(cboPaging.SelectedItem))
 
       Dim start As Integer = 0
       If i > 1 Then
             start = (i * PageSize) - PageSize
       End If
       FetchData(start)
End Sub
 
 
In the code above, first we page currently selected in the ComboBox. If it is equal to one, then the records selected from the WCF will start at zero and return ten records. If it is greater than one, it determines what the starting page is based on their selection. Run the project and you’ll be able to page through entries in your computers event log.  Cool huh!
 
As I mentioned at the beginning of this article, I am a huge fan of efficient code, and LINQ’s built-in paging functionality is a great way to write efficient paging code with ease. The source code of this article can be downloaded from here.
 
If you liked the article,  Subscribe to the RSS Feed or Subscribe Via Email  
Malcolm Sheridan is an independent contractor who has been working with Microsoft technologies since VB4. Malcolm has worked with .NET since its inception and thoroughly enjoys ASP.NET. 
 
Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Malcolm Sheridan is a Microsoft awarded MVP in ASP.NET, a Telerik Insider and a regular presenter at conferences and user groups throughout Australia and New Zealand. Being an ASP.NET guy, his focus is on web technologies and has been for the past 10 years. He loves working with ASP.NET MVC these days and also loves getting his hands dirty with jQuery and JavaScript. He also writes technical articles on ASP.NET for SitePoint and other various websites. Follow him on twitter @malcolmsheridan


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Paul Hienner on Friday, April 3, 2009 2:28 AM
10 on 10 for this one.
Comment posted by Malcolm Sheridan on Monday, April 6, 2009 7:27 AM
@Paul Hienner
Thanks for the positive feedback!

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel