Descargar archivos desde las celdas de un control DataGrid de Silverlight (I)

Durante el desarrollo de una aplicación para entorno Web, uno de los requerimientos con que nos podemos encontrar consiste en ofrecer al usuario la posibilidad de realizar la descarga de un archivo al seleccionar o hacer clic en uno de los controles que forman nuestra página, habitualmente un vínculo construido mediante la etiqueta <a> de HTML junto a su atributo href.


Sin embargo en esta ocasión vamos a ir un paso más allá, trasladando el escenario a una página Silverlight que contenga un control DataGrid; dicho control deberá ofrecer una columna cuyas celdas contengan imágenes en las que al hacer clic, darán al usuario la posibilidad de descargar un archivo a su máquina local.


 


El escenario a desarrollar


Supongamos que una agencia de viajes quiere ofrecer en su portal Web una página con su catálogo de destinos, de forma que los usuarios puedan visualizar la oferta disponible, seleccionar un destino, y descargar un archivo conteniendo el folleto en formato electrónico con la información sobre el destino elegido.


Para desarrollar esta característica podemos utilizar una página Web con contenido Silverlight, donde a través de un control DataGrid ofreceremos esta información que se encuentra contenida en la base de datos de la agencia, dentro de una tabla con la siguiente estructura.


CREATE TABLE [dbo].[Viajes](
[IDViaje] [bigint] NULL,
[Nombre] [varchar](50) NULL,
[FechaAlta] [datetime] NULL,
[Importe] [decimal](9, 4) NULL,
[Folleto] [varchar](100) NULL,
[Bandera] [varchar](100) NULL
) ON [PRIMARY]

 


En la siguiente imagen podemos ver una muestra de los registros contenidos en esta tabla.



 


Creación y configuración del entorno de datos


Comenzaremos por crear desde Visual Studio 2008 un nuevo proyecto de tipo Silverlight, donde estableceremos -desde el proyecto Web que compone la solución-en la ventana Server Explorer una conexión con la base de datos SQL Server AgenciaViajes, a fin de poder acceder a la tabla Viajes.



 


Los siguientes pasos para la creación de la fuente de datos utilizada por el DataGrid ya fueron explicados en este enlace, aunque a continuación los repasaremos brevemente.


Mediante la opción de menú de Visual Studio Project > Add New Item, añadiremos al proyecto Web de la solución un elemento LINQ to SQL Classes, con lo que obtendremos un contexto de datos al que llamaremos DCAgenciaViajes; la propiedad Serialization Mode del contexto de datos deberá tener el valor Unidirectional.


Arrastrando la tabla Viajes desde la ventana Server Explorer hasta el diseñador del contexto de datos, será creada una entidad de datos con el nombre Viaje, que representará los registros de la tabla Viajes, y nos servirá para poder transferir la información desde la fuente de datos hasta el control Silverlight como veremos próximamente.


También agregaremos al proyecto un servicio Web WCF con su correspondiente interfaz, donde escribiremos un método que devuelva, mediante LINQ, el contenido de la tabla Viajes a través de una colección genérica List<Viaje>.


[ServiceContract]
public interface IWSDatos
{
[OperationContract]
List<Viaje> ObtenerViajes();
}
//—————————————
public class WSDatos : IWSDatos
{
#region IWSDatos Members

public List<Viaje> ObtenerViajes()
{
DCAgenciaViajesDataContext dcAgenciaViajes = new DCAgenciaViajesDataContext();

var lstLista = from tblViajes
in dcAgenciaViajes.Viajes
select tblViajes;

return lstLista.ToList();
}

#endregion
}


 


Para recuperar dicha colección estableceremos en el proyecto Silverlight una referencia hacia el servicio WCF, y desde el evento de carga de la página XAML crearemos una instancia del servicio, a través de la cual haremos una llamada a su método ObtenerViajes, el cual nos devolverá la colección en el evento de finalización de la llamada. Esta colección será asignada a la propiedad ItemsSource del control DataGrid, que la utilizará para visualizarla en formato tabular.


public partial class Page : UserControl
{
public Page()
{
InitializeComponent();

this.Loaded += new RoutedEventHandler(Page_Loaded);
}

void Page_Loaded(object sender, RoutedEventArgs e)
{
RefWSDatos.WSDatosClient wsDatos = new DescargaArchivosCeldasDataGridSL_CS.RefWSDatos.WSDatosClient();
wsDatos.ObtenerViajesCompleted += new EventHandler<DescargaArchivosCeldasDataGridSL_CS.RefWSDatos.ObtenerViajesCompletedEventArgs>(wsDatos_ObtenerViajesCompleted);
wsDatos.ObtenerViajesAsync();
}

void wsDatos_ObtenerViajesCompleted(object sender, DescargaArchivosCeldasDataGridSL_CS.RefWSDatos.ObtenerViajesCompletedEventArgs e)
{
this.grdDatos.ItemsSource = e.Result;
}
}


 


Comenzando a desarrollar la interfaz de usuario. Presentación básica


