接收從網頁拖動到 WPF 視窗的圖像
我希望我的 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(); }

