Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Adding and using 32 bit alphablended images and icons to the imagelist control

0.00/5 (No votes)
23 Dec 2004 1  
An article on adding alphablended images to an imagelist control and using them on components.

Sample Image

Introduction

As time goes on and applications evolve, the end user expects a richer and more vibrant user interface. Not only is it visually appealing, a nice user interface also serves as a function. Better design and use of icons to represent tasks will not only make your application stand out but will also give the end user a better experience working with your applications. This is the reason why I decided to publish this article. Now, let's jump to the technical aspects...

We've all seen those nice toolbar icons Microsoft (c) uses in their products. They include shadows, 24 bit true colors, transparencies etc. These images are actually 24 bit images that contain an alpha channel which makes them 32 bit format. This format not only allows transparent pixels, but semi-transparent pixels as well. This allows images to blend in with their background nicely without the "jaggedness" you sometimes see with regular 24 bit transparent images. If you have ever tried using such icons or images in your applications, you have quickly found out that it's somewhat more of a challenge than you expected, and in fact there are some obstacles to overcome. Not to mention a bug that exists in the framework that will prevent you from displaying such images properly. In this article, I will try to explain what is needed to overcome these obstacles and also how to use my ImageList class to fix the problem. From this point on, I will refer to both icons and images as just images and also refer to 24 bit alpha blended images as just 32 bit images.

The Problem

The bug I mentioned above, I believe, exists in the ImageList component itself. If you try to add a 32 bit image using the Images.Add() method of the ImageList control, or try to add images using the Visual Studio IDE, you will find that the alpha channel becomes either corrupt or lost all together based on the raw format of the image file itself. An example of this would be to create a new Bitmap object and load a 32 bit image such as a .png file, using the Bitmap constructor like: Bitmap bm = new Bitmap( file );, and then using the ImageList. Add method to add the bitmap object to the ImageList. Even though the bitmap's .IsAlphaFormat property is true, the image becomes corrupt in the process of adding to the ImageList control, and if you try to use these images on controls, you will find that they are displayed incorrectly. My belief is that the problem exists in the way the .Add() method extracts the alpha channel and copies it to the ImageList from a bitmap structure. The reason why I came to this conclusion was because supplying a handle to an image with the .Add() method seems to work. You will see I use this method for Icons (.ico files) since the Icon class has a .FromHandle method that returns a Windows icon.

Now, if you try using these added images for your controls, you will find that since the alpha channel has become corrupt, the images contain solid black for the areas where alpha blending was to take place. In cases where you add PNG files with alpha channel, you will find that alpha blending does occur but contains a subtle color error in the areas where alpha blending was to take place. You can see a good example of this in the included image above by looking at the zoomed IE and MSN icon on the left. This might not be an eye catching thing for these 32x32 pixel icons but when you have 16x16 icons, the color error is very apparent. In conclusion, the ImageList control itself can hold 32 bit alpha blended images with no problems but the methods used to add those images are causing the corruption to occur.

The Fix

I have come up with a simple class with some static methods to perform the adding of 32 bit images to your ImageLists. The class Imagelist and its five methods allow you to add images correctly from various sources. These sources include an Image or Icon object, an external image file or icon file, an embedded image resource in your project, any external file icon or folder icon, and any file system extension icon. Each of these have examples and explanations below. Various methods are used for adding images from the different sources, but for the main part, Win32 API is used. When adding images from external files or from an Image object, basically what is done is a new bitmap is created using the CreateDIBSection() API call which resides in the gdi32.dll, and creates a bitmap object and returns a handle to the bitmap object in memory. Then the image is copied into this area in memory with correct formatting. The bitmap is then loaded into the ImageList using the ImageList_Add() call which takes a handle to an ImageList control and a handle to a bitmap object. When adding Icon objects or files, you can add them correctly by using the Icon object's .Handle property. Once the images are loaded into the ImageList control, you can use them normally for toolbars, listviews, etc. by assigning the indexer to a control. Also, all of its native methods hold and return correct values such as the .Count property.

Before using the code