Pasemos ahora a la fase de creación de la interfaz de usuario, que como ya hemos mencionado estará compuesta principalmente por un control DataGrid, cuyo proceso de creación y puesta a punto iremos refinando progresivamente, a fin de que el lector pueda apreciar con mejor detalle las diferentes fases de construcción llevadas a cabo.


El modo más simple de presentación de los datos asignados al control en la propiedad ItemsSource consiste en establecer el valor True en su propiedad AutoGenerateColumns.


<my:DataGrid x:Name=»grdDatos»
Width=»575″ Height=»225″
AutoGenerateColumns=»True»
IsReadOnly=»True»>
<!–….–>
<!–….–>
</my:DataGrid>

 


Esta  acción visualizará la información con sus valores originales.



 


Creación manual de columnas


Para comenzar con la personalización de las columnas asignaremos el valor False a la propiedad AutoGenerateColumns, esto supone que tendremos que crear manualmente cada columna que necesitemos visualizar, lo cual haremos dentro de la colección Columns del control, utilizando alguna de las clases derivadas de DataGridBoundColumn: DataGridTextColumn para mostrar valores textuales, y DataGridCheckBoxColumn para mostrar valores lógicos a través de una casilla de marcado.


Nota. Dentro del contexto del presente artículo, emplearemos el término «campo» para referirnos a las columnas o campos de la tabla de la base de datos que vamos a visualizar en el DataGrid.


La forma de indicar a un objeto columna el elemento – campo- de la fuente de datos que debe mostrar, pasa por asignar a la propiedad DisplayMemberBinding una expresión de marcado extendida con la información del enlace a establecer utilizando el formato «{Binding NombreCampo}». El siguiente código fuente define varias columnas del origen de datos mediante objetos DataGridTextColumn; como podemos observar, no es necesario crear todas las columnas disponibles en la fuente de datos, sino simplemente aquellas que necesitemos.


<my:DataGrid x:Name=»grdDatos»
Width=»575″ Height=»225″
AutoGenerateColumns=»False»
IsReadOnly=»True»>

<my:DataGrid.Columns>
<my:DataGridTextColumn Header=»Código» DisplayMemberBinding=»{Binding IDViaje}» />
<my:DataGridTextColumn Header=»Descripción» DisplayMemberBinding=»{Binding Nombre}» />
<my:DataGridTextColumn Header=»Creado» DisplayMemberBinding=»{Binding FechaAlta}» />
</my:DataGrid.Columns>

</my:DataGrid>


 


La siguiente imagen muestra el resultado obtenido.



 


Convertidores de tipo. Dando forma a los datos


Antes de entrar a resolver el objetivo principal de este artículo: la descarga de archivos desde el control de cuadrícula, observemos un notable inconveniente en la presentación de los datos de tipo fecha, consistente en que dicha información se muestra sin formato alguno, lo cual también es totalmente lógico, puesto que el control desconoce a priori el estilo de formato que queremos aplicar.


La técnica a seguir para establecer un formato personalizado en una columna del DataGrid consiste en la creación de un convertidor de tipo, el cual aplicaremos en aquel punto del código XAML donde se establezca el enlace a datos de la columna; a partir de ese momento, el dato enviado por el origen para ser mostrado en el grid, pasará primeramente por el convertidor, que lo «moldeará» según nuestras necesidades antes de mandarlo a la celda.


Para ello tenemos que añadir al proyecto Silverlight una clase que implemente la interfaz IValueConverter, que se compone de los métodos Convert y ConvertBack. En el primero escribiremos el código para formatear el valor que recibimos del origen de datos tal y como queremos visualizarlo en el control, mientras que en el segundo deberemos devolver el valor formateado al tipo original. Dado que en nuestro ejemplo no vamos a editar las celdas del DataGrid, en el método ConvertBack sencillamente retornaremos null.


using System;
using System.Windows.Data;
using System.Threading;
//….
public class FechaConvertidor:IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DateTime dtFecha = (DateTime)value;
return dtFecha.ToString(«dd – MMMM – yyyy», culture);
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}

#endregion
}


 


El modo de utilizar este convertidor en nuestro código XAML pasa por declarar un espacio de nombres que haga referencia al ensamblado en el que reside la clase del convertidor -en este caso, el mismo ensamblado del proyecto Silverlight. Como nombre para el espacio de nombres utilizaremos la palabra «propio», tal y como vemos en la siguiente figura.



 


A continuación, dentro del apartado de recursos de la página, declararemos un nuevo recurso, que partiendo del espacio de nombres que acabamos de declarar, accederá a la clase del convertidor. También le asignaremos mediante el atributo Key, una clave para su posterior utilización.


<UserControl.Resources>
<propio:FechaConvertidor x:Key=»cnvFecha» />
</UserControl.Resources>

 


Tanto en el momento de declarar el espacio de nombres como al crear el recurso contaremos con la asistencia de Intellisense, que nos evitará tener que escribir el valor, guiándonos además en su selección, como vemos en la siguiente figura.



En el caso de que Intellisense no ofrezca la lista de selección automáticamente, podemos invocarla pulsando la combinación Ctrl + J.


