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

En la primera entrega de este artículo iniciamos el desarrollo de una página Silverlight que incluía un control DataGrid, cuya principal característica debía consistir en la posibilidad de descargar archivos asociados a las celdas de una columna.


En esa primera parte se trataron aspectos tales como la creación del contexto de datos y el servicio WCF que proveía al DataGrid de la información a mostrar. También vimos la creación manual de columnas junto a los convertidores de tipo, elementos estos fundamentales en el proceso de formatear el dato original antes de ser presentado por el control.


Esta segunda entrega hará hincapié una vez más en el tema de los convertidores y explicará, tal y como habíamos prometido, los detalles relacionados con la columna para la descarga de archivos.


 


Un convertidor para tipos numéricos


La siguiente columna sobre la que tenemos que aplicar un formato es la que muestra el campo Importe. Se trata de valores numéricos que necesitamos visualizar con los separadores decimales y de millar, por lo que la operativa a seguir es prácticamente igual que la descrita para la columna de fecha: crear un convertidor de tipo, declararlo como recurso dentro del código XAML, e incluirlo en la definición de la columna del DataGrid, como vemos en los siguientes fragmentos de código.


public class NumeroConvertidor : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
decimal nNumero = (decimal)value;
string sNumero = nNumero.ToString(«#,#.00», Thread.CurrentThread.CurrentCulture);
return sNumero;
}

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

#endregion
}


 


<UserControl.Resources>
<!–….–>
<propio:NumeroConvertidor x:Key=»cnvNumero» />
</UserControl.Resources>

<!–….–>

<my:DataGrid.Columns>
<!–….–>
<my:DataGridTextColumn
Header=»Tarifa»
DisplayMemberBinding=»{Binding Importe, Converter={StaticResource cnvNumero}}» />
<!–….–>
</my:DataGrid.Columns>


 


En la siguiente imagen vemos la nueva columna numérica, formateada según acabamos de indicar.



 


Al observar el resultado en ejecución, el único inconveniente que en principio podemos achacar a esta columna radica en que sus valores no se encuentran alineados a la derecha, como sería lo deseable al tratarse de una columna de contenido numérico. Dado que entre las propiedades de la clase DataGridTextColumn no encontramos ninguna que nos permita realizar la mencionada alineación, debemos buscar otro medio para resolver el problema.


La solución en este caso la encontramos recurriendo al uso de una columna de tipo plantilla -clase DataGridTemplateColumn-la cual nos ofrece un alto grado de personalización ante el dato a mostrar.


Una vez añadida al código XAML la etiqueta correspondiente a este tipo de columna, dentro de la misma situaremos la plantilla de celda -propiedad/etiqueta CellTemplate-en cuyo interior añadiremos una plantilla de datos -etiqueta DataTemplate-, que será la que contenga aquellos controles que nos servirán para ajustar con mayor precisión el valor a mostrar.


El control en cuestión que incluiremos dentro del DataTemplate será un TextBlock sobre el que estableceremos la adecuada alinación de contenido mediante sus propiedades Margin, HorizontalAligment y VerticalAlignment.


<my:DataGrid.Columns>
<!–….–>
<my:DataGridTemplateColumn Header=»Tarifa»>
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text=»{Binding Importe, Converter={StaticResource cnvNumero}}»
Margin=»0,0,10,0″
HorizontalAlignment=»Right»
VerticalAlignment=»Center» />
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
</my:DataGridTemplateColumn>
<!–….–>
</my:DataGrid.Columns>

 


A partir de este momento, la columna se mostrará con la alineación que queríamos.



 


Creando la columna para descargar archivos


Y llegamos a la parte del ejemplo en la que crearemos una columna que nos permitirá realizar la descarga de archivos. Como habíamos comentado al comienzo del artículo, esta columna debería mostrar una imagen, en la que al hacer clic, se realice la descarga del correspondiente archivo.


Vamos a asumir que el servidor Web de la agencia de viajes contiene los archivos a descargar en la siguiente ruta:


http://localhost/viajes/folletos/


Mientras que los archivos de imagen están en esta otra ruta:


http://localhost/viajes/paises/


Resulta obvio deducir, que dada la información y comportamiento que debe ofrecer esta columna, tendremos que implementarla utilizando un objeto DataGridTemplateColumn, debido a su mayor nivel de flexibilidad.


En lo que respecta a la funcionalidad de descarga de archivos, recurriremos al uso de un control HyperlinkButton, en cuya propiedad NavigateUri asignaremos la ruta del archivo a descargar, mientras que la propiedad Content nos permitirá mostrar el literal en forma de vínculo sobre el cual, cuando el usuario haga clic, provocará el comienzo de la descarga. Sin embargo, si enlazamos estas propiedades con la fuente de datos mediante un objeto Binding de la siguiente forma.


<my:DataGrid.Columns>
<!–….–>
<my:DataGridTemplateColumn Header=»Folleto»>
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<HyperlinkButton NavigateUri=»{Binding Folleto}» Content=»{Binding Folleto}» />
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
</my:DataGridTemplateColumn>
<!–….–>
</my:DataGrid.Columns>

 


En la celda veremos el nombre del archivo, pero al hacer clic en él, no se producirá descarga alguna debido a que el vínculo no está correctamente construido; para colmo de males, el texto de dicho nombre de archivo tampoco se encontrará correctamente alineado dentro de la celda.



 


