A monotone Image class (or "grayable" image)

I was trying the other day to make a button with an image look like it’s disabled, but I couldn’t make the image gray.

I searched the internet for a solution but couldn’t find anything that satisfied me. Don’t get me wrong, some of the solutions did get the job done, but I have this problem that I must have everything in the most elegant way. Cest la vie.

I’ve decided to find a solution myself, and I didn’t want to limit myself only to the gray color, and to the "IsEnabled false" state because I was involved in a project that also required displaying button images in a bluish hue until the mouse if over them, and only than present them in full color.

One of the approaches that I considered was using the FormatConvertedBitmap class inside the XAML, as the Image.Source value, but WPF’s designer doesn’t seem to work with it (no image is shown), and this class lets me convert only to a gray scale image and not to colors other than gray.

Another approach I saw was of Microsoft in Visual Studio 2010. As you know, the UI of Visual Studio 2010 is written in WPF so I wanted to see how they fixed the problem. I saw (using Reflector, of course) that they created a GrayscaleImageConverter. On one hand the usage of this converter looks cumbersome because there is not a direct way to activate it when the image is disabled, and will probably force me to use a style; but on the other hand it does allow me to set a color other than gray, although it was written in a very limiting way (I guess VS doesn’t need more then 128×128 images with different types of image formats).

I’ve decided to take a different approach: First, I created my own MonotoneImage class which derives from the original Image class, and I rewrote the OnRender method to change the hue of the image.

I was thinking a lot about the IsFullColor property. On my first attempt I was using the IsEnabled property to decide if to draw the image in gray, but than I thought it might limit me when I’ll use it with the "mouse over" scenario, so I’ve decided to create the IsFullColor property and bind the IsEnabled property to it. You might wonder why I didn’t create a property like IsMonotone (which has a more elegant name). The reason is I wanted the colorful state to be the positive state. That way it’s easier to use binding the IsEnabled or IsMouseOver properties without using value converters to convert ‘true’ to ‘false’ and vice versa.

The BiasColor property controls the hue of the monotone image. The default is White, which makes the image grayscale, but you can choose any other color to make the image appear in that color. I also wanted to be able to make the color transformation smooth using animation, so I needed to find a way to control the impact of the bias color, which means finding a way to control for each pixel how much I want to see its original color, and how much I want to see the bias color. I ‘ve decided to use the bias color’s unused alpha channel for that purpose. On one hand I has an unused property that I could harness for my needs, and on the other hand it made sense to specify the alpha channel to control that behavior; So, the closer the bias color’s alpha channel is to 0, the less we’ll see the bias channel, and the closer it gets to 255 (FF), the more we’ll see the bias color and not the original color of each pixel.

Notice that you we can’t animate the actual alpha channel, because the alpha property (Color.A) is not a dependency property, hence not animatable. What we can do is use the ColorAnimation and use the same RGB values, but different A value.

I based my conversion methods on Microsoft’s GrayscaleImageConverter, but I’ve made some changes to them in order to support a larger variety of images, and to support the bias color’s alpha channel impact.

Now I can use the image and make it gray when it’s disabled, or bind the IsFullColor to the IsMouseOver property to make the image in some color when the mouse is not over the image, and displaying it in full color when the mouse is over the image.