viernes, 27 de julio de 2012

Diferencias entre DataContext y ItemsSource en Metro

Para aquellos que no tengamos demasiada experiencia en WPF o Silverlight el bindeo de datos puede ser un poco confuso teniendo en cuenta que parece que se puede hacer de dos maneras distintas, tanto a través de la propiedad DataContext como de la propiedad ItemsSource. Pese a que puedan parecer propiedades idénticas nada más lejos de la realidad ya que ambas propiedades tienen un propósito bien distinto.

DataContext es una propiedad general de todos los descendientes de FrameworkElement. Es heredada a de padres a hijos y puede ser usada como origen de datos para el DataBinding.

ItemsSource es una propiedad que identifica la generación de elementos de los controles que derivan de ItemsControl. Cuando establecemos esta propiedad a través de DataBinding o vía código el control generará los elementos internamente. Estableciendo la propiedad DataContext en un ItemsControls no provocaremos el mismo efecto.

Bien, pero como no sólo de teoría vive el desarrollador vamos a ver esto en un ejemplo. Imaginemos que tenemos esta clase
public class MenuModel
{
    public int IdMenu { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}
Y que inicializamos una lista de elementos de la siguiente manera
List<MenuModel> items = new List<MenuModel>();
items.Add(new MenuModel() { IdMenu = 1, Name = "Elemento 1", Description = "Descripción del elemento 1" });
items.Add(new MenuModel() { IdMenu = 2, Name = "Elemento 2", Description = "Descripción del elemento 2" });
Bien con esto podremos hacer lo siguiente sobre un GridView
GridViewMenu.ItemsSource = items;
O sea, aplicaremos como origen de datos la colección de elementos única y exclusivamente al GridView. El resultado será este

GridView

Bien, ahora cambiaremos la llamada a ItemsSource por DataContext y veremos que ocurre. Correcto, lo que ocurre es que no se ha generado el GridView y eso porque el ItemsSource si lanza la generación del control pero el DataContext no. Para que se genere el control debemos modificar el DataBind del GridView  de la siguiente manera
<GridView x:Name="GridViewMenu"
            HorizontalAlignment="Left"
            Margin="19,70,0,0"
            Grid.Row="1"
            VerticalAlignment="Top"
            SelectionMode="None"
            IsItemClickEnabled="True"
            ItemTemplate="{StaticResource itemTemplate}"
            ItemsSource="{Binding}">

Bien, pero con DataContext podemos hacer algunas cosas más como hemos comentado antes. Vamos a crear un modelo que tenga nuestra lista de elemento y un texto y vamos a ver como podemos hacer el bindeo de datos de una manera muy simple.
public class MainPageModel
{
    public string Titulo { get; set; }
    public List<MenuModel> Items { get; set; }

    public MainPageModel()
    {
        this.Titulo = "Mi título";

        Items = new List<MenuModel>();
        Items.Add(new MenuModel() { IdMenu = 1, Name = "Elemento 1", Description = "Descripción del elemento 1" });
        Items.Add(new MenuModel() { IdMenu = 2, Name = "Elemento 2", Description = "Descripción del elemento 2" });
    }
}
Como hemos dicho antes DataContext es una propiedad que se programa por toda la jerarquía de hijos, así que la asignaremos al DataContext de la página
this.DataContext = model;
Y en el diseñador pondremos también un TextBox de la siguiente manera
<TextBlock HorizontalAlignment="Left" Height="24" Margin="18,16,0,0" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Titulo}" FontSize="24" VerticalAlignment="Top" Width="339"/>
<GridView x:Name="GridViewMenu"
            HorizontalAlignment="Left"
            Margin="19,70,0,0"
            Grid.Row="1"
            VerticalAlignment="Top"
            SelectionMode="None"
            IsItemClickEnabled="True"
            ItemTemplate="{StaticResource itemTemplate}"
            ItemsSource="{Binding Items}">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
El resultado de esto es el siguiente

GridView

En lineas generales podemos decir que usaremos ItemsSource si solo vamos a enlazar un ItemsControl con un único origen de datos y si tenemos varios elementos que van consumir nuestro origen de datos usaremos DataContext.

Nota: En el fichero StandardStyles.xaml tengo definida la siguiente Template para definir es aspecto del GridView
<!-- DataTemplates -->
<DataTemplate x:Key="itemTemplate">
    <Grid Background="DarkGray" Width="250" Height="250">
        <StackPanel Orientation="Vertical" VerticalAlignment="Bottom" Margin="0,0,0,10">
            <TextBlock Text="{Binding Name}" 
                    FontSize="24"  Margin="10,0,0,0" 
                    TextTrimming="WordEllipsis" TextWrapping="Wrap" 
                    HorizontalAlignment="Left"/>
            <TextBlock Text="{Binding Description}" 
                    FontSize="12" Margin="10,0,0,0" 
                    TextTrimming="WordEllipsis" TextWrapping="Wrap" 
                    HorizontalAlignment="Left"/>
        </StackPanel>
    </Grid>
</DataTemplate>

Happy coding!

No hay comentarios:

Publicar un comentario