El motivo se debe a que la propiedad NavigateUri del control HyperlinkButton espera recibir un tipo Uri, y lo que nosotros le estamos pasando actualmente es una cadena simple, que ni tan siquiera contiene una ruta válida.


Puesto que conocemos la ruta en la que residen los archivos y el nombre del archivo seleccionado nos lo proporciona la fuente de datos a través de un objeto Binding, un modo de construir el vínculo pasaría, de nuevo, por recurrir a un convertidor de tipo que realice esta operación, lo cual vuelve a demostrarnos la potencia que este elemento tiene en la adaptación de los valores a visualizar dentro de la interfaz de usuario.


public class UriConvertidor : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Uri oUri = new Uri(«http://localhost/viajes/folletos/» + value.ToString());
return oUri;
}

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

#endregion
}


 


<UserControl.Resources>
<!–….–>
<propio:UriConvertidor x:Key=»cnvUri» />
</UserControl.Resources>

<my:DataGrid.Columns>
<!–….–>
<my:DataGridTemplateColumn Header=»Folleto»>
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<HyperlinkButton NavigateUri=»{Binding Folleto, Converter={StaticResource cnvUri}}»
Content=»{Binding Folleto}» />
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
</my:DataGridTemplateColumn>
<!–….–>
</my:DataGrid.Columns>


 


Tras agregar el anterior código a la aplicación, cuando a partir de ahora volvamos a hacer clic en las celdas de esta columna, se abrirá el conocido cuadro de diálogo para la descarga del archivo.



 


Añadiendo las imágenes


Para dar el toque final a este ejemplo solamente nos queda un detalle: reemplazar por imágenes el texto que en esta columna aparece en forma de vínculos.


El control HyperlinkButton, como ya hemos visto, dispone de la propiedad Content para albergar el contenido a visualizar, lo cual quiere decir que no estamos obligados a utilizar valores textuales para que el usuario interactúe con el control, sino que podemos asignarle una imagen para que reaccione ante un clic del usuario, navegando al destino especificado en la propiedad NavigateUri. El modo de establecer dicha imagen desde el código XAML pasa por utilizar la propiedad Content como una etiqueta, dentro de la cual situaremos un control Image.


<HyperlinkButton NavigateUri=»{Binding Folleto, Converter={StaticResource cnvUri}}»>
<HyperlinkButton.Content>
<!–….–>
<!–contenido–>
<!–….–>
</HyperlinkButton.Content>
</HyperlinkButton>

 


Centrando seguidamente nuestra atención sobre el control Image, su propiedad Source será la que usaremos para asignar la imagen a mostrar. El valor para dicha propiedad lo tomaremos de la fuente de datos mediante un objeto Binding como en las otras columnas, y como también ocurre con el control HyperlinkButton, debemos crear un convertidor que construya la ruta en la que se encuentra el archivo de imagen, a fin de que el control Image pueda acceder adecuadamente a ella para visualizarla.


public class RutaImagenConvertidor : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string sRuta = «http://localhost/viajes/paises/» + value.ToString();
return sRuta;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}

#endregion
}


 


<UserControl.Resources>
<!–….–>
<propio:UriConvertidor x:Key=»cnvUri» />
<propio:RutaImagenConvertidor x:Key=»cnvRutaImagen» />
</UserControl.Resources>

<my:DataGrid.Columns>
<!–….–>
<my:DataGridTemplateColumn Header=»Folleto»>
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<HyperlinkButton
NavigateUri=»{Binding Folleto, Converter={StaticResource cnvUri}}»>
<HyperlinkButton.Content>
<Image
Source=»{Binding Bandera, Converter={StaticResource cnvRutaImagen}}»
Width=»30″ Height=»30″ />
</HyperlinkButton.Content>
</HyperlinkButton>
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
</my:DataGridTemplateColumn>
<!–….–>
</my:DataGrid.Columns>


 


La siguiente imagen muestra finalmente el DataGrid resultante con toda la funcionalidad requerida.



 


Y con esto concluimos el presente artículo, en el que hemos explicado cómo añadir al control DataGrid de Silverlight una funcionalidad que no viene incorporada «de fábrica», pero que posiblemente necesitaremos implementar en diversas ocasiones sobre este control de cuadrícula. El código fuente, al igual que en la primera parte, está disponible en los enlaces C# y VB.


Espero que os resulte de utilidad.


Un saludo.

7 Comentarios

  1. anonymous

    Gracias Miguel, esta bien interesante y clara tu explicacion.

  2. lmblanco

    Hola Rafael Ángel

    Gracias a tí por leer el post.

    Un saludo,
    Luismi

  3. lmblanco

    Hola Enrique

    Muchas gracias por los ánimos, espero poder seguir posteando cosas interesantes sobre este control aunque lo del evento creo que es correr mucho 😉

    Un saludo,
    Luismi

  4. anonymous

    esta chevere

  5. lmblanco

    Hola Samuel

    Gracias por tu interés, celebro que te haya gustado.

    Un saludo.
    Luismi

  6. anonymous

    Me parecio un gran proyecto voy a usarlo seguramete en estos dias. Gracias por este y tus demas publicaciones

  7. lmblanco

    Hola Miguel Ángel

    Gracias a tí por tu interés en el blog, me alegra que te pareciera de utilidad.

    Un saludo,
    Luismi

Responder a Cancelar respuesta

Tema creado por Anders Norén