接收从Web页面拖动到WPF窗口的图像 [英] Receiving an image dragged from Web Page to WPF window

查看:164
本文介绍了接收从Web页面拖动到WPF窗口的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望我的WPF应用程序成为一个放下目标,我希望能够从任何网页拖动图像。



当图像从一个网页,显然是在DragImageBits格式,它可以反序列化为键入)

  MemoryStream imageStream = data.GetData(DragImageBits)作为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];
//我们必须以循环的形式读取图像数据,因此它的翻转格式为
(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作为预期目的(作为拖动图像),请参阅这个博客为一个简单的可下载示例。



所以,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是string)
{
html =(string)obj;
}
else if(obj是MemoryStream)
{
MemoryStream ms =(MemoryStream)obj;
byte [] buffer = new byte [ms.Length];
ms.Read(buffer,0,(int)ms.Length);
if(buffer [1] ==(byte)0)//检测unicode
{
html = System.Text.Encoding.Unicode.GetString(buffer);
}
else
{
html = System.Text.Encoding.ASCII.GetString(buffer);
}
}
//使用正则表达式来解析HTML,但JUST FOR This Example :-)
var match = new Regex(@< img [^ /] SRC = ([^ ] *) )匹配(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中运行良好。 Chrome不支持它。 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;
}
}

如果拖动,则不提供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 this blog 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();
                }

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