Telerik blogs

This is the last part of the series on building WP7 ToDo application with Telerik WP7 Control toolkit. Please refer to the "master" blog post for more details and links for the other part of the series.

In this article I will show you how to implement a real life LOB scenario for WP7. In our latest Telerik WP7 ToDo application we have a lot of tasks which should be persisted on the device. We are using SterlingDB to persist and retrieve the data to/from the IsolatedStorage and RadJumpList to group and display the data.

SterlingDB is an open source, well documented and high performance database – it is available on CodePlex. The author of the DB – Jeremy Likness prepared a thorough guide about how to work with the database – I suggest you to read each article very well to get the ideas and main concepts about its usage. The guide is available online here - http://www.sterlingdatabase.com/.

The single most important thing we learned while using the SterlingDB, which you should consider when architecting your application, is to use the Sterling Indexes instead of the actual business objects. Getting the entire business objects from the IsolatedStorage is an expensive operation, while working with in-memory indexes is very fast.
In our To-Do application we have the following UI that should be implemented:

We needed to show a list of tasks grouped by due date. We have a very simple data model – only Task and Project entities. The only relation between them is that Projects can contain multiple Tasks. Here is the model diagram below:

 

In our UI we can have a lot of Tasks displayed in the JumpList – in extreme scenarios users can have up-to 1000 tasks created. If we get all these 1000 tasks at once users will need to wait a lot until all the data is retrieved from the IsolatedStorage and the UI containers for the data created. The good news here is that RadJumpList combined with the SterlingDB gives you the optimal performance.

In RadJumpList we have UI virtualization implemented, so that the UI containers will not be created until not needed. In SterlingDB there is implementation for lazy load of database entities. By using these features properly we can achieve load and display of the information only when needed. Then we can scale to any number of items displayed in the list.

Let’s see how the things look like in code. First we need to register our database. Here is the code that shows the database declaration:

public class ToDoDatabase : BaseDatabaseInstance
{
  /// <summary>
  ///     The name of the database instance
  /// </summary>
  public override string Name
  {
   get { return "ToDo Database"; }
  }
  
  /// <summary>
  ///     Method called from the constructor to register tables
  /// </summary>
  /// <returns>The list of tables for the database</returns>
  protected override List<ITableDefinition> _RegisterTables()
  {
   return new List<ITableDefinition>
                       {
       CreateTableDefinition<Project, int>(fg => fg.Id)
             .WithIndex<Project, int, int>("Project_Status", fg => fg.StatusId)
       CreateTableDefinition<Task, int>(fg => fg.Id)
             .WithIndex<Task, DateTime, bool, int>("Task_DueDate_IsCompleted", task => Tuple.Create(task.DueDate.HasValue ? task.DueDate.Value : DateTime.MaxValue, task.IsCompleted))
          .WithIndex<Task, int, bool, int>("Task_ProjectId_IsCompleted", task => Tuple.Create(task.ProjectId, task.IsCompleted))
       CreateTableDefinition<TaskCategory, int>(fg => fg.Id)
                       };
  }
}

 

As you can see we have two tables – one that holds the Projects and one that holds the Tasks. Apart from this we have defined the needed indexes for our needs. Each index defines the fields that are needed and each index has a name. In our scenario to show the tasks list for a given DueDate we are using the “Task_DueDate_IsCompleted” index. Here is the query that will return all items that have DueDate in the future and are not completed:

TasksList.ItemsSource = (from k in SterlingService.Current.Database.Query<Task, DateTime, bool, int>("Task_DueDate_IsCompleted")
          where k.Index.Item1 >= DateTime.Today.Date
          where k.Index.Item2 == false
          orderby k.Index.Item1 ascending
          select k);

 

As you can see in the query we are specifying the index Name that we want to use and then by using the Index Tuple we can filter and sort the data. No data retrieval is done here! We are querying only the indexes and this is done very fast even for large data.

Next – we want to sort and group the Tasks by DueDate. This operation is handled entirely from the RadJumpList. We have a custom data engine built for WP7 which supports sorting, filtering and grouping over the data. First we will specify the SortDescriptor:

// Tasks are sorted by DueDate by default. We are using Sterling Indexes so that the operations are fast and not values are get from database.
TasksList.SortDescriptors.Add(new GenericSortDescriptor<TableIndex<Task, Tuple<DateTime, bool>, int>, DateTime>(task => task.Index.Item1));

 

Item1 property from the Index points to the DueDate property of the actual Task object. Remember we are still working with indexes only – we don’t have the actual Task retrieved from the database yet!

Next to group the data we are using GroupDescriptor:

// Tasks are grouped by DueDate and here we create custom groups based on the app logic.
TasksList.GroupDescriptors.Add(new GenericGroupDescriptor<TableIndex<Task, Tuple<DateTime, bool>, int>, string>(delegate(TableIndex<Task, Tuple<DateTime, bool>, int> tableIndex)
{
 if (tableIndex.Index.Item1.Date == DateTime.Now.Date)
 {
  return "TODAY";
 }
 else if (tableIndex.Index.Item1.Date == DateTime.Now.Date.AddDays(1))
 {
  return "TOMORROW";
}
    else
    {
     return "NEXT";
    }
   }));

 

Again here we are using the Item1 property to query the DueDate and to set the item into the correct group.

After this is done – our data is prepared and there is nothing left but to visualize the information. Here is how the XAML looks like:

<telerikDataControls:RadJumpList GroupHeaderTemplate="{StaticResource JumpListGroupHeaderTemplate}" ItemTemplate="{StaticResource TaskDetailsTemplate}" IsAsyncBalanceEnabled="True" x:Name="TasksList"/>

 

Here is the ItemTemplate (simplified for the sake of the blog post):

<DataTemplate x:Key="TaskDetailsTemplate">
<TextBlock Text="{Binding LazyValue.Value.Name}" Style="{StaticResource PhoneTextLargeStyle}"/>
</DataTemplate>

This is the only place where we are using the LazyValue.Value property. This is the place where the real value retrieval from database is done. But as I mentioned before this will happen only when the DataTemplate is actually used – e.g. only when the UI container for this business object should be displayed. This is done only when the object should be displayed in the ViewPort of the JumpList (or a little earlier if we must be exact, because we have buffers where we are pre-fetching the containers to enable smooth scrolling on the phone).

As you can see from the example above – combining RadJumpList with SterlingDB for WP7 gives you all you need for a real-life LOB scenario – you can filter/sort the data by using the queries in SterlingDB and you can group the data by using the data engine used internally in RadJumpList.

In this blog post I’m not giving the full setup of the Database because I want to get your attention on how to retrieve and display the data with the best possible performance. The setup of the database requires also SterlingService, Triggers, etc. All the code of the application is available for free in the download section of your Telerik account.


About the Author

Valio Stoychev

Valentin Stoychev (@ValioStoychev) for long has been part of Telerik and worked on almost every UI suite that came out of Telerik. Valio now works as a Product Manager and strives to make every customer a successful customer.

 

Comments

Comments are disabled in preview mode.