Рисуем прозрачную кнопку

Я пытаюсь создать прозрачную кнопку на С# (.NET 3.5 SP1) для использования в моем приложении WinForms. Я пробовал все, чтобы кнопка была прозрачной (она должна показывать градиентный фон под кнопкой), но это просто не работает.

Вот код, который я использую:

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

}

Проблема в том, что кнопка, кажется, откуда-то захватывает случайную память пользовательского интерфейса и заполняет себя некоторым буфером из пользовательского интерфейса Visual Studio (в режиме разработки). Во время выполнения он захватывает какой-то нулевой буфер и полностью черный.

Моя конечная цель — нарисовать изображение на невидимой кнопке вместо прямоугольника. Однако концепция должна оставаться прежней. Когда пользователь наводит курсор на кнопку, рисуется фигура типа кнопки.

Любые идеи?

EDIT: спасибо всем, мне помогло следующее:

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

person rein    schedule 06.07.2009    source источник
comment
Выглядит как дубликат: stackoverflow.com/questions/201778/   -  person Aamir    schedule 06.07.2009
comment
То, что было предложено в этой теме, мне не помогло.   -  person rein    schedule 06.07.2009
comment
попробуйте рисовать новым цветом со значением непрозрачности 0 вместо Color.Transparent   -  person TheVillageIdiot    schedule 06.07.2009


Ответы (4)


WinForms (и базовый User32) вообще не поддерживает прозрачность. Однако WinForms может имитировать прозрачность, используя предоставленный вами стиль управления — SupportsTransparentBackColor, но в этом случае все, что делает «прозрачный» элемент управления, позволяет рисовать родительский фон.

ButtonBase использует некоторые стили окон, которые мешают работе этого механизма. Я вижу два решения: одно — получить элемент управления из Control (вместо ButtonBase), а второе — использовать DrawToBitmap Parent для получения фона под вашей кнопкой, а затем нарисовать это изображение в OnPaint.

person arbiter    schedule 06.07.2009
comment
+1 за отличную информацию @arbiter, но какой-нибудь пример кода, безусловно, пошел бы намного дальше. - person TheVillageIdiot; 06.07.2009
comment
Что-то вроде этого он и имел в виду, но кнопку, а не ярлык. (я думаю) christian-helle.blogspot.com/ 2008/01/ - person TEEKAY; 04.08.2009

В winforms есть некоторые приемы, позволяющие правильно рисовать фон элемента управления при использовании прозрачности. Вы можете добавить этот код в OnPaint или OnPaintBackground, чтобы получить элементы управления, которые у вас есть на рисуемом фоне:

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);...
person jmservera    schedule 06.07.2009

Я не уверен, что ButtonBase поддерживает прозрачность... вы проверяли это?

Я написал несколько прозрачных элементов управления, но я всегда унаследовал их от Control или UserControl.

Если вы хотите, чтобы элемент управления рисовал свой фон, вы должны переопределить OnPaintBackground вместо OnPaint и не вызывать базовый класс.

Однако заполнение прямоугольника с помощью Brushes.Transparent забавно — вы рисуете невидимым цветом то, что там находится. Или, говоря иначе: он ничего не делает!

person Dan Byström    schedule 06.07.2009

Я знаю, что этот вопрос старый, но если кто-то не хочет создавать элемент управления для этого, я придумал этот код из другой статьи и изменил его методом расширения.

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

И вызов типа:

buttonUpdate.ToTransparent(Color.Magenta);
person Kevin    schedule 02.04.2016