First and foremost, your C# application and control properties must be setup correctly before the images are displayed properly. Let's go over these first before moving onto the usage of the class. You will see below that the Application.EnableVisualStyles() and Application.DoEvents() must be made before a .Run method. Another crucial property that must be set is the ImageList ColorDepth property, which must be set to ColorDepth.Depth32Bit. You can of course set this property using the Visual Studio IDE. Also keep in mind that alpha blended images are only supported in Windows XP+ and .NET Framework 1.1+. This code will have no effect on earlier versions of Windows.

//

// Enabling Visual Styles

//


static void Main() 
{
    // These two calls MUST be made before the

    // .Run method to enable 32bit icons to be used.

    Application.EnableVisualStyles();
    Application.DoEvents();

    // The color depth of the ImageList MUST be set

    // to 32bit before images can be added to the control.

    myImagelist.ColorDepth = ColorDepth.Depth32Bit;

    Application.Run(new Form1());
}

Public Methods of the ImageList Class

public sealed class Imagelist
{
    public static void AddFromImage( Image sourceImage, 
                       ImageList destinationImagelist ){..}
    public static void AddFromImage( Icon sourceIcon, 
                       ImageList destinationImagelist ){..}
    public static void AddFromFile( string fileName, 
                       ImageList destinationImagelist ){..}
    public static void AddIconOfFile( string fileName, IconSize iconSize, 
                       bool selectedState, bool openState, 
                       bool linkOverlay, ImageList destinationImagelist ){..}
    public static void AddIconOfFile( string fileName, 
                       ImageList destinationImagelist ){..}
    public static void AddIconOfFileExt( string fileExtension, 
                       IconSize iconSize, bool selectedState, bool openState, 
                       bool linkOverlay , ImageList destinationImagelist ){..}
    public static void AddIconOfFileExt( string fileExtension, 
                       ImageList destinationImagelist ){..}
    public static void AddEmbeddedResource( string resourceName, 
                       ImageList destinationImagelist){..}
}

Now that those calls and properties have been made, we can move on to the usage of the static class methods....

Adding Image or Icon objects

//

// This example adds a 32bit alphablended image

// to myImagelist using the AddFromImage() method.

// This overloaded method accepts either

// an Image object or Icon object as its source.

//


Narbware.Imagelist.AddFromImage( Image.FromFile("image.png"), 
                                 myImagelist );

Bitmap myBitmap = new Bitmap( "image.png" );
Narbware.Imagelist.AddFromImage( myBitmap, myImagelist );

Icon myIcon = new Icon("icon.ico");
Narbware.Imagelist.AddFromImage( myIcon, myImagelist );

Adding images from embedded resources

The importance of this method is that you can use it to pre load images to the ImageList from an embedded resource. This means no external image files are needed when wanting preloaded images in your ImageList. This is because you cannot use the IDE to pre load images because that essentially uses the ImageList.Add() method. An important step when adding embedded resources is after you have added the image resource to your project, you must set its Build Action property to 'Embedded Resource'. Once that is done, the code below will add that resource to your ImageList.

//

// This example adds a 32bit alphablended image from

// an embedded resource to myImagelist

// using the AddEmbeddedResource() method.

//


Narbware.Imagelist.AddEmbeddedResource( "myApplicationName.image.png", 
  myImagelist ); // where image.png resides in the projects root

Adding images from external files

This method saves some time when wanting to add images from files that reside outside of your project's directory. Since you don't have to create an Image, load the file into the Image, then add it to the ImageList. Instead, you can just supply the path of the image you want to add.

//

// This example adds a 32bit alphablended image from an external

// file to myImagelist using the AddFromFile() method.

//


Narbware.Imagelist.AddFromFile( "c:\\blah\image.png", myImagelist );
Narbware.Imagelist.AddFromFile( "c:\\icons\icon.ico", myImagelist );

Extracting and adding file or folder icons

This method extracts file or folder icons and adds them to ImageList. This is a powerful method that the framework currently does not support and can be appreciated in any application dealing with the shell. This method has two overloads, a simple overload which, by default, will add a large sized icon to an ImageList, and a second overload which gives you the ability to format the icon you want to extract. For example, adding the selected state of the icon, or adding the icon with a link overlay, specifying the size of the icon you want to extract etc...

