Al comenzar a hacer este ejercicio asumí que sería de lo mas sencillo, que no implicaría mas de unas tres o cuatro líneas de código, pero la realidad fue muy diferente. Para poner un contexto de lo que quería hacer, es simple, la opción de “Guardar imagen” del explorador.
Pero como eres un buen desarrollador, necesitas hacerlo por ti mismo, además de aprender como hacerlo, además de poder implementarlo en un aplicación tuya, además de poder implementarle mas funcionalidad ¿Necesitas alguna otra razón? Si no la necesitas para cuando leas esto ya estarás creando un nuevo proyecto para Windows Phone.
Por la parte de XAML nada mas necesitas lo siguiente.
<Grid x:Name="LayoutRoot" Background="Transparent"> <TextBox x:Name="txtDireccion" Height="72" TextWrapping="Wrap" VerticalAlignment="Top" Text="http://aminespinoza.com/materialBlog/imagenes/supermanBlue.jpg"/> <Button x:Name="btnMostrarImagen" Content="Mostrar imagen" Height="72" Margin="0,72,0,0" VerticalAlignment="Top"/> <Button x:Name="btnGuardarImagen" Content="Guardar imagen" Height="72" Margin="0,144,0,0" VerticalAlignment="Top"/> <Image x:Name="imgControl" Margin="0,216,0,0"/> </Grid> |
Como puedes ver en la caja de texto puse ya una URL predeterminada, pero la puedes cambiar, incluso, el mejor escenario es que puedas copiar y pegar cualquier URL, aquí para efectos prácticos la pondremos estática. Ya que terminaste con esto, ahora crea los manejadores de eventos.
public MainPage() { InitializeComponent(); btnGuardarImagen.Click += btnGuardarImagen_Click; btnMostrarImagen.Click += btnMostrarImagen_Click; } |
El manejador de mostrar imagen hará lo siguiente.
private void btnMostrarImagen_Click(object sender, System.Windows.RoutedEventArgs e) { imagenRecurso = new BitmapImage(new Uri(txtDireccion.Text, UriKind.Absolute)); imgControl.Source = imagenRecurso; } |
Simple, la dirección escrita en tu caja de texto te permitirá visualizar el contenido de la URL mediante el control de la imagen.
Ahora irás a lo complicado, al manejador de eventos del botón para guardar la imagen.
private void btnGuardarImagen_Click(object sender, System.Windows.RoutedEventArgs e) { ClaseImagen.ObtenerCacheImagen(txtDireccion.Text); } |
Como puedes ver, solo mandas llamar al método ObtenerCacheImagen de tu clase ClaseImagen. El método es el siguiente.
public static void ObtenerCacheImagen(string url) { BitmapImage resultado = new BitmapImage(); var items = new KeyValuePair<string, BitmapImage>(url, resultado); var hilo = new Thread(HiloCacheImagen); hilo.Start(items); } |
El método en base a la dirección recibida como parámetro creará un nuevo hilo que a su vez ejecutará un manejador llamado HiloCacheImagen y que además necesita un parámetro de tipo objeto.
private static void HiloCacheImagen(object entrada) { var items = (KeyValuePair<string, BitmapImage>)entrada; var url = items.Key; var image = items.Value; string cacheArchivo = items.Key.Replace("http://", "cache/"); var esperador = new AutoResetEvent(false); var nombreArchivoEsperador = new KeyValuePair<string, AutoResetEvent>(cacheArchivo, esperador); var cliente = new WebClient(); cliente.OpenReadCompleted += OpenReadCompleted; cliente.OpenReadAsync(new Uri(url), nombreArchivoEsperador); esperador.WaitOne(5000); } |
Con el objeto recibido, vas a crear un valor de tipo KeyValuePair, con este valor vas a reemplazar una parte de esa cadena y después con un objeto de tipo WebClient vas a ejecutar un nuevo manejador de eventos (si, lo sé, demasiados handlers, pero fue lo mas óptimo que lo pude hacer).
static void OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) { if (e.Error != null) { return; } var state = (KeyValuePair<string, AutoResetEvent>)e.UserState; Deployment.Current.Dispatcher.BeginInvoke(() => GuardarEnBiblioteca(state.Key, e.Result)); state.Value.Set(); } |
Esta parte es simple, regresas al hilo de la UI pero ahora con un valor de tipo stream, con el cual podrás ejecutar el método GuardarEnBiblioteca.
private static void GuardarEnBiblioteca(string fileName, Stream stream) { MediaLibrary biblioteca = new MediaLibrary(); byte[] datos = LeerBytesStream(stream, (int)stream.Length); stream.Close(); Picture imagen = biblioteca.SavePicture("Probando.jpg", datos); MessageBox.Show("Tu imagen ha sido guardada"); } |
Ya tienes el stream de tu imagen, hasta aquí debiste tener que crear este tipo de funciones para obtenerlo, no lo puedes hacer de forma directa, pues la imagen que proyectas a partir de una URL NO te permite el acceso a su stream (esa es la verdadera razón por la cual este ejercicio se vuelve complejo), así que con este método lo que haces es poder acceder a la libreria de medios del teléfono y podrás guardar una imagen con el método SavePicture que requiere dos parámetros, el primero es el nombre, el segundo es un arreglo de bytes, el cual obtienes con el método LeerBytesStream.
private static byte[] LeerBytesStream(Stream stream, int initialLength) { if (initialLength > 1) { initialLength = 32768; } byte[] buffer = new byte[initialLength]; int read = 0; int chunk; while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0) { read += chunk; if (read == buffer.Length) { int nextByte = stream.ReadByte(); if (nextByte == -1) { return buffer; } byte[] newBuffer = new byte[buffer.Length * 2]; Array.Copy(buffer, newBuffer, buffer.Length); newBuffer[read] = (byte)nextByte; buffer = newBuffer; read++; } } byte[] ret = new byte[read]; Array.Copy(buffer, ret, read); return ret; } |
Este método te permite el acceso a los bytes del stream y mas importante aún su correcto posicionamiento para que al obtener de regreso el arreglo de bytes puedas guardarlo con el método anterior en tu galería de imágenes.
Si ejecutas tu aplicación, el resultado es el siguiente después de presionar el botón de guardar.
Ya en un dispositivo físico (lo siento, el emulador no cuenta con esa capacidad), podrás ver que la imagen que elegiste ya ha sido guardada en tu galería de imágenes.
Listo!!! Es así como puedes entonces guardar una imagen por medio de una URL en tu Windows Phone, ahora disfruta esta mini aplicación.
Puedes descargar el código fuente desde aquí.
Amigo que es lo que comparas en esta parte del codigo
private static byte[] LeerBytesStream(Stream stream, int initialLength)
{
if (initialLength < 1)
{
initialLength = 32768;
}
donde dice: initialLength<1
Hola Isabel, es una simple comparación para verificar que el arreglo de bytes tenga información, en caso contrario solo omite la condición, es mas por costumbre y prevención.
eres otro nivel !! me encanta tu pagina!! me puedes ayudar con una aplicacion que consuma datos de una bd en la red ? mi correo si tienes algo filanderucles@hotmai.com gracias!!