Search code examples
c#winformsbuttoncustom-controlsinvalidation

Why doesn't Invalidate() method work for my custom button in design time?


I am working on my custom button in WinForm. I am already done at the work. But I have one last problem that my button doesn't Invalidate the area when a property was changed. I call the Invalidate method in set block and OnResize, but it doesn't work for the button. But it is fixed at runtime or rebuild. How can I fix it?

Here is the a picture for example:

enter image description here

My code:

    public class AltoButton : Control
            {
                int radius;
                RoundedRectangle roundedRect;
                Color inactive1, inactive2, pressed1, pressed2;
                LinearGradientBrush InactiveGB, MouseOverGB, BorderGB, currentGB;
                public AltoButton()
                {
                    inactive1 = Color.FromArgb(44, 188, 210);
                    inactive2 = Color.FromArgb(33, 167, 188);

                    pressed1 = Color.FromArgb(64, 168, 183);
                    pressed2 = Color.FromArgb(36, 164, 183);

                    radius = 10;
                    roundedRect = new RoundedRectangle(Width, Height, radius);

                    SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | 
                             ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
                }

                protected override void OnPaint(PaintEventArgs e)
                {
                    e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
                    roundedRect = new RoundedRectangle(Width, Height, radius);
                    InactiveGB = new LinearGradientBrush(new Rectangle(0, 0, Width, Height), inactive1, inactive2, 90f);
                    MouseOverGB = new LinearGradientBrush(new Rectangle(0, 0, Width, Height), pressed1, pressed2, 90f);
                    BorderGB = new LinearGradientBrush(new Rectangle(0, 0, Width, Height), Color.FromArgb(162, 120, 101), Color.FromArgb(162, 120, 101), 90f);
                    if (currentGB == null)
                        currentGB = InactiveGB;
                    e.Graphics.FillPath(currentGB, roundedRect.Path);
                    e.Graphics.DrawPath(new Pen(BorderGB), roundedRect.Path);
                }
                protected override void OnResize(EventArgs e)
                {
                    Invalidate();
                    base.OnResize(e);
                }
                protected override void OnMouseEnter(EventArgs e)
                {
                    currentGB = MouseOverGB;
                    Invalidate();
                    base.OnMouseEnter(e);
                }
                protected override void OnMouseLeave(EventArgs e)
                {
                    base.OnMouseLeave(e);
                    currentGB = InactiveGB;
                    Invalidate();
                }
                public int Radius
                {
                    get
                    {
                        return radius;
                    }
                    set
                    {
                        radius = value;
                        Invalidate();
                    }
                }
    }
    public class RoundedRectangle
        {
            Point location;
            int radius;
            GraphicsPath grPath;
            public RoundedRectangle(int width, int height, int radius)
            {
                location = new Point(0, 0);
                this.radius = radius;

                Rectangle upperLeftRect = new Rectangle(0, 0, 2 * radius, 2 * radius);
                Rectangle upperRightRect = new Rectangle(width - 2 * radius - 1, 0, 2 * radius, 2 * radius);
                Rectangle lowerLeftRect = new Rectangle(0, height - 2 * radius - 1, 2 * radius, 2 * radius);
                Rectangle lowerRightRect = new Rectangle(width - 2 * radius - 1, height - 2 * radius - 1, 2 * radius, 2 * radius);

                grPath = new GraphicsPath();
                grPath.AddArc(upperLeftRect, 180, 90);
                grPath.AddArc(upperRightRect, 270, 90);
                grPath.AddArc(lowerRightRect, 0, 90);
                grPath.AddArc(lowerLeftRect, 90, 90);
                grPath.CloseAllFigures();

            }
            public RoundedRectangle()
            {
            }
            public GraphicsPath Path
            {
                get
                {
                    return grPath;
                }
            }
            public Rectangle Rect
            {
                get
                {
                    return new Rectangle(location.X, location.Y, 2 * radius, 2 * radius);
                }
            }
        }
 public int Radius
        {
            get
            {
                return radius;
            }
            set
            {
                radius = value;
                Invalidate();
            }
        }
        public Color Inactive1
        {
            get
            {
                return inactive1;
            }
            set
            {
                inactive1 = value;
                Invalidate();
            }
        }
        public Color Inactive2
        {
            get
            {
                return inactive2;
            }
            set
            {
                inactive2 = value;
                Invalidate();
            }
        }
        public Color Pressed1
        {
            get
            {
                return pressed1;
            }
            set
            {
                pressed1 = value;
                Invalidate();
            }
        }
        public Color Pressed2
        {
            get
            {
                return pressed2;
            }
            set
            {
                pressed2 = value;
                Invalidate();
            }
        }

Solution

  • Remove the OnResize override and include ControlStyles.ResizeRedraw in the styles that you set to true.

    Or alternatively set Control.ResizeRedraw property to true.

    UPDATE: Actually the problem is more trivial. You have a cached brush in currentGB field which is created using a specific Width and Height.

    So you can keep your code the way it is, and just set currentGB to null (you should really be disposing all these brushes, but that's another story) when the size is changed:

    protected override void OnResize(EventArgs e)
    {
        currentGB = null;
        Invalidate();
        base.OnResize(e);
    }