//

// This example extracts and adds 32bit alphablended icons

// from external files and folders

// to myImagelist using the AddIconOfFile() method.

//


// This short overload extracts and adds

// a largs icon (32x32) to and imagelist

Narbware.Imagelist.AddIconOfFile( "c:\\file.mpg", 
   myImagelist )
// adds the files icons, in this case the systems mpg icon


// This longer method gives you more

// options on the type of icon to extract

Narbware.Imagelist.AddIconOfFile( "c:\\file.mpg", 
         Narbware.IconSize.Small, false, false, 
         false, myImagelist );

// This adds a folder icon. It can also extract

// special folder icons such as My Music, My Pictures, etc...

Narbware.Imagelist.AddIconOfFile( "c:\mySpecialFolder", 
         Narbware.IconSize.Large, false, false, 
         false, myImagelist );

Extracting and adding file type icons (extensions)

This method is similar to the above AddIconOfFile() method, instead you supply the method with the extension of the icon you want to extract from the system. All the rules from the above method hold for this method. Examples below...

//

// This example extracts and adds 32bit alphablended file

// extension icons to myImagelist using the AddIconOfFileExt() method.

//


// adds a large sized icon associated with jpeg files.

Narbware.Imagelist.AddIconOfFileExt( "jpg", myImagelist );
Narbware.Imagelist.AddIconOfFileExt( "zip", Narbware.IconSize.Small, 
         false, false, false, myImagelist );
// adds a small sized zip file icon to the imagelist.

Points of Interest

Below is the private method used to add the 32 bit images to ImageLists. This is essentially the fix to the problem of the corrupt alpha channel. Note that a new Device Independent Bitmap (DIB) object is created with correct formatting based on the BITMAPINFO structure, and the desired bitmap is then copied to this location and added to our ImageList using Win32 API. The other overloaded Add() method is used to extract file icons which is done by creating and passing a SHFileInfo object and returning a handle to the file icon at which point it is added using the default ImageList's Images.Add(); method.

private static void Add( Bitmap bm, ImageList il )
{
    IntPtr hBitmap, ppvBits;
    BITMAPINFO bmi = new BITMAPINFO();
    if ( bm.Size != il.ImageSize )
    {
        // resize the image to dimensions of imagelist before adding

        bm = new Bitmap( bm, il.ImageSize.Width, il.ImageSize.Height );
    }
    bmi.biSize = 40;            // Needed for RtlMoveMemory()

    bmi.biBitCount = 32;        // Number of bits

    bmi.biPlanes = 1;           // Number of planes

    bmi.biWidth = bm.Width;     // Width of our new bitmap

    bmi.biHeight = bm.Height;   // Height of our new bitmap

    bm.RotateFlip( RotateFlipType.RotateNoneFlipY );
    // Required due to the way bitmap is copied and read


    hBitmap = CreateDIBSection( new IntPtr(0), bmi, 0, 
              out ppvBits, new IntPtr(0), 0 );
              //create our new bitmap

    BitmapData bitmapData = bm.LockBits( new Rectangle( 0, 0, 
               bm.Width, bm.Height ), ImageLockMode.ReadOnly, 
               PixelFormat.Format32bppArgb );
    RtlMoveMemory( ppvBits, bitmapData.Scan0, 
                   bm.Height * bitmapData.Stride );
                   // copies the bitmap

    bm.UnlockBits( bitmapData );
    ImageList_Add( il.Handle, hBitmap, new IntPtr(0) );
    // adds the new bitmap to the imagelist control

}

Conclusion

By using the ImageList class and its methods of adding images from various sources, you are now be able to use 32 bit alpha blended images in your C# applications. Once the images have been added, you can continue to use the ImageList control as usual. If you feel there are incorrect or misleading information in this article, please feel free to point them out. I'm somewhat of a beginner to C#, so I might not have everything down yet. Also, any comments or suggestions would be appreciated. Thanks.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here