Como implementar TemplateSelector en el ListBox de Windows Phone 7

Si solo has trabajando con Silverlight nunca has conocido el TemplateSelector de WPF, que como su nombre indica permite hacer un selector por discriminador para las plantillas de datos. En el caso que nos atañe ListBox, tiene una propiedad llamada ItemTemplate en la que se establece la plantilla de datos para cada uno de los ítems.

¿Para qué se puede querer cambiar la plantilla?

Imaginaros el escenario de estar haciendo una aplicación para mostrar una lista de noticias provenientes de un rss, podemos tener una plantilla para las noticias con imágenes, otra plantilla para las noticias sin imágenes y además podemos querer una plantilla especial para una noticia destacada. Este tipo de escenario que es el más común es bastante difícil de conseguir con Silverlight puesto que no tiene TemplateSelector.

Para conseguir esta funcionalidad tenemos que implementarlo a mano.

Hay varias maneras de llegar hasta esta aproximación la que desde mi punto de vista es la más adecuada es crear un ListBox personalizado, porque nos permite tener toda la funcionalidad y aspecto existente del ListBox pero con el selector de plantillas.

El proceso de creación de un ListBox personalizado se hace en dos partes, la primera se tiene que hacer una clase que herede de ListBox. En esta clase, que llamaremos DataTemplateListBox, tenemos que sobrescribir dos métodos virtuales:

  • GetContainerForItemOverride: este método devuelve un objeto que será el ítem container que el ListBox usará para alojar los ítems que se establezcan en el ItemSource. En el caso del ListBox, la implementación predeterminada de este método devuelve un ListBoxItem que es el contenedor predeterminado del ListBox. En nuestro ejemplo tenemos que generar un ListBoxItem personalizado que tendrá la funcionalidad de establecer el DataTemplate.
  • PrepareContainerForItemOverride: Este método es llamado cuando el ListBox está a punto de empezar a hacer la pasada de Layout, y es el momento justo para establecer el código de nuestros discriminador. Este método acepta dos parámetros, uno llamado element de tipo DependencyObject que es el contenedor del ListBox, y el otro ítem de tipo object que es el objeto que nosotros estamos estableciendo al ListBox.
public class DataTemplateListBox : ListBox
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new DataTemplateListBoxItem();
    }
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);
        DataTemplateListBoxItem lbi = (DataTemplateListBoxItem)element;
        if (item is News)
        {
            News newItem = (News)item;
            if (!newItem.IsHighLighted)
            {
                lbi.CustomTemplate = (DataTemplate)App.Current.Resources["NewsDataTemplate"];
            }
            else
            {
                lbi.CustomTemplate = (DataTemplate)App.Current.Resources["HighLightedNewsDataTemplate"];
            }
        }
    }
}

Como hemos dicho anteriormente en el proceso de creación del ListBox tenemos que crear también un ListBoxItem que será el contenedor neutro que tendrá una propiedad de tipo DataTemplate, que será el DataTemplate usado para dibujar ese elemento con una plantilla especifica.

[TemplatePart(Name = "DisplayContent", Type = typeof(ContentControl))]
public class DataTemplateListBoxItem : ListBoxItem
{
    #region Properties
    
    public DataTemplate CustomTemplate
    {
        get { return (DataTemplate)GetValue(CustomTemplateProperty); }
        set { SetValue(CustomTemplateProperty, value); }
    }

    public static readonly DependencyProperty CustomTemplateProperty =
        DependencyProperty.Register(
            "CustomTemplate", 
            typeof(DataTemplate), 
            typeof(DataTemplateListBoxItem), 
            new PropertyMetadata(null));

    #endregion

    #region Constructors
    public CategoryItemsListBoxItem()
    {
        DefaultStyleKey = typeof(DataTemplateListBoxItem);
    }
    #endregion
}

Este es el código de ejemplo de un DataTemplateListBoxItem, pero también necesitamos generar un estilo predeterminado que contenga un elemento de tipo ContentControl que será el elemento que tendrá la interfaz del usuario del elemento.

<Style TargetType="controls:DataTemplateListBoxItem" >
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="controls:DataTemplateListBoxItem">
                <ContentControl x:Name="DisplayContent" Content="{TemplateBinding DataContext}" 
                                ContentTemplate="{TemplateBinding CustomTemplate}"  />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Como se puede apreciar en el código xaml lo que se hace es agregar a la plantilla un ControlTemplate y en ese ControlTemplate se establecen dos propiedades Content con un binding de plantilla (TemplateBinding) a la propiedad DataContext, con eso conseguirnos establecer el objeto como contenido y que se dibuje. La otra propiedad que falta por establecer es justamente la propiedad ContentTemplate que en este caso se hace lo mismo, hacer un binding de plantilla con la propiedad CustomTemplate que es de la clase DataTemplateListBoxItem.

Ahora lo que tenemos que hacer es insertar nuestro DataTemplateListBox en el control MainPage y enlazarle un ViewModel con datos para mostrar un par de noticias generadas por código:

<Grid x:Name="ContentPanel" Grid.Row="1">
   <DataTemplateDemo_Controls:DataTemplateListBox ItemsSource="{Binding Items}"/>
</Grid>

Con eso conseguimos tener un ListBox en el nosotros decidimos en cada uno de los elementos como queremos aplicarle una plantilla de datos. Este es el resultado:

image

La demo no es muy impresionante en sí, pero permite tener la flexibilidad de poder elegir elemento por elemento cual es la plantilla que vamos a usar de manera programática.

La demo completa la podéis descargar de aquí.

Luis Guerrero.

2 comentarios sobre “Como implementar TemplateSelector en el ListBox de Windows Phone 7”

Deja un comentario

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