WPF Drag and Drop Image Between Applications

Why does the simple stuff always trip one up? All I wanted to do was drag and drop an image between two separate WPF applications, with bonus points if I could also drag and drop bitmaps between my WPF application and another non-WPF application.

To make a long story short, one entire day completely shot making this work, and I didn't even earn the bonus points. Well, partial bonus points since drag and drop between separate applications almost always uses file names anyway (and that's trivial to deal with).

For this example assume we have a Canvas that we dragging bitmaps onto, whether those bitmaps come from the application or someplace else doesn't matter.

public partial class Window1 : Window {
    public Window1() {
        InitializeComponent();
    }

    private void DragImage(object sender, MouseButtonEventArgs e) {
        var image = e.Source as Image;
        Debug.Assert(image != null);
        Debug.Assert(image.Source != null && image.Source as BitmapSource != null);
        // If we allow D&D just within the application we don't need to do this, we can just 
        // use the BitmapSource; however, if we cross applications we need this (even if they
        // are both WPF)
        Bitmap bm = CreateBitmap((BitmapSource) image.Source);
        var data = new DataObject(DataFormats.Bitmap, bm, true);
        DragDropEffects de = DragDrop.DoDragDrop(image, data, DragDropEffects.Copy);
    }

    private void DropImage(object sender, DragEventArgs e) {
        ImageSource imageSource = GetImageSourceFromDropData(e);
        if (imageSource != null) {
            var imageControl = new Image {Source = imageSource};
            Canvas.SetLeft(imageControl, e.GetPosition(Canvas).X);
            Canvas.SetTop(imageControl, e.GetPosition(Canvas).Y);
            Canvas.Children.Add(imageControl);
        }
    }

    private static ImageSource GetImageSourceFromDropData(DragEventArgs e) {
        if (e.Data.GetDataPresent(DataFormats.Bitmap, true)) {
            var bm = e.Data.GetData(DataFormats.Bitmap, true);
            // This is an BitmapSource so we can just use it; we wouldn't usually see this if we 
            // did something like e.Data.GetData("System.Windows.Media.Imaging.BitmapSource") and
            // it succeeded.
            var interopBitmap = bm as InteropBitmap;
            if (interopBitmap != null) {
                return interopBitmap;
            }

            var bitmap = bm as Bitmap;
            if (bitmap != null) {
                return CreateBitmapSource(bitmap);
            }
        }
        else if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
            var fileNames = (string[]) e.Data.GetData(DataFormats.FileDrop, true);
            var bmp = new BitmapImage(new Uri("file:///" + fileNames[0].Replace("\\", "/")));
            return bmp;
        }
        return null;
    }

    private static Bitmap CreateBitmap(BitmapSource source) {
        using (var memoryStream = new MemoryStream()) {
            var bitmapEncoder = new BmpBitmapEncoder();
            bitmapEncoder.Frames.Add(BitmapFrame.Create(source));
            bitmapEncoder.Save(memoryStream);
            memoryStream.Position = 0;
            return new Bitmap(memoryStream);
        }
    }

    private static BitmapSource CreateBitmapSource(Bitmap bitmap) {
        IntPtr hBitmap = bitmap.GetHbitmap();
        BitmapSource bmpSrc = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero,
          Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
        DeleteObject(hBitmap);
        return bmpSrc;
    }

    [DllImport("gdi32.dll")]
    private static extern bool DeleteObject(IntPtr hObject);
}

This is just a start, but it should get you going doing your own drag and drop of images/bitmaps.


About this entry