Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / WPF

Bitmap to BitmapSource

Rate me:
Please Sign up or sign in to vote.
4.67/5 (20 votes)
7 Sep 2010CPOL2 min read 181.6K   33   18
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:

C#
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():

C#
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:

C#
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:

C#
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.

C#
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)


Written By
Software Developer
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIs there a way to convert byte array directly to bitmapsource Pin
Dotnet@work26-Oct-10 0:17
Dotnet@work26-Oct-10 0:17 
AnswerRe: Is there a way to convert byte array directly to bitmapsource Pin
chaf270127-Oct-10 6:49
chaf270127-Oct-10 6:49 
GeneralConversion Speed Pin
Member 5343577-Sep-10 5:05
Member 5343577-Sep-10 5:05 
GeneralRe: Conversion Speed Pin
chaf27017-Sep-10 15:24
chaf27017-Sep-10 15:24 
GeneralMy vote of 4 Pin
_H2_31-Aug-10 6:39
_H2_31-Aug-10 6:39 
I surmise this code is best code for this operation.
GeneralYou could avoid P/Invoke and GDI... Pin
Matt T Heffron26-Aug-10 10:31
professionalMatt T Heffron26-Aug-10 10:31 
GeneralRe: You could avoid P/Invoke and GDI... Pin
Josh Fischer26-Aug-10 15:33
Josh Fischer26-Aug-10 15:33 
GeneralRe: You could avoid P/Invoke and GDI... Pin
chaf27012-Sep-10 8:01
chaf27012-Sep-10 8:01 
GeneralRe: You could avoid P/Invoke and GDI... Pin
Matt T Heffron2-Sep-10 8:24
professionalMatt T Heffron2-Sep-10 8:24 

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

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