The gray image on the button when the item is disabled (simple and beautiful way)

I want gray images (on buttons) when buttons are disabled. When I have text (without images) on the button, the text is grayed out (with the image as the contents of the button, they are not gray-gray). Is there a simple and beautiful way to do this?

This is my xaml file:

<Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ToolBarTray VerticalAlignment="Top" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" IsLocked="true" Grid.Row="0"> <ToolBar Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Band="1" BandIndex="1"> <Button Command="{Binding Button1}" RenderOptions.BitmapScalingMode="NearestNeighbor"> <Button.Content> <Image Source="open.ico"></Image> </Button.Content> </Button> <Button Command="{Binding Button2}" RenderOptions.BitmapScalingMode="NearestNeighbor"> <Button.Content> <Image Source="open.ico"></Image> </Button.Content> </Button> </ToolBar> </ToolBarTray> </Grid> </Window> 

and this is my code behind the file:

 public partial class MainWindow : Window { private RelayCommand _button1; private RelayCommand _button2; public MainWindow() { InitializeComponent(); DataContext = this; } public ICommand Button1 { get { if (_button1 == null) { _button1 = new RelayCommand(Button1E, Button1C); } return _button1; } } public ICommand Button2 { get { if (_button2 == null) { _button2 = new RelayCommand(Button2E, Button2C); } return _button2; } } public void Button1E(object parameter) { Trace.WriteLine("Button 1"); } public bool Button1C(object parameter) { return true; } public void Button2E(object parameter) { Trace.WriteLine("Button 2"); } public bool Button2C(object parameter) { return false; } } 
+19
command image button wpf xaml
Jul 03 '12 at 6:29
source share
5 answers

As Thomas Lebrun says in his post How is the gray MenuItem icon? the best way at the moment is probably to create a little class, AutoGreyableImage, which allows you to have an image that automatically switches to gray when the control is disabled.

Here is how you can use it:

 <MenuItem Header="Edit">  <MenuItem x:Name="miPaste"       Header="Paste">    <MenuItem.Icon>      <local:AutoGreyableImage Source="pack://application:,,,/Images/Paste.png"                          />    </MenuItem.Icon>  </MenuItem> </MenuItem> 

And here is the implementation:

 /// <summary> /// Class used to have an image that is able to be gray when the control is not enabled. /// Author: 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 property.    IsEnabledProperty.OverrideMetadata(typeof(AutoGreyableImage), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnAutoGreyScaleImageIsEnabledPropertyChanged)));  }  /// <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>  private static void OnAutoGreyScaleImageIsEnabledPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)  {    var autoGreyScaleImg = source as AutoGreyableImage;    var isEnable = Convert.ToBoolean(args.NewValue);    if (autoGreyScaleImg != null)    {      if (!isEnable)      {        // Get the source bitmap        var bitmapImage = new BitmapImage(new Uri(autoGreyScaleImg.Source.ToString()));        // Convert it to Gray        autoGreyScaleImg.Source = new FormatConvertedBitmap(bitmapImage, PixelFormats.Gray32Float, null, 0);        // Create Opacity Mask for greyscale image as FormatConvertedBitmap does not keep transparency info        autoGreyScaleImg.OpacityMask = new ImageBrush(bitmapImage);      }      else      {        // Set the Source property to the original value.        autoGreyScaleImg.Source = ((FormatConvertedBitmap) autoGreyScaleImg.Source).Source;        // Reset the Opcity Mask        autoGreyScaleImg.OpacityMask = null;      }    }  } } 

Here is the result:

Result of applying class to perform grayscale rendering when disabled

Sources

+16
Jul 06 '12 at 6:19 06:19
source share
โ€” -

You can use pixel shaders to do this automatically.

+4
Jul 03 '12 at 8:15
source share

A more complete version of AutoGreyableImage by Thomas Lebrun . For everyone who is interested, I started using the Thomas Lebruns class and ran into a few exceptions due to the null reference, and also found out that the image would not be disabled if the isEnabled property was set first and the source was set after.

So here is a class that finally helped. You can, of course, add an opacity question to this, but I decided to leave it until 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' } } } } 
+3
Jul 01 '14 at 13:50
source share

Or the same with an attached property.

 <Image behaviors:GrayoutImageBehavior.GrayOutOnDisabled="True" Source="/WpfApp;component/Resources/picture.png" /> 

