Click here to Skip to main content
Click here to Skip to main content

Tagged as

Bitmap to BitmapSource

, 7 Sep 2010
Rate this:
Please Sign up or sign in to vote.
How to convert System.Drawing.Bitmap to System.Windows.Media.Imaging.BitmapSource

Introduction

When I started to convert my application from WinForms to WPF, I quickly reached the point where I needed to use my System.Drawing.Bitmap resources in WPF controls. But WPF uses System.Windows.Media.Imaging.BitmapSource.

The .NET Framework provides some interoperability methods to make this conversion but be careful when using them! This article will point some interesting things to know when using these methods and how you can avoid them.

Using the Code

My first attempt looked like this:

public static class Imaging
{
    public static BitmapSource CreateBitmapSourceFromBitmap(Bitmap bitmap)
    {
        if (bitmap == null)
            throw new ArgumentNullException("bitmap");

        return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
            bitmap.GetHbitmap(),
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());
    }
}

The CreateBitmapSourceFromHBitmap method does all the job: it returns a managed BitmapSource, based on the provided pointer to an unmanaged bitmap and palette information.

The problem with this piece of code is the call to GetHbitmap. It will leave a dangling GDI handle unless you P/Invoke to DeleteObject():

public static class Imaging
{
    [DllImport("gdi32.dll")]
    private static extern bool DeleteObject(IntPtr hObject);

    public static BitmapSource CreateBitmapSourceFromBitmap(Bitmap bitmap)
    {
        if (bitmap == null)
            throw new ArgumentNullException("bitmap");

        IntPtr hBitmap = bitmap.GetHbitmap();

        try
        {
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                hBitmap,
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());
        }
        finally
        {
            DeleteObject(hBitmap);
        }
    }
}

Calling DeleteObject will release the GDI handle. This method will work perfectly for most cases. However, if you have to work in a multi-threaded environment, be aware that it is not allowed to call GetHbitmap on the same bitmap in two different threads at the same time. To avoid this, use the lock keyword to create a critical section:

public static class Imaging
{
    [DllImport("gdi32.dll")]
    private static extern bool DeleteObject(IntPtr hObject);

    public static BitmapSource CreateBitmapSourceFromBitmap(Bitmap bitmap)
    {
        if (bitmap == null)
            throw new ArgumentNullException("bitmap");

        lock (bitmap)
        {
            IntPtr hBitmap = bitmap.GetHbitmap();

            try
            {
                return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                    hBitmap,
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(hBitmap);
            }
        }
    }
}

In my opinion, using DllImport is not elegant and I try to avoid it when possible. To avoid using it, you should get rid of the interoperability method and use Bitmap Decoders:

public static class Imaging
{
    public static BitmapSource CreateBitmapSourceFromBitmap(Bitmap bitmap)
    {
        if (bitmap == null)
            throw new ArgumentNullException("bitmap");

        using (MemoryStream memoryStream = new MemoryStream())
        {
            try
            {
                // You need to specify the image format to fill the stream. 
                // I'm assuming it is PNG
                bitmap.Save(memoryStream, ImageFormat.Png);
                memoryStream.Seek(0, SeekOrigin.Begin);

                BitmapDecoder bitmapDecoder = BitmapDecoder.Create(
                    memoryStream,
                    BitmapCreateOptions.PreservePixelFormat,
                    BitmapCacheOption.OnLoad);
                
                // This will disconnect the stream from the image completely...
                WriteableBitmap writable = 
		new WriteableBitmap(bitmapDecoder.Frames.Single());
                writable.Freeze();

                return writable;
            }
            catch (Exception)
            {
                return null;
            }
        }
    }
}

There is still a problem with this way of doing it: this method needs to be called from the UI thread otherwise it might throw exceptions later depending on how you are using the bitmap.

public static class Imaging
{
    public static BitmapSource CreateBitmapSourceFromBitmap(Bitmap bitmap)
    {
        if (bitmap == null)
            throw new ArgumentNullException("bitmap");
                
        if (Application.Current.Dispatcher == null)
            return null; // Is it possible?
                
        try
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                // You need to specify the image format to fill the stream. 
                // I'm assuming it is PNG
                bitmap.Save(memoryStream, ImageFormat.Png);
                memoryStream.Seek(0, SeekOrigin.Begin);
                
                // Make sure to create the bitmap in the UI thread
                if (InvokeRequired)
                    return (BitmapSource)Application.Current.Dispatcher.Invoke(
                        new Func<Stream, BitmapSource>(CreateBitmapSourceFromBitmap),
                        DispatcherPriority.Normal,
                        memoryStream);
                
                return CreateBitmapSourceFromBitmap(memoryStream);
            }
        }
        catch (Exception)
        {
            return null;
        }
    }
                
    private static bool InvokeRequired
    {
        get { return Dispatcher.CurrentDispatcher != Application.Current.Dispatcher; }
    }
                
    private static BitmapSource CreateBitmapSourceFromBitmap(Stream stream)
    {
        BitmapDecoder bitmapDecoder = BitmapDecoder.Create(
            stream,
            BitmapCreateOptions.PreservePixelFormat,
            BitmapCacheOption.OnLoad);
                
        // This will disconnect the stream from the image completely...
        WriteableBitmap writable = new WriteableBitmap(bitmapDecoder.Frames.Single());
        writable.Freeze();
                
        return writable;
    }
} 

When Do You Really Need to do Conversions Like These?

As I pointed in the introduction of this article, I needed to make conversions from System.Drawing.Bitmap to System.Windows.Media.Imaging.BitmapSource because my application was sharing some resources between WinForms and WPF. In fact, I can't really think of any other situation where it would be really required to do so (if you have any, let me know).

For sure, you should not need to do these conversions when starting a WPF application from scratch. You should take a look at this article (or Google it to find tons of articles about this subject) to learn how to manage images in a WPF application.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

chaf2701
Software Developer
Canada Canada
No Biography provided

Comments and Discussions

 
QuestionSo complex.. PinmemberJohnnyCN23-Aug-12 20:37 
AnswerRe: So complex.. Pinmemberchaf270125-Aug-12 4:48 
Hi,
1. If the BitmapSource is created in another thread, in some situations, I get an exception saying "The calling thread cannot access this object because a different thread owns it".
2. I haven't found an easier way. If you find one, let me know. The only thing I can think of is the suggestion of Matt T Heffron in this forum. He suggested to use a Binding with a ValueConverter instead of using Invoke because the Binding is always evaluated in the UI thread.
GeneralRe: So complex.. PinmemberJohnnyCN25-Aug-12 5:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 7 Sep 2010
Article Copyright 2010 by chaf2701
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid