Cargando datos dinámicamente en el TreeView del Toolkit de Silverlight

agosto 2, 2009
By

El Silverlight Toolkit es una colección de controles fascinante que facilitan considerablemente la creación de aplicaciones de negocios (LOB – Line Of Business Applications / Data-Centric Applications). Sin embargo hay un control en particular que tiene un comportamiento básico que debería ser mejorado en versiones posteriores (Estoy utilizando el Silverlight Toolkit July 2009 Preview). Hablo del TreeView. El problema consiste en que cuando vinculamos el control a una fuente jerárquica de datos, hay que descargar toda la jerarquía hacia el cliente para poder representar el control. Si esta fuente de datos fuese considerablemente grande, podría introducir un problema de rendimiento en nuestra aplicación. Comercialmente, la compañía Telerik brinda un control TreeView que permite cargar los datos por demanda mientras que el usuario navega por la jerarquía, pero… ¿Con el TreeView del Silverlight Toolkit no hay nada que podamos hacer? Claro que si.

Primero, veamos el diseño de la fuente de datos que vamos a utilizar en el ejemplo.

image

Como pueden observar, es una tabla muy sencilla donde cada nodo del árbol se identifica por el campo ID y la jerarquía se representa a través de una referencia al nodo padre (IDPadre). El campo Valor digamos que es el texto que queremos visualizar en cada nodo del TreeView.

No voy a detenerme en como crear toda la plomería para el acceso a datos en Silverlight ya que existen muy buenos artículos al respecto. Solo voy a comentar que utilizo Linq to SQL en la aplicación Web y para la comunicación entre la aplicación Silverlight y la aplicación Web estoy utilizando los .NET Ria Services July 2009 Preview.

En la capa de negocios (aquella que conecta la capa de datos con la capa de presentación) tendría el siguiente código:

[EnableClientAccess()]
public class ArbolDomainService : LinqToSqlDomainService<ArbolSqlDataContext>
{

    public IQueryable<Arbol> GetForest()
    {
        return this.Context.Arbols.Where(tree=>tree.IDPadre==null);
    }

    public IQueryable<Arbol> GetChildrenByParentId(int IDPadre)
    {
        return this.Context.Arbols.Where(tree=>tree.IDPadre==IDPadre);
    }

    /**/
}

Note que hemos creado un Domain Service utilizando los .NET Ria Services. El método GetForest() devuelve todos los elementos que tengan el campo IDPadre en null. Estos elementos serían las raíces del bosque que queremos representar en el TreeView.

El método GetChildrenByParentId(int IDPadre) devuelve todos los elementos que tengan el campo IDPadre igual al valor pasado por parámetro. Esto garantiza devolver dado un nodo del árbol todos sus hijos.

El código declarativo (XAML) para la creación del TreeView sería el siguiente:

<controls:TreeView x:Name="miTreeView"></controls:TreeView>

Note que en la página o control de usuario deberá declarar el siguiente espacio de nombre para que el código anterior cobre sentido:

xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"

El kit del truco para la carga de datos por demanda está en ir rellenando el TreeView de forma programática y perezosa (Lazy) como se muestra a continuación:

public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            miTreeView.Loaded += new RoutedEventHandler(miTreeView_Loaded);
        }

        void miTreeView_Loaded(object sender, RoutedEventArgs e)
        {
            var context = new ArbolDomainContext();
            context.Load<Arbol>(context.GetForestQuery(),
                result =>
                {
                    foreach (var nodo in result.Entities)
                    {
                        var tvItem = new TreeViewItem();
                        tvItem.Tag = nodo.ID;
                        tvItem.Header = nodo.Valor;
                        tvItem.Expanded += new RoutedEventHandler(tvItem_Expanded);
                        tvItem.Items.Add(new TreeViewItem());
                        miTreeView.Items.Add(tvItem);
                    }
                }, null);
        }

Como se puede observar, solamente se cargan las raíces del bosque y por cada una de ellas se realizan dos operaciones significativas:

  • Se registra el manejador tvItem_Expanded para controlar cuando el usuario desee expandir el nodo y se guarda en el Tag del nodo el valor del ID.
  • Se añade como hijo un “Nodo Tonto” que es necesario para que el TreeView muestre el control de expandir/contraer para cada uno de los elementos agregados. Si este paso no se realiza pues no se podrán expandir nodos.

El código del manejador  tvItem_Expanded sería el siguiente:

void tvItem_Expanded(object sender, RoutedEventArgs e)
{
    var tvParentItem = (TreeViewItem)sender;

    var context = new ArbolDomainContext();
    context.Load<Arbol>(context.GetChildrenByParentIdQuery((int)tvParentItem.Tag),
        result =>
        {
            tvParentItem.Items.Clear();
            foreach (var nodo in result.Entities)
            {
                var tvItem = new TreeViewItem();
                tvItem.Tag = nodo.ID;
                tvItem.Header = nodo.Valor;
                tvItem.Expanded += new RoutedEventHandler(tvItem_Expanded);
                tvItem.Items.Add(new TreeViewItem());
                tvParentItem.Items.Add(tvItem);
            }
        }, null);
}

Note que es básicamente lo mismo, salvo que se utiliza el método context.GetChildrenByParentIdQuery, el ID se obtiene del Tag del nodo Padre y el nodo tonto que se había agregado se elimina.

De esta forma se cargan dinámicamente los datos que se van a representar en el TreeView y el usuario podrá navegar por grandes colecciones jerárquicas de una forma más eficiente.

Tags: , , , ,

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Acerca del autor...

Alejandro Tamayo

Web: http://www.linkedin.com/in/atamayocastillo
Alejandro Tamayo
Professor, Researcher, Developer, Consultant and technology enthusiast. Master of Science (MSc) in Computer Science and member of Weboo Research Group.Leer completo