3

I need an Image that is grayed out when disabled (IsEnabled=False). A grayed out version of the image can be produced by reading the BitmapImage into a FormatConvertedBitmap which is shown here.

I have been able to get this working with a UserControl but now I would like the same behavior in a specialized Image class for more flexibility. I don't care if this is implemented in XAML, code-behind or both, but it needs to be a subclass of Image.

The usage could be:

<DisableableImage Source="Images/image1.png" />
<DisableableImage Source="Images/image1.png" IsEnabled="False" />

<!-- Since IsEnabled is inherited down the tree,
     the image will be grayed out like the rest of the button -->
<Button IsEnabled="False">
    <StackPanel Orientation="Horizontal">
        <TextBlock>OK</TextBlock>
        <DisableableImage Source="Images/ok.png" />
    </StackPanel>
</Button>
Oskar
  • 7,945
  • 5
  • 36
  • 44
  • possible duplicate of [Is there a way to desaturate an Image on a Button thats disabled?](http://stackoverflow.com/questions/4304972/is-there-a-way-to-desaturate-an-image-on-a-button-thats-disabled) – Justin Apr 29 '14 at 18:26

5 Answers5

9

Have a look at this link

EDIT: Or this one (all you need is the AutoGreyableImage class)

David Sherret
  • 101,669
  • 28
  • 188
  • 178
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Thanks! But I'm not ready to buy the whole Infragistics Win Client package for this class only :) – Oskar Jun 06 '09 at 14:11
  • Sorry, I didn't realize it was an Infragistics control... I have another link, I'll update my answer – Thomas Levesque Jun 06 '09 at 14:49
  • The second link is a good start but not the complete solution. I've added a MarkupExtension that creates such an image and adds a style with a trigger that sets Opacity to 0.5 as well when disabled, to make the image light and grey. – ygoe Feb 27 '14 at 16:53
  • Link only answer could be improved by adding relevant details here... – StayOnTarget Jul 16 '20 at 12:20
6

I made a little comparison based on the following solutions.

Since I already had a licens for the Infragistics Net Advantage for WPF it was easy to try it out

Here is the result

enter image description here

So the best approach depends on what results you are after. As for me, I think the result produced by AutoDisabledImage from Infragistics is too bright, AutoGreyableImage does a pretty good job (Identical result to Approach 1 (OP link)) and GreyscaleEffect produces the best result.

Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
2

More complete version of the AutoGreyableImage by Thomas Lebrun. For anyone interested, I started using Thomas Lebruns class and ran into a couple of nullreference exceptions, as well as finding out that an image would not be disabled if the isEnabled property was set first and the source set after.

So here's the class that finally did the trick for me. À propos, you can of course add the matter of opacity into this, but I decided to leave that up to the xaml around the image.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Media;

namespace MyDisabledImages
{
    /// <summary>
    /// Class used to have an image that is able to be gray when the control is not enabled.
    /// Based on the version by Thomas LEBRUN (http://blogs.developpeur.org/tom)
    /// </summary>
    public class AutoGreyableImage : Image
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="AutoGreyableImage"/> class.
        /// </summary>
        static AutoGreyableImage()
        {
            // Override the metadata of the IsEnabled and Source property.
            IsEnabledProperty.OverrideMetadata(typeof(AutoGreyableImage), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnAutoGreyScaleImageIsEnabledPropertyChanged)));
            SourceProperty.OverrideMetadata(typeof(AutoGreyableImage), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnAutoGreyScaleImageSourcePropertyChanged)));
        }

        protected static AutoGreyableImage GetImageWithSource(DependencyObject source)
        {
            var image = source as AutoGreyableImage;
            if (image == null)
                return null;

            if (image.Source == null)
                return null;

            return image;
        }

        /// <summary>
        /// Called when [auto grey scale image source property changed].
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        protected static void OnAutoGreyScaleImageSourcePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs ars)
        {
            AutoGreyableImage image = GetImageWithSource(source);
            if (image != null)
                ApplyGreyScaleImage(image, image.IsEnabled);
        }

        /// <summary>
        /// Called when [auto grey scale image is enabled property changed].
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        protected static void OnAutoGreyScaleImageIsEnabledPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
        {
            AutoGreyableImage image = GetImageWithSource(source);
            if (image != null)
            {
                var isEnabled = Convert.ToBoolean(args.NewValue);
                ApplyGreyScaleImage(image, isEnabled);
            }
        }

        protected static void ApplyGreyScaleImage(AutoGreyableImage autoGreyScaleImg, Boolean isEnabled)
        {
            try
            {
                if (!isEnabled)
                {
                    BitmapSource bitmapImage = null;

                    if (autoGreyScaleImg.Source is FormatConvertedBitmap)
                    {
                        // Already grey !
                        return;
                    }
                    else if (autoGreyScaleImg.Source is BitmapSource)
                    {
                        bitmapImage = (BitmapSource)autoGreyScaleImg.Source;
                    }
                    else // trying string 
                    {
                        bitmapImage = new BitmapImage(new Uri(autoGreyScaleImg.Source.ToString()));
                    }
                    FormatConvertedBitmap conv = new FormatConvertedBitmap(bitmapImage, PixelFormats.Gray32Float, null, 0);
                    autoGreyScaleImg.Source = conv;

                    // Create Opacity Mask for greyscale image as FormatConvertedBitmap does not keep transparency info
                    autoGreyScaleImg.OpacityMask = new ImageBrush(((FormatConvertedBitmap)autoGreyScaleImg.Source).Source); //equivalent to new ImageBrush(bitmapImage)
                }
                else
                {
                    if (autoGreyScaleImg.Source is FormatConvertedBitmap)
                    {
                        autoGreyScaleImg.Source = ((FormatConvertedBitmap)autoGreyScaleImg.Source).Source;
                    }
                    else if (autoGreyScaleImg.Source is BitmapSource)
                    {
                        // Should be full color already.
                        return;
                    }

                    // Reset the Opcity Mask
                    autoGreyScaleImg.OpacityMask = null;
                }
            }
            catch (Exception)
            {
                // nothin'
            }

        }

    }
}
Koert van Kleef
  • 772
  • 9
  • 17
2

if you use this a lot consider creating a custom Effect introduced with .NET 3.5 SP1 (not bitmapeffect) to render such an operation on your GPU. this effect can then be easily controlled by triggers.

Joachim Kerschbaumer
  • 9,695
  • 7
  • 49
  • 84
0

Create a DisableableImage class that is a typical WPF control. Inside, place two elements: the image, and a rectangle that appears only when the control is disabled. The rectangle should be the same width and height as the image, and it should overlay the image. With a color of gray and an alpha of somewhere around 40%, you should get an effect similar to actually graying out the image -- without all the effort to actually modify the image itself.

John Fisher
  • 22,355
  • 2
  • 39
  • 64
  • It's a nice idea, but if you try it, it looks nothing like the effect the questoner's trying to achieve. It just looks like a semi-transparent gray rectangle over an image. :) – Mal Ross Dec 11 '09 at 14:42
  • I guess that depends on exactly what someone means by "grayed out", but yes, there would be a little color left. – John Fisher Dec 12 '09 at 16:03
  • Not only would there be colour left – the outline would be changed from the icon content to a rectangle. That's something completely different. You might as well just hide the icon completely. – ygoe Feb 27 '14 at 16:19