Option 1: CustomControl (PictureBox) + ColorMatrix
Since you don't want to use a Button, which would gray-out the Image for you when the Control is disabled, you can use a ColorMatrix to change the PictureBox.BackgroundImage
or (Image) to a gray scale.
The GrayScale matrix you see here uses well-known values to convert an Image to a scaled gray color representation. You can find other interpretations of the same matrix object that may generate different results.
It can be fun to test some or tweak it yourself.
The GrayScale procedure is implemented as an Extension method, since it may come in handy in other situations.
It extends the Image class, adding a ToGrayScale()
method (you can of course also extend the Bitmap class: you just need to call the Image extension, casting Bitmap to Image, or the other way around, as you prefer).
Assuming you have a Custom Control, when the BackgroundImage is changed, you create a GrayScale representation of it and store it.
Then override OnEnabledChanged
to change the BackgroundImage to either the original or its GrayScale version. A simple check prevents the code from generating a new GrayScale image when the BackgroundImage is changed internally (it's a somewhat simplified method, you should paint the background instead. I'll update it when I can).
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
[DesignerCategory("Code")]
public class ButtonPicture : PictureBox
{
private Image m_sourceImage = null;
private Image m_grayImage = null;
public ButtonPicture() { }
protected override void OnEnabledChanged(EventArgs e) {
base.OnEnabledChanged(e);
this.BackgroundImage = this.Enabled ? m_sourceImage : m_grayImage;
}
protected override void OnBackgroundImageChanged(EventArgs e) {
base.OnBackgroundImageChanged(e);
if (this.BackgroundImage == m_sourceImage ||
this.BackgroundImage == m_grayImage) return;
m_sourceImage = this.BackgroundImage;
m_grayImage = m_sourceImage.ToGrayScale();
}
protected override void Dispose(bool disposing) {
if (disposing) {
m_grayImage.Dispose();
}
base.Dispose(disposing);
}
}
The extension method:
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
public static class ImageExtensions
{
static ColorMatrix grayMatrix = new ColorMatrix(new float[][]
{
new float[] { .2126f, .2126f, .2126f, 0, 0 },
new float[] { .7152f, .7152f, .7152f, 0, 0 },
new float[] { .0722f, .0722f, .0722f, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 0, 0, 0, 0, 1 }
});
public static Bitmap ToGrayScale(this Image source) {
var grayImage = new Bitmap(source.Width, source.Height, source.PixelFormat);
grayImage.SetResolution(source.HorizontalResolution, source.VerticalResolution);
using (var g = Graphics.FromImage(grayImage))
using (var attributes = new ImageAttributes()) {
attributes.SetColorMatrix(grayMatrix);
g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height),
0, 0, source.Width, source.Height, GraphicsUnit.Pixel, attributes);
return grayImage;
}
}
}
Option 2: ControlPaint + PictureBox.Paint event (lame)
You can use the ControlPaint.DrawImageDisabled() method to draw a disabled Bitmap.
- The Graphics object the methods needs is provided by the Control's PaintEventArgs: subscribe to the
PictureBox.Paint
event and pass the e.Graphics
object to the method.
- The
Image
argument can be a copy of the PictureBox.Image
property value.
- The only advantage is that you need quite less code, but the default implementation cannot be controlled and the result is, well, questionable (but probably good enough for a generic use).
? Compare the Image on the left, disabled using a ColorMatrix, with the Image on the right, disabled using the ControlPaint method:
private void buttonPicture_Paint(object sender, PaintEventArgs e)
{
var pict = sender as PictureBox;
if (pict != null && (!pict.Enabled)) {
using (var img = new Bitmap(pict.Image, pict.ClientSize)) {
ControlPaint.DrawImageDisabled(e.Graphics, img, 0, 0, pict.BackColor);
}
}
}