2011-12-09 12 views
11

Chcę, aby moja aplikacja WPF była celem upuszczania i chcę mieć możliwość przeciągania obrazu z dowolnej strony internetowej.Odbieranie obrazu przeciągniętego ze strony sieci Web do okna WPF

Kiedy obraz jest przeciągany ze strony internetowej, widocznie znajduje się on w formacie "DragImageBits", który można przekształcić do postaci ShDragImage. (Zobacz podsumowanie tego, jak je zdefiniowałem)

Jak przekonwertować to na obraz WPF?

Oto moja obecna próba. (Jeśli ktoś wie jak poprawnie wykonaj desirialization, jestem wszystkich uszy)

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); 

W tym momencie pojawia się wyjątek typu System.Runtime.InteropServices.ExternalException z komunikatu po prostu będąc .

Czy ktoś wie, co powinienem robić?


A oto definicje klas pomocniczych. Skopiowałem je z 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; 
    } 

Odpowiedz

15

Oto czego się nauczyłem:

„DragImageBits” jest dostarczana przez okna powłoki i jest przeznaczona tylko do przeciągania kursora, a nie do ostatecznych danych. Powłoka przekształca obraz na odpowiedni kursor przeciągania poprzez zmianę rozmiaru i przezroczystość.

Na przykład, jeśli przeciągnąć ten obraz:

Fully Opaque Image

SHDRAGIMAGE odda jak to:

enter image description here

Jeśli naprawdę chcesz, aby wyodrębnić obraz z SHDRAGIMAGE, tutaj jest kod. (Częściowo zniesione od 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); 

Jeśli chcesz wykorzystać DragImageBits za to zamierzonego celu (jako obraz drag), patrz this blog na prosty przykład, do pobrania.


Tak więc, "DragImageBits" było dość odciągające od faktycznego problemu, jakim jest zaakceptowanie obrazu przeciągniętego ze strony internetowej.

Przeciągnięcie obrazu ze strony internetowej staje się skomplikowane, ponieważ Firefox, Chrome i IE9 oferują cały zestaw różnych formatów. Ponadto, chcesz obsłużyć zarówno obraz, jak i hiperłącze obrazu, i są one traktowane inaczej.

Google i Firefox udostępniają format "text/html", który daje pojedynczy element HTML jako obraz. Google podaje je jako ciąg znaków ASCII, a Firefox przekazuje je jako ciąg znaków Unicode. Oto kod, który napisałem, aby go obsłużyć:

 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); 
      } 
     } 

W tym przypadku wyrażenie regularne będzie obsługiwać zarówno prosty obraz, jak i hiperłącze obrazu.

A moja SetImageFromUri funkcja:

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); 
    } 

Dla IE9 można obsługiwać format "FileDrop". Działa to dobrze w IE9. Chrome go nie obsługuje. Firefox obsługuje go, ale konwertuje obraz na bitmapę i konwertuje przezroczyste piksele na czerń. Z tego powodu należy obsługiwać format "FileDrop", jeśli format "text.html" nie jest dostępny.

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; 
     } 
    } 

Format „FileDrop” jeśli przeciągnąć hiperłącza obrazu z IE9 nie jest przewidziane. Nie wymyśliłem, jak przeciągnąć obraz z hiperłącza do obrazu w IE9 na moją kontrolę obrazu.


Extra Info

Jeśli używasz tego przykładu, ale nadal trzeba przekonwertować ten danych binarnych na obraz, oto przydatny fragment kodu:

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

Mam ten sam problem - odkryłem, że zarówno Firefox, jak i IE9 udostępniają również format niezależnej od urządzenia bitmapy (DIB). W przypadku, gdy jest to dostępne, nie ma potrzeby używania programu WebClient do pobrania pliku, ponieważ mamy dostęp do pliku w znanym formacie (bitmapowym). Firefox wyświetla również format "FileDrop", ale w przypadku parsowania html i używania formatu filedrop, możliwe jest pobieranie obiektów, które niekoniecznie muszą być obrazami. – funseiki

+3

To mi bardzo pomaga. Dzięki za publikację. Wyrażenia regularne nie pasują do wielu poprawnych linków graficznych. Znalazłem @ "] + src =" "([^" "] *)" "" działa trochę lepiej. –

+0

Jesteś prawdziwym ratownikiem. Jeśli inni ludzie, tacy jak ja, chcą przeciągać obrazy prosto z obrazów google, sprawdź w UriString, czy zaczyna się od "danych", ciągi te zawierają już dane i nie powinny być pobierane (z [to pytanie] (http : //stackoverflow.com/q/25748858/3410196)) –

Powiązane problemy