Finalmente, en la definición de la columna que muestra la fecha ampliaremos la expresión de marcado que habíamos asignado a la propiedad DisplayMemberBinding, incluyendo, mediante la propiedad Converter del enlace a datos -objeto Binding-una referencia hacia el convertidor de tipo que hemos declarado como recurso, utilizando la palabra clave StaticResource. La sintaxis empleada tendrá el siguiente formato: {Binding NombreCampo, Converter = {StaticResource ClaveRecurso}}.


<my:DataGrid.Columns>
<!–….–>
<my:DataGridTextColumn
Header=»Creado»
DisplayMemberBinding=»{Binding FechaAlta, Converter={StaticResource cnvFecha}}» />
<!–….–>
</my:DataGrid.Columns>

 


La siguiente imagen muestra el formato resultante sobre la columna de fecha.



 


No obstante seguimos observando una pequeña pega: el nombre de los meses está en inglés, dado que en el método FechaConvertidor.Convert utilizamos el objeto CultureInfo que este manipulador de evento recibe como parámetro. Para evitar tal inconveniente, lo que vamos a hacer es utilizar la información cultural del sistema que obtenemos de la hebra de ejecución de nuestra aplicación -Thread.CurrentThread.CurrentCulture- en forma de objeto CultureInfo. Sustituyendo este objeto por el que estábamos utilizando como segundo parámetro del método DateTime.ToString habremos solucionado el problema.


public class FechaConvertidor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DateTime dtFecha = (DateTime)value;
return dtFecha.ToString(«dd – MMMM – yyyy», Thread.CurrentThread.CurrentCulture);
}
//….
}

 


Como vemos en la siguiente imagen, las fechas ahora sí que muestran el valor deseado.



 


Y llegados a este punto concluimos la primera parte de este artículo, en la siguiente entrega abordaremos algunos aspectos adicionales sobre los convertidores de tipo, así como la prometida funcionalidad de la columna para realizar la descarga de archivos a través de sus celdas. Para todos aquellos que quieran probar el ejemplo al completo, en los enlaces C# y VB se encuentran los proyectos para los respectivos lenguajes.


Un saludo.

4 Comentarios

  1. anonymous

    Un articulo impresionante.
    Muy bien redactado y sobretodo muy util.
    Enhorabuena.
    Un saludo.

  2. lmblanco

    Hola Juan Pablo, cuanto tiempo!! 😎

    Gracias por leer el artículo, y me alegra mucho que te haya gustado. Estoy dando los últimos retoques a la segunda entrega, espero que también te sea de utilidad.

    Un saludo.
    Luismi

  3. lmblanco

    Hola Enrique

    Muchas gracias por tu interés, a ver que tal la segunda parte 😉

    Un saludo.
    Luismi

  4. lmblanco

    Hola espinete

    Existe una gran cantidad de recursos para comenzar a dar nuestros primeros pasos tanto con Silverlight como con WPF; podemos empezar por ejemplo por un par de publicaciones periodicas en donde podemos encontrar mensualmente algún que otro artículo sobre estos temas:

    http://www.dotnetmania.com/

    http://msdn.microsoft.com/en-us/magazine/default.aspx

    Si nos centramos en Silverlight, de visita obligada es la página oficial de Silverlight, donde tienes un buen montón de tutoriales, videos demostrativos, casos reales de sitios desarrollados con Silverlight, etc.

    http://silverlight.net/

    También tenemos los blogs de conocidos evangelistas en la materia como Jesse Liberty, Tim Sneath, Scott Guthrie.

    http://silverlight.net/blogs/jesseliberty/default.aspx

    http://weblogs.asp.net/scottgu/pages/silverlight-posts.aspx

    http://blogs.msdn.com/tims/

    En el siguiente enlace también tienes un post que publiqué en mi blog sobre el orden de instalación de herramientas necesarias para desarrollar con Silverlight, aunque con la aparición de las últimas versiones, algunas cuestiones estén un poco obsoletas 😉

    http://geeks.ms/blogs/lmblanco/archive/2008/04/05/orden-de-instalaci-243-n-de-herramientas-para-desarrollo-con-silverlight-2-0-actualizado-el-post-de-enlaces-para-silverlight.aspx

    En cuanto a libros, uno muy recomendable es «Silverlight 2 Visual Essentials» de Matthew MacDonald

    Pasando a WPF también tenemos algunos blogs entre los que destaca el de Ian Griffiths, coautor junto a Chris Sells del primer libro publicado sobre el tema (cuando todavía estaba bajo el codename Avalon).

    http://www.interact-sw.co.uk/iangblog/

    http://www.thewpfblog.com/

    Respecto a libros hay muchísimos y es complicado elegir, pero aquí te paso algunos títulos que creo son bastante recomendables:

    Programming WPF (Chris Sells y Ian Griffiths)

    Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation (Charles Petzold)

    Windows Presentation Foundation Unleashed (Adam Nathan)

    Cuaderno Técnico de dotNetManía nº7 «Windows Presentation Foundation» (Grupo Weboo)

    Bueno, pues creo que con todo este material tienes bastante para dar los primeros pasos 8-).

    Espero que te resulte de utilidad.

    Un saludo.
    Luismi

Responder a Cancelar respuesta

Tema creado por Anders Norén