In search for an improved treeview solution – part 1.

Introduction

This post is the first in a series:

Introducing on demand loading in the LightSwitch treeview user control – part 2.

LightSwitch Treeview on demand loading with direct OData connection – part 3

There are a few LightSwitch treeview solutions around, based on the silverlight treeview control. There is even a LightSwitch treeview extension.  In this first “treeview post”, I will analyse how this treeview solution works and draw your attention to a kind of drawback of this solution when it comes to data loading. It’s not my intention to criticize this solution, but just to share with your the room for improvement. 

How does the mainstream LightSwitch treeview solution looks?

I’ll use as example structure, a treeview depicting the different departments of an organization. A department structure is inherently hierarchical.

 

Important to setup the self-relationship correctly:

 

Following silverlight usercontrol can be consumed in a LightSwitch screen:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightClassLibrary.SilverlightTreeWithLightSwitchBinding"
    mc:Ignorable="d" xmlns:local="clr-namespace:SilverlightClassLibrary"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.Resources>
        <local:EntityCollectionValueConverter x:Key="EntityCollectionValueConverter" />
        <sdk:HierarchicalDataTemplate x:Name="TreeDataTemplate"  ItemsSource="{Binding 
                        Converter={StaticResource EntityCollectionValueConverter}, 
                        ConverterParameter=ChildDepartments}">
            <TextBlock Text="{Binding DepartmentName}" />
        </sdk:HierarchicalDataTemplate>
    </UserControl.Resources>
        <Grid x:Name="LayoutRoot" Background="White">

        <sdk:TreeView ItemTemplate="{StaticResource TreeDataTemplate}" ItemsSource="{Binding Screen.GetRoot}"   >

        </sdk:TreeView>

    </Grid>
</UserControl>

As you can see, the TreeView binds to Screen.GetRoot:

The GetRoot query goes as follows:

 partial void GetRoot_PreprocessQuery(ref IQueryable<Department> query)
        {
            query = query.Where(d => d.ParentDepartment == null);
        }
and the query is bound to the datacontext of the silverlight user control:

 The most interesting part in the user control is the treeview’s itemtemplate which is an hierarchical data template:

 <sdk:HierarchicalDataTemplate x:Name="TreeDataTemplate"  ItemsSource="{Binding 
                        Converter={StaticResource EntityCollectionValueConverter}, 
                        ConverterParameter=ChildDepartments}">
            <TextBlock Text="{Binding DepartmentName}" />
        </sdk:HierarchicalDataTemplate>

The data template his no direct binding, but binds the ChildDepartments via a ValueConverter:

 public class EntityCollectionValueConverter : IValueConverter
    {
        public object Convert(object value,
           Type targetType,
           object parameter,
           System.Globalization.CultureInfo culture)
        {
            string strErrorMessage
                = "Converter parameter should be set to the property name that will serve as source of data";
            IEntityObject entity = value as IEntityObject;
            if (entity == null)
                throw new ArgumentException("The converter should be using an entity object");
            string sourcePropertyName = parameter as string;
            if (string.IsNullOrWhiteSpace(sourcePropertyName))
                throw new ArgumentException(strErrorMessage);

            var entities = new ObservableCollection<IEntityObject>();
            var temporaryEntites = new List<IEntityObject>();
            entity.Details.Dispatcher.BeginInvoke(delegate
            {
                IEntityCollection eCollection =
                    entity.Details.Properties[sourcePropertyName].Value as IEntityCollection;
                if (eCollection == null)
                {
                    Debug.Assert(false, "The property " + sourcePropertyName + " is not an entity collection");
                    return;
                }

                foreach (IEntityObject e in eCollection)
                {
                    temporaryEntites.Add(e);
                }
                Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(delegate
                {
                    foreach (IEntityObject e in temporaryEntites)
                    {
                        entities.Add(e);
                    }
                });
            });
            return entities;
        }
        public object ConvertBack(object value,
            Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Basically, this valueconverter will make sure the ChildDepartments are added in an observable collection in such a way the binding system can pick up it’s values and all this should be done on the right thread.  Although it’s not quite sure if the above is reflecting the main intention of the idea of a “value converter”‘, it’s clear that this is a very clever solution.  It works great for small trees. Nonetheless, there is a problem…

What’s the problem?

In essence, the problem with the above solution is that the valueconverter causes a very chatty communication pattern which is causing for large trees a lot of delays in the screen rendering.

We are simply loading the tree which has some 25 root elements, but the value converter is sending out 25 requests instead of just one request.

You could use fiddler here, but the trace handler (trace.axd) will reveal enough information as well:

Hmm… no good. Fidller will learn us that also the children of all root elements are already loaded. That’s crazy, because maybe we only want to go to the details of let’s say root element 5. When you open consecutive child elements, you will notice that loading becomes very slow.

What do we need?

Well… we want that when we click on a tree node

  • only the immediate children are loaded
  • the data are loaded in one request.

This is called on demand loading. I prefer in the context of a treeview “on demand loading” above “lazy loading”, because it expresses clearly what it does: loading only data when they are needed or demanded when the user clicks on a node.

How can we achieve this?

That will be the topic of my next treeview post :)