Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
304 views
in Technique[技术] by (71.8m points)

c# - How can I gray-out a disabled PictureBox used as Button?

I'm using Picturebox control as a Button in my app's main dashboard.
The PictureBox has of course an Image that identifies the Button function.

If I use normal Button controls, when disabled, the Buttons' Image it grayed out automatically.
This doesn't happen using a PictureBox.

How can I generated the same effect using a Picturebox.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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).

GrayScale Matrix PictureBox Image

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:

ControlPaint PictureBox disabled Image

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);
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...