user9514996
user9514996

Reputation:

Control.OnPaint() is called twice in a Custom Control

I am new to Custom Controls. I'm trying to create a flat Button which changes its border color and text color when the Mouse or the Keyboard focus comes to it.

When I change the BorderColor Property of the Button from the Designer, it doesn't update the color and also when I run the program, BorderColor remains the same.

I am not sure about the OnPaint() event code I have written. It consoles out twice when run first time. I don't know why!

How to make a Custom Control so its attributes can be fiddled in Designer?

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Mato
{
    class FlatButton : Control
    {
        public override Cursor Cursor { get; set; } = Cursors.Hand;
        public float BorderThickness { get; set; } = 2;
        public Color BorderColor { get; set; } = ColorTranslator.FromHtml("#0047A0");
        public Color TextColor { get; set; } = ColorTranslator.FromHtml("#0047A0");
        public Color ActiveBorderColor { get; set; } = ColorTranslator.FromHtml("#158C3F");
        public Color ActiveTextColor { get; set; } = ColorTranslator.FromHtml("#158C3F");

        private SolidBrush borderBrush, textBrush;
        private Rectangle borderRectangle;
        private bool active = false;
        private StringFormat stringFormat = new StringFormat();

        public FlatButton()
        {
            borderBrush = new SolidBrush(BorderColor);
            textBrush = new SolidBrush(TextColor);

            stringFormat.Alignment = StringAlignment.Center;
            stringFormat.LineAlignment = StringAlignment.Center;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Console.WriteLine(textBrush.Color.ToString());
            borderRectangle = new Rectangle(0, 0, Width, Height);
            e.Graphics.DrawRectangle(new Pen(borderBrush, BorderThickness), borderRectangle);
            e.Graphics.DrawString(this.Text, this.Font, (active) ? textBrush : borderBrush, borderRectangle, stringFormat);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            textBrush.Color = ActiveTextColor;
            this.Refresh();
            active = true;
            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            textBrush.Color = TextColor;
            this.Refresh();
            active = false;
            base.OnMouseUp(e);
        }

        protected override void OnGotFocus(EventArgs e)
        {
            //MessageBox.Show("Focused");
            borderBrush.Color = ActiveBorderColor;
            textBrush.Color = ActiveTextColor;
            this.Refresh();
            active = true;
            base.OnGotFocus(e);
        }

        protected override void OnLostFocus(EventArgs e)
        {
            borderBrush.Color = BorderColor;
            textBrush.Color = TextColor;
            this.Refresh();
            active = false;
            base.OnLostFocus(e);
        }
    }
}

Upvotes: 2

Views: 252

Answers (1)

Jimi
Jimi

Reputation: 32288

I slightly modified your original class, adding some private fields that keep track of the current status of your Button control.

Substituted: OnGotFocus() and OnLostFocus() with OnEnter() and OnLeave()

Added some private fields that reference the current Color of Text and Border, when those are modified on a Button Click() event and when the Control is entered or when the input focus leaves.

private Color m_CurrentBorderColor;
private Color m_CurrentTextColor;

Also, each time a property is changed, the Invalidate() method is called, which raises the OnPaint() event of the Control, updating its aspect.

Other minor changes.

class FlatButton : Control
{
    private Color m_TextColor;
    private Color m_BorderColor;
    private Color m_ActiveBorderColor;
    private Color m_ActiveTextColor;
    private Color m_CurrentBorderColor;
    private Color m_CurrentTextColor;

    public override Cursor Cursor { get; set; } = Cursors.Hand;
    public float BorderThickness { get; set; } = 2;
    public Color BorderColor {
        get => this.m_BorderColor;
        set { this.m_BorderColor = value;
              this.m_CurrentBorderColor = value;
              this.Invalidate();
        }
    }
    public Color TextColor {
        get => this.m_TextColor;
        set {
            this.m_TextColor = value;
            this.m_CurrentTextColor = value;
            this.Invalidate();
        }
    }
    public Color ActiveBorderColor { get => this.m_ActiveBorderColor; set { this.m_ActiveBorderColor = value; } }
    public Color ActiveTextColor { get => this.m_ActiveTextColor; set { this.m_ActiveTextColor = value; } }

    private StringFormat stringFormat;

    public FlatButton()
    {
        this.m_TextColor =  ColorTranslator.FromHtml("#0047A0");
        this.m_BorderColor = ColorTranslator.FromHtml("#0047A0");
        this.m_ActiveBorderColor = ColorTranslator.FromHtml("#158C3F");
        this.m_ActiveTextColor = ColorTranslator.FromHtml("#158C3F");
        this.m_CurrentTextColor = this.m_TextColor;
        this.m_CurrentBorderColor = this.m_BorderColor;

        stringFormat = new StringFormat()
        {
            Alignment = StringAlignment.Center,
            LineAlignment = StringAlignment.Center
        };
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        using (SolidBrush textBrush = new SolidBrush(this.m_CurrentTextColor))
        using (Pen pen = new Pen(this.m_CurrentBorderColor, this.BorderThickness))
        {
            e.Graphics.DrawRectangle(pen, e.ClipRectangle);
            e.Graphics.DrawString(this.Text, this.Font, textBrush, e.ClipRectangle, stringFormat);
        }
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        this.m_CurrentTextColor = this.m_ActiveTextColor;
        this.Invalidate();
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        base.OnMouseUp(e);
        this.m_CurrentTextColor = this.m_TextColor;
        this.Invalidate();
    }

    protected override void OnEnter(EventArgs e)
    {
        base.OnEnter(e);
        this.m_CurrentBorderColor = this.m_ActiveBorderColor;
        this.m_CurrentTextColor = this.m_ActiveTextColor;
        this.Invalidate();
    }

    protected override void OnLeave(EventArgs e)
    {
        base.OnLeave(e);
        this.m_CurrentBorderColor = this.m_TextColor;
        this.m_CurrentTextColor = this.m_BorderColor;
        this.Invalidate();
    }
}

Upvotes: 1

Related Questions