65.9K
CodeProject is changing. Read more.
Home

Image that is grayed when disabled, for use in buttons, etc.

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Mar 16, 2011

CPOL
viewsIcon

23111

AutoGrayImage: An image class that changes to grayscale when it becomes disabled.

Download GrayImage.zip - 61.83 KB

Introduction

A well known WPF misfeature is that images of disabled (toolbar or other) buttons, or menuitems, are not displayed in grayscale. There are three well known ways around:
  • Set the image's Opacity with a trigger.
  • Set the image's Effect.
  • Use a custom image class.
All three approaches have been used before. Here I give an implementation of the third way.

Using the code

<Button>
  <my:AutoGrayImage Source2="/GrayImage;component/Images/Tulips.png" />
</Button>  
In the XAML, just replace your Image with AutoGrayImage, and set Source2 instead of the Source property. Here is how the result looks:

The Code

You should also add the following class to your project:
    public class AutoGrayImage : Image
    {
        public AutoGrayImage()
        {
            IsEnabledChanged += new
          DependencyPropertyChangedEventHandler(AutoGrayImage_IsEnabledChanged); 
        }

        void AutoGrayImage_IsEnabledChanged(object sender, 
                                DependencyPropertyChangedEventArgs e)
        {
            Source = IsEnabled?Source2:GrayedImage;   
        }
        FormatConvertedBitmap GrayedImage = null;

        public static readonly DependencyProperty Source2Property = 
                     DependencyProperty.Register("Source2", typeof(BitmapSource),
                            typeof(AutoGrayImage), new PropertyMetadata(null, 
                            OnSource2Changed));
        /// <summary>
        /// Sets the image to be grayed, or not.
        /// </summary>
        public BitmapSource Source2
        {
            get { return (BitmapSource)GetValue(Source2Property); }
            set { SetValue(Source2Property, value); }
        }
        static void OnSource2Changed(DependencyObject sender,
                                      DependencyPropertyChangedEventArgs e)
        {
            AutoGrayImage s = sender as AutoGrayImage;
            if (s.Source2 == null)
            {
                s.GrayedImage = null;
            }
            else
            {
                s.GrayedImage = new FormatConvertedBitmap(s.Source2,
                                    PixelFormats.Gray8, null, 0);
                s.OpacityMask = new ImageBrush(s.Source2);
            }
            s.AutoGrayImage_IsEnabledChanged(s, new 
                                 DependencyPropertyChangedEventArgs());
        }
    }
I admit that introducing another Source property, i.e., Source2, is not entirely clean. However, it seemed to be the option with the best (cleanliness / lines of code written) ratio.