2011-12-09 16 views
11

Quiero que mi aplicación WPF sea un destino de bajada, y quiero poder arrastrar una imagen desde cualquier página web.Recibir una imagen arrastrada de la página web a la ventana de WPF

Cuando se arrastra una imagen desde una página web, aparentemente está en el formato "DragImageBits", que se puede deserializar para escribir ShDragImage. (Consulte la parte inferior de la pregunta para ver cómo lo he definido)

¿Cómo convierto esto en una imagen WPF?

Aquí está mi intento actual. (Si alguien sabe la forma correcta de hacer el desirialization, soy todo oídos)

private void UserControl_Drop(object sender, System.Windows.DragEventArgs e) 
    { 

      string[] formats = data.GetFormats(); 

      // DragImageBits 
      if (formats.Contains("DragImageBits")) 
      { 
      MemoryStream imageStream = data.GetData("DragImageBits") as MemoryStream; 

      // Now I'm deserializing this, the only way I know how 
      imageStream.Seek(0, SeekOrigin.Begin); 
      BinaryReader br = new BinaryReader(imageStream); 

      ShDragImage shDragImage; 
      shDragImage.sizeDragImage.cx = br.ReadInt32(); 
      shDragImage.sizeDragImage.cy = br.ReadInt32(); 
      shDragImage.ptOffset.x = br.ReadInt32(); 
      shDragImage.ptOffset.y = br.ReadInt32(); 
      shDragImage.hbmpDragImage = new IntPtr(br.ReadInt32()); 
      shDragImage.crColorKey = br.ReadInt32(); 


      var systemDrawingBitmap = System.Drawing.Bitmap.FromHbitmap(shDragImage.hbmpDragImage); 

En este punto me sale una excepción de tipo System.Runtime.InteropServices.ExternalException, con el mensaje simple hecho de estar .

¿Alguien sabe lo que debería hacer?


Y aquí están las definiciones de las clases de soporte. Los copié desde this blog entry.

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 
    public struct Win32Point 
    { 
     public int x; 
     public int y; 
    } 

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 
    public struct Win32Size 
    { 
     public int cx; 
     public int cy; 
    } 

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 
    public struct ShDragImage 
    { 
     public Win32Size sizeDragImage; 
     public Win32Point ptOffset; 
     public IntPtr hbmpDragImage; 
     public int crColorKey; 
    } 

Respuesta

15

Esto es lo que he aprendido:

"DragImageBits" es proporcionada por el shell de Windows, y está destinado sólo para el cursor de arrastre, no para los datos finales. El shell transforma la imagen en un cursor de arrastre apropiado a través del redimensionamiento y la transparencia.

Por ejemplo, si arrastra esta imagen:

Fully Opaque Image

El SHDRAGIMAGE rendirá como esto:

enter image description here

Si realmente desea extraer la imagen de la SHDRAGIMAGE, aquí está el código. (Parcialmente levantado de this answer)

 MemoryStream imageStream = data.GetData("DragImageBits") as MemoryStream; 
     imageStream.Seek(0, SeekOrigin.Begin); 
     BinaryReader br = new BinaryReader(imageStream); 
     ShDragImage shDragImage; 
     shDragImage.sizeDragImage.cx = br.ReadInt32(); 
     shDragImage.sizeDragImage.cy = br.ReadInt32(); 
     shDragImage.ptOffset.x = br.ReadInt32(); 
     shDragImage.ptOffset.y = br.ReadInt32(); 
     shDragImage.hbmpDragImage = new IntPtr(br.ReadInt32()); // I do not know what this is for! 
     shDragImage.crColorKey = br.ReadInt32(); 
     int stride = shDragImage.sizeDragImage.cx * 4; 
     var imageData = new byte[stride * shDragImage.sizeDragImage.cy]; 
     // We must read the image data as a loop, so it's in a flipped format 
     for (int i = (shDragImage.sizeDragImage.cy - 1) * stride; i >= 0; i -= stride) 
     { 
      br.Read(imageData, i, stride); 
     } 
     var bitmapSource = BitmapSource.Create(shDragImage.sizeDragImage.cx, shDragImage.sizeDragImage.cy, 
                96, 96, 
                PixelFormats.Bgra32, 
                null, 
                imageData, 
                stride); 

Si desea utilizar los DragImageBits para la finalidad a que se destina (imagen como un lastre), ver this blog para un ejemplo sencillo, descargable.


Por lo tanto, el "DragImageBits" fue más o menos una distracción del problema real, que es aceptar una imagen arrastrada de una página web.

Arrastrar una imagen desde una página web se vuelve complicado, porque Firefox, Chrome e IE9 le brindan un conjunto diferente de formatos. Además, desea manejar tanto una imagen como un hipervínculo de imagen, y estos se tratan de manera diferente nuevamente.

Google y Firefox proporcionan un formato "text/html", que le proporciona un único elemento HTML como imagen. Google te lo da como una cadena ASCII, y Firefox te lo da como una cadena Unicode. Así que aquí está el código que escribí para manejarlo:

 System.Windows.IDataObject data = e.Data; 
     string[] formats = data.GetFormats(); 


     if (formats.Contains("text/html")) 
     { 

      var obj = data.GetData("text/html"); 
      string html = string.Empty; 
      if (obj is string) 
      { 
       html = (string)obj; 
      } 
      else if (obj is MemoryStream) 
      { 
       MemoryStream ms = (MemoryStream)obj; 
       byte[] buffer = new byte[ms.Length]; 
       ms.Read(buffer, 0, (int)ms.Length); 
       if (buffer[1] == (byte)0) // Detecting unicode 
       { 
        html = System.Text.Encoding.Unicode.GetString(buffer); 
       } 
       else 
       { 
        html = System.Text.Encoding.ASCII.GetString(buffer); 
       } 
      } 
      // Using a regex to parse HTML, but JUST FOR THIS EXAMPLE :-) 
      var match = new Regex(@"<img[^/]src=""([^""]*)""").Match(html); 
      if (match.Success) 
      { 
       Uri uri = new Uri(match.Groups[1].Value); 
       SetImageFromUri(uri); 
      } 
     } 

En este caso, la expresión regular se encargará tanto una imagen recta y un hipervínculo imagen.

Y mi SetImageFromUri función:

private void SetImageFromUri(Uri uri) 
    { 
     string fileName = System.IO.Path.GetTempFileName(); 
     using (WebClient webClient = new WebClient()) 
     { 
      webClient.DownloadFile(uri, fileName); 
     } 
     using (FileStream fs = File.OpenRead(fileName)) 
     { 
      byte[] imageData = new byte[fs.Length]; 
      fs.Read(imageData, 0, (int)fs.Length); 
      this.ImageBinary = imageData; 
     } 
     File.Delete(fileName); 
    } 

Para IE9 puede manejar el formato "FileDrop". Esto funciona bien en IE9. Chrome no lo admite. Firefox sí lo admite, pero convierte la imagen en un mapa de bits y convierte los píxeles transparentes en negro. Por esta razón, solo debe manejar el formato "FileDrop" si el formato "text.html" no está disponible.

else if (formats.Contains("FileDrop")) 
    { 
     var filePaths = (string[])data.GetData("FileDrop"); 
     using (var fileStream = File.OpenRead(filePaths[0])) 
     { 
      var buffer = new byte[fileStream.Length]; 
      fileStream.Read(buffer, 0, (int)fileStream.Length); 
      this.ImageBinary = buffer; 
     } 
    } 

El formato "FileDrop" no se proporciona si se arrastra un hipervínculo de imagen de IE9. No he descubierto cómo arrastrar una imagen desde un hipervínculo de imagen en IE9 al control de mi imagen.


Extra Info

Si utiliza este ejemplo, pero aún es necesario convertir estos datos binarios en una imagen, aquí es un fragmento de código útil:

   BitmapImage sourceImage = new BitmapImage(); 
       using (MemoryStream ms = new MemoryStream(imageBinary)) 
       { 
        sourceImage.BeginInit(); 
        sourceImage.CacheOption = BitmapCacheOption.OnLoad; 
        sourceImage.StreamSource = ms; 
        sourceImage.EndInit(); 
       } 
+0

estoy teniendo el mismo problema: descubrí que tanto Firefox como IE9 también ofrecen un formato de mapa de bits independiente del dispositivo (DIB). En el caso de que esté disponible, no es necesario usar WebClient para descargar el archivo, ya que tenemos acceso al archivo en un formato familiar (mapa de bits). Firefox también expone un formato 'FileDrop', pero en el caso de analizar el html y usar el formato de archivo de archivo, es posible descargar objetos que pueden no ser necesariamente imágenes – funseiki

+3

Esto me ayuda mucho. Gracias por publicar. La expresión regular no puede coincidir con muchos enlaces de imagen válidos. He encontrado @ "] + src =" "([^" "] *)" "" funciona un poco mejor. –

+0

Eres un verdadero salvavidas. Si otras personas como yo quieren arrastrar imágenes directamente desde las imágenes de Google, compruebe el UriString si comienza con '" datos "', esas cadenas ya contienen los datos y no deben descargarse (de [esta pregunta] (http : //stackoverflow.com/q/25748858/3410196)) –

Cuestiones relacionadas