Dot-Net

接收從網頁拖動到 WPF 視窗的圖像

  • June 27, 2020

我希望我的 WPF 應用程序成為放置目標,並且我希望能夠從任何網頁拖動圖像。

當從網頁中拖動圖像時,顯然它是“DragImageBits”格式,可以反序列化為ShDragImage類型。(有關我如何定義它,請參閱問題的底部)

如何將其轉換為 WPF 圖像?

這是我目前的嘗試。(如果有人知道進行反序列化的正確方法,我會全神貫注)

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

在這一點上,我得到了一個類型異常,System.Runtime.InteropServices.ExternalException消息只是Generic GDI+ error.

有誰知道我應該做什麼?


這是支持的類定義。我從這個部落格條目中複製了它們。

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

這是我學到的:

“DragImageBits” 由 windows shell 提供,僅用於拖動游標,不適用於最終數據。外殼通過調整大小和透明度將圖像轉換為適當的拖動游標。

例如,如果您拖動此圖像:

完全不透明的圖像

SHDRAGIMAGE 將呈現如下:

在此處輸入圖像描述

如果你真的想從 SHDRAGIMAGE 中提取圖像,這裡是程式碼。(部分摘自此答案

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

如果您想將 DragImageBits 用於其預期目的(作為拖動圖像),請參閱*Shell Style Drag and Drop in .NET(WPF 和 WinForms)* (在此處存檔)以獲得簡單的可下載範例。


因此,“DragImageBits”幾乎分散了實際問題的注意力,即接受從網頁拖動的圖像。 從網頁中拖動圖像變得複雜,因為 Firefox、Chrome 和 IE9 都為您提供了一組不同的格式。此外,您希望同時處理圖像和圖像超連結,它們的處理方式再次不同。

Google 和 Firefox 提供“text/html”格式,它為您提供單個 HTML 元素作為圖像。Google 將其作為 ASCII 字元串提供給您,而 Firefox 將其作為 unicode 字元串提供給您。所以這是我編寫的處理它的程式碼:

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

在這種情況下,正則表達式將同時處理直接圖像和圖像超連結。

我的SetImageFromUri功能:

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

對於 IE9,您可以處理“FileDrop”格式。這在 IE9 中執行良好。鉻不支持它。Firefox 確實支持它,但會將圖像轉換為點陣圖並將透明像素轉換為黑色。因此,如果“text.html”格式不可用,您應該只處理“FileDrop”格式。

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

如果從 IE9 中拖動圖像超連結,則不提供“FileDrop”格式。我還沒有弄清楚如何將圖像從 IE9 中的圖像超連結拖到我的圖像控制項上。


額外的資訊 如果您正在使用此範例,但仍需要將此二進制數據轉換為圖像,這裡有一個有用的程式碼片段:

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

引用自:https://stackoverflow.com/questions/8442085