0

I've created GradientButton which changes angle of gradient when mouse cursor is in its' bounds. Unfortunately the graphics is corrupted as there are random flashes during rendering.

To achieve gradient rotation I start a thread upon MouseEnter and stop it at MouseLeave. Doublebuffered set to true, helped a lot but did not solve this fully.

  public partial class GradientButton : UserControl {
        public Color Color1 { get; set; }
        public Color Color2 { get; set; }

        public bool Down { get; set; }
        public bool MouseOnButton { get; set; }

        [Browsable(true)]
        public  string TextToDraw { get; set; }
        public int Angle { get; set; }

        public GradientButton() {
            InitializeComponent();
            Color1 = Color.YellowGreen;
            Color2 = Color.LightGreen;
        }

        protected override void OnPaint(PaintEventArgs e) {
            base.OnPaint(e);
            var color1 = Color1;
            var color2 = Color2;
            if (Down) {
                var temp = color1;
                color1 = color2;
                color2 = temp;
            }
            if (MouseOnButton) {
                color1 = ControlPaint.Dark(Color2);
            }
            using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle, color1, color2, Angle)) {
                e.Graphics.FillRectangle(brush, this.ClientRectangle);
            }
            Rectangle rect1 = ClientRectangle;

            // Create a StringFormat object with the each line of text, and the block
            // of text centered on the page.
            StringFormat stringFormat = new StringFormat();
            stringFormat.Alignment = StringAlignment.Center;
            stringFormat.LineAlignment = StringAlignment.Center;

            // Draw the text and the surrounding rectangle.

            e.Graphics.DrawString(TextToDraw, Font, new SolidBrush(ForeColor), rect1, stringFormat);       
        }

        protected override void OnClick(EventArgs e) {
            base.OnClick(e);
        }
        protected override void OnResize(EventArgs e) {
            base.OnResize(e);
            Invalidate();
        }

        protected override void OnMouseDown(MouseEventArgs e) {
            base.OnMouseDown(e);
            Down = true;
            Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs e) {
            base.OnMouseUp(e);
            Down = false;
            Invalidate();
        }

        protected override void OnMouseEnter(EventArgs e) {
            base.OnMouseEnter(e);
            MouseOnButton = true;
            Thread t = new Thread(Animate);
            t.Start();

        }

        public void Animate() {
            while (MouseOnButton) {
                Angle += 5;
                Thread.Sleep(25);
                Invalidate();
            }
        }
        protected override void OnMouseLeave(EventArgs e) {
            base.OnMouseLeave(e);
            Angle = 0;
            MouseOnButton = false;
            Invalidate();
        }
    }

GradientButton.Designer.cs

partial class GradientButton {
    /// <summary> 
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary> 
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing) {
        if (disposing && (components != null)) {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Component Designer generated code

    /// <summary> 
    /// Required method for Designer support - do not modify 
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent() {
        this.SuspendLayout();
        // 
        // GradientButton
        // 
        this.Name = "GradientButton";
        this.Size = new System.Drawing.Size(78, 28);
        this.ResumeLayout(false);

    }

    #endregion




}
Yoda
  • 17,363
  • 67
  • 204
  • 344
  • Override OnPaintBackground() and do nothing, no more "flashes" from the BackColor property. Or add `this.DoubleBuffered = true;` in the constructor if the painting gets elaborate. Albeit that it is slower and the unpainted button might be noticeable, an issue that's fixed by [compositing the entire window](http://stackoverflow.com/a/3718648/17034). – Hans Passant Jun 03 '16 at 14:00

1 Answers1

1

Use

SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlsStyles.Opaque, true); 

to prevent the background from being painted prior to OnPaint processing. This should prevent the flicker inbetween the background erase and the painting.

Ben Jackson
  • 1,108
  • 6
  • 9
  • +1, `ControlStyles.OptimizedDoubleBuffer` also might be helpful (although to be honest I just always add the flag without really ever checking the difference). – vgru Jun 03 '16 at 13:03