GrayoutImageBehavior:

 public class GrayoutImageBehavior { public static readonly DependencyProperty GrayOutOnDisabledProperty = DependencyProperty.RegisterAttached("GrayOutOnDisabled", typeof(bool), typeof(GrayoutImageBehavior), new PropertyMetadata(default(bool), OnGrayOutOnDisabledChanged)); public static void SetGrayOutOnDisabled(Image element, bool value) { element.SetValue(GrayOutOnDisabledProperty, value); } public static bool GetGrayOutOnDisabled(Image element) { return (bool)element.GetValue(GrayOutOnDisabledProperty); } private static void OnGrayOutOnDisabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { Image image = (Image) obj; image.IsEnabledChanged -= OnImageIsEnabledChanged; if ((bool)args.NewValue) image.IsEnabledChanged += OnImageIsEnabledChanged; ToggleGrayOut(image); // initial call } private static void OnImageIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs args) { var image = (Image)sender; ToggleGrayOut(image); } private static void ToggleGrayOut(Image image) { try { if (image.IsEnabled) { var grayImage = image.Source as FormatConvertedBitmap; if (grayImage != null) { image.Source = grayImage.Source; // Set the Source property to the original value. image.OpacityMask = null; // Reset the Opacity Mask image.Opacity = 1.0; } } else { var bitmapImage = default(BitmapImage); if (image.Source is BitmapImage) bitmapImage = (BitmapImage) image.Source; else if (image.Source is BitmapSource) // assume uri source bitmapImage = new BitmapImage(new Uri(image.Source.ToString())); if (bitmapImage != null) { image.Source = new FormatConvertedBitmap(bitmapImage, PixelFormats.Gray32Float, null, 0); // Get the source bitmap image.OpacityMask = new ImageBrush(bitmapImage); // Create Opacity Mask for grayscale image as FormatConvertedBitmap does not keep transparency info image.Opacity = 0.3; // optional: lower opacity } } } catch (Exception ex) { LogicLogger.WriteLogEntry("Converting image to grayscale failed", LogLevel.Debug, false, ex); } } } 
+3
Aug 26 '14 at 17:16
source share

I changed GrayoutImageBehavior so that there are no memory leaks from the gray image caused when the user changes the "enable" property of the image from true to false and vice versa several times. I added DependencyProperty to preserve the gray image and the opacity mask of the gray image.

  public class GrayoutImageBehavior { public static readonly DependencyProperty GrayOutOnDisabledProperty = DependencyProperty.RegisterAttached("GrayOutOnDisabled", typeof(bool), typeof(GrayoutImageBehavior), new PropertyMetadata(default(bool), OnGrayOutOnDisabledChanged)); public static void SetGrayOutOnDisabled(Image element, bool value) { element.SetValue(GrayOutOnDisabledProperty, value); } public static bool GetGrayOutOnDisabled(Image element) { return (bool)element.GetValue(GrayOutOnDisabledProperty); } private static DependencyProperty GrayImageProperty = DependencyProperty.RegisterAttached("GrayImage", typeof(FormatConvertedBitmap), typeof(GrayoutImageBehavior)); private static void SetGrayImage(Image element, FormatConvertedBitmap value) { element.SetValue(GrayImageProperty, value); } private static FormatConvertedBitmap GetGrayImage(Image element) { return (FormatConvertedBitmap)element.GetValue(GrayImageProperty); } private static DependencyProperty GrayImageOpacityMaskProperty = DependencyProperty.RegisterAttached("GrayImageOpacityMask", typeof(Brush), typeof(GrayoutImageBehavior)); private static void SetGrayImageOpacityMask(Image element, Brush value) { element.SetValue(GrayImageOpacityMaskProperty, value); } private static Brush GetGrayImageOpacityMask(Image element) { return (Brush)element.GetValue(GrayImageOpacityMaskProperty); } private static void OnGrayOutOnDisabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { Image image = (Image)obj; image.IsEnabledChanged -= OnImageIsEnabledChanged; if ((bool)args.NewValue) image.IsEnabledChanged += OnImageIsEnabledChanged; ToggleGrayOut(image); // initial call } private static void OnImageIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs args) { var image = (Image)sender; ToggleGrayOut(image); } private static void ToggleGrayOut(Image image) { try { if (image.IsEnabled) { var grayImage = image.Source as FormatConvertedBitmap; if (grayImage != null) { image.Source = grayImage.Source; // Set the Source property to the original value. image.OpacityMask = null; // Reset the Opacity Mask //image.Opacity = 1.0; } } else { FormatConvertedBitmap grayImage = GetGrayImage(image); Brush grayOpacityMask = GetGrayImageOpacityMask(image); if(grayImage == null) { var bitmapImage = default(BitmapImage); if (image.Source is BitmapImage) bitmapImage = (BitmapImage)image.Source; else if (image.Source is BitmapSource) // assume uri source bitmapImage = new BitmapImage(new Uri(image.Source.ToString())); if (bitmapImage != null) { grayImage = new FormatConvertedBitmap(bitmapImage, PixelFormats.Gray32Float, null, 0); // Get the source bitmap SetGrayImage(image, grayImage); grayOpacityMask = new ImageBrush(bitmapImage); // Create Opacity Mask for grayscale image as FormatConvertedBitmap does not keep transparency info SetGrayImageOpacityMask(image, grayOpacityMask); //image.Opacity = 0.3; // optional: lower opacity } } image.Source = grayImage; image.OpacityMask = grayOpacityMask; } } catch (Exception ex) { //LogicLogger.WriteLogEntry("Converting image to grayscale failed", LogLevel.Debug, false, ex); } } } 
+1
Dec 6 '16 at 6:07
source share



All Articles