Receiving an image dragged from Web Page to WPF window Receiving an image dragged from Web Page to WPF window wpf wpf

Receiving an image dragged from Web Page to WPF window


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:

Fully Opaque Image

The SHDRAGIMAGE will render as this:

enter image description here

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