接收从网页拖动到 WPF 窗口的图像

Receiving an image dragged from Web Page to WPF window(接收从网页拖动到 WPF 窗口的图像)
本文介绍了接收从网页拖动到 WPF 窗口的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我希望我的 WPF 应用程序成为放置目标,并且我希望能够从任何网页拖动图像.

从网页拖拽图片时,显然是DragImageBits"格式,可以反序列化输入)

 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());//我不知道这是干什么用的!shDragImage.crColorKey = br.ReadInt32();int stride = shDragImage.sizeDragImage.cx * 4;var imageData = new byte[stride * shDragImage.sizeDragImage.cy];//我们必须循环读取图像数据,所以它是翻转格式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,空值,图像数据,步幅);

如果您想将 DragImageBits 用于其预期目的(作为拖动图像),请参阅 Shell 样式拖放在 .NET(WPF 和 WinForms)中(存档 here) 获取一个简单的可下载示例.


因此,DragImageBits"几乎分散了实际问题的注意力,即接受从网页拖动的图像.

从网页中拖动图像变得很复杂,因为 Firefox、Chrome 和 IE9 都为您提供了一组不同的格式.此外,您希望同时处理图像和图像超链接,它们的处理方式再次不同.

Google 和 Firefox 提供了一个text/html"格式,它为您提供单个 HTML 元素作为图像.Google 将其作为 ASCII 字符串提供给您,而 Firefox 将其作为 unicode 字符串提供给您.所以这是我编写的处理它的代码:

 System.Windows.IDataObject 数据 = e.Data;string[] 格式 = data.GetFormats();if (formats.Contains("text/html")){var obj = data.GetData("text/html");字符串 html = string.Empty;if (obj 是字符串){html = (string)obj;}else if (obj 是 MemoryStream){MemoryStream ms = (MemoryStream)obj;字节[] 缓冲区 = 新字节[ms.Length];ms.Read(buffer, 0, (int)ms.Length);if (buffer[1] == (byte)0)//检测 unicode{html = System.Text.Encoding.Unicode.GetString(buffer);}别的{html = System.Text.Encoding.ASCII.GetString(buffer);}}//使用正则表达式解析 HTML,但仅用于此示例 :-)var match = new Regex(@"<img[^/]src=""([^""]*)""").Match(html);如果(匹配.成功){Uri uri = new Uri(match.Groups[1].Value);SetImageFromUri(uri);}}

在这种情况下,正则表达式将同时处理直接图像和图像超链接.

还有我的 SetImageFromUri 函数:

 private void SetImageFromUri(Uri uri){字符串文件名 = System.IO.Path.GetTempFileName();使用 (WebClient webClient = new WebClient()){webClient.DownloadFile(uri, fileName);}使用 (FileStream fs = File.OpenRead(fileName)){byte[] imageData = new byte[fs.Length];fs.Read(imageData, 0, (int)fs.Length);this.ImageBinary = imageData;}文件.删除(文件名);}

对于 IE9,您可以处理FileDrop";格式.这在 IE9 中运行良好.铬不支持它.Firefox 确实支持它,但会将图像转换为位图并将透明像素转换为黑色.因此,您应该只处理FileDrop";格式如果text.html";格式不可用.

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

FileDrop"如果从 IE9 中拖动图像超链接,则不提供格式.我还没有弄清楚如何将图像从 IE9 中的图像超链接拖到我的图像控件上.


**额外信息**

如果您正在使用此示例,但仍需要将此二进制数据转换为图像,这里有一个有用的代码片段:

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

I want my WPF application to be a drop target, and I want to be able to drag an image from any web page.

When an image is dragged from a web page, apparently it is in the "DragImageBits" format, which can be deserialized to type ShDragImage. (See the bottom of the question for how I've defined it)

How do I convert this to a WPF image?

Here's my current attempt. (If anybody knows the correct way to do the desirialization, I'm all ears)

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

At this point I get an exception of type System.Runtime.InteropServices.ExternalException, with the message simply being Generic GDI+ error.

Does anyone know what I should be doing?


And here are the supporting class definitions. I copied them from 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;
    }

解决方案

Here's what I've learnt:

"DragImageBits" is provided by the windows shell, and is meant only for the drag cursor, not for the final data. The shell transforms the image to an appropriate drag cursor through resizing and transparency.

For example, if you drag this image:

The SHDRAGIMAGE will render as this:

If you really want to extract the image from the SHDRAGIMAGE, here is the code. (Partially lifted from 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);

If you want to utilize the DragImageBits for it's intended purpose (as a drag image), see Shell Style Drag and Drop in .NET (WPF and WinForms) (archived here) for a simple, downloadable example.


So, the "DragImageBits" was pretty much a distraction from the actual problem, which is to accept an image dragged from a web page.

Dragging an image from a web page gets complicated, because Firefox, Chrome, and IE9 all give you a different set of formats. Also, you want to handle both an image and an image hyperlink, and these are treated differently again.

Google and Firefox provides a "text/html" format, which gives you a single HTML element as an image. Google gives it to you as an ASCII string, and Firefox gives it to you as a unicode string. So here's the code I wrote to handle it:

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

In this case, the regular expression will handle both a straight image and an image hyperlink.

And my SetImageFromUri function:

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

For IE9 you can handle the "FileDrop" format. This works well in IE9. Chrome does not support it. Firefox does support it, but converts the image to a bitmap and converts transparent pixels to black. For this reason, you should only handle the "FileDrop" format if the "text.html" format isn't available.

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

The "FileDrop" format is not provided if you drag an image hyperlink from IE9. I haven't figured out how to drag an image from an image hyperlink in IE9 onto my image control.


**Extra Info**

If you're using this example, but still need to convert this binary data into an image, here's a useful code snippet:

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

这篇关于接收从网页拖动到 WPF 窗口的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

本站部分内容来源互联网,如果有图片或者内容侵犯了您的权益,请联系我们,我们会在确认后第一时间进行删除!

相关文档推荐

Force JsonConvert.SerializeXmlNode to serialize node value as an Integer or a Boolean(强制 JsonConvert.SerializeXmlNode 将节点值序列化为整数或布尔值)
Using JSON to Serialize/Deserialize TimeSpan(使用 JSON 序列化/反序列化 TimeSpan)
Could not determine JSON object type for type quot;Classquot;(无法确定类型“Class的 JSON 对象类型.)
How to deserialize a JSONP response (preferably with JsonTextReader and not a string)?(如何反序列化 JSONP 响应(最好使用 JsonTextReader 而不是字符串)?)
how to de-serialize JSON data in which Timestamp it-self contains fields?(如何反序列化时间戳本身包含字段的JSON数据?)
JSON.Net custom contract serialization and Collections(JSON.Net 自定义合约序列化和集合)