简体   繁体   中英

Drawing a transparent button

I'm trying to create a transparent button in C# (.NET 3.5 SP1) to use in my WinForms application. I've tried everything to get the button to be transparent (it should show the gradient background underneath the button) but it's just not working.

Here is the code I'm using:

public class ImageButton : ButtonBase, IButtonControl
{
    public ImageButton()
    {
        this.SetStyle(
            ControlStyles.SupportsTransparentBackColor | 
            ControlStyles.OptimizedDoubleBuffer | 
            ControlStyles.AllPaintingInWmPaint | 
            ControlStyles.ResizeRedraw | 
            ControlStyles.UserPaint, true);
        this.BackColor = Color.Transparent;
    }

    protected override void OnPaint(PaintEventArgs pevent)
    {
        Graphics g = pevent.Graphics;
        g.FillRectangle(Brushes.Transparent, this.ClientRectangle);
        g.DrawRectangle(Pens.Black, this.ClientRectangle);
    }


    // rest of class here...

}

The problem is that the button seems to be grabbing random UI memory from somewhere and filling itself with some buffer from Visual Studio's UI (when in design mode). At runtime it's grabbing some zero'd buffer and is completely black.

My ultimate goal is to paint an image on an invisible button instead of the rectangle. The concept should stay the same however. When the user hovers over the button then a button-type shape is drawn.

Any ideas?

EDIT: Thanks everybody, the following worked for me:

public class ImageButton : Control, IButtonControl
{
    public ImageButton()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        SetStyle(ControlStyles.Opaque, true);
        SetStyle(ControlStyles.ResizeRedraw, true);
        this.BackColor = Color.Transparent;

    }

    protected override void OnPaint(PaintEventArgs pevent)
    {
        Graphics g = pevent.Graphics;
        g.DrawRectangle(Pens.Black, this.ClientRectangle);
    }


    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
        // don't call the base class
        //base.OnPaintBackground(pevent);
    }


    protected override CreateParams CreateParams
    {
        get
        {
            const int WS_EX_TRANSPARENT = 0x20;
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= WS_EX_TRANSPARENT;
            return cp;
        }
    }

    // rest of class here...
}

WinForms (and underlying User32) does not support transparency at all. WinForms however can simulate transparency by using control style you provide - SupportsTransparentBackColor, but in this case all that "transparent" control does, it to allow drawing parent its background.

ButtonBase uses some windows styles that prevent working this mechanism. I see two solutions: one is to derive your control from Control (instead of ButtonBase), and second is to use Parent's DrawToBitmap to get background under your button, and then draw this image in OnPaint.

In winforms there are some tricks to allow a control having its background correctly painted when using transparency. You can add this code to the OnPaint or OnPaintBackground to get the controls you have in the background being painted:

if (this.Parent != null)
{
 GraphicsContainer cstate = pevent.Graphics.BeginContainer();
 pevent.Graphics.TranslateTransform(-this.Left, -this.Top);
 Rectangle clip = pevent.ClipRectangle;
 clip.Offset(this.Left, this.Top);
 PaintEventArgs pe = new PaintEventArgs(pevent.Graphics, clip);

 //paint the container's bg
 InvokePaintBackground(this.Parent, pe);
 //paints the container fg
 InvokePaint(this.Parent, pe);
 //restores graphics to its original state
 pevent.Graphics.EndContainer(cstate);
}
else
  base.OnPaintBackground(pevent); // or base.OnPaint(pevent);...

I'm not sure ButtonBase supports transparency... have you checked that out?

I've written a number of transparent controls, but I have always inherited from Control or UserControl.

When you want to block out a control painting it's background - you should override OnPaintBackground instead of OnPaint and not call the base class.

Filling a rectangle with Brushes.Transparent is funny though - you're painting with an invisible color over what's aready there. Or to put it another way: it does nothing!

I know this question is old, but if someone doesn't want to create a control to do this I came up with this code from a different article and changed it an extension method.

public static void ToTransparent(this System.Windows.Forms.Button Button,
     System.Drawing.Color TransparentColor)
{
    Bitmap bmp = ((Bitmap)Button.Image);
    bmp.MakeTransparent(TransparentColor);
    int x = (Button.Width - bmp.Width) / 2;
    int y = (Button.Height - bmp.Height) / 2;
    Graphics gr = Button.CreateGraphics();
    gr.DrawImage(bmp, x, y);
}

And the call like:

buttonUpdate.ToTransparent(Color.Magenta);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM