i got a problem with flickering in C# WinForms proyect.
I simply made a SlideButton control: this is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.ComponentModel.Design;
namespace DELTA.UI.Search
{
[Designer("System.Windows.Forms.Design.ParentControlDesigner,System.Design", typeof(IDesigner))]
public partial class Slide : UserControl
{
public Boolean IsShown
{
get { return button.IsOpen; }
set
{
button.IsOpen = value;
}
}
private int isShownHeight;
public Slide()
{
InitializeComponent();
this.SetStyle(
System.Windows.Forms.ControlStyles.UserPaint |
System.Windows.Forms.ControlStyles.AllPaintingInWmPaint |
System.Windows.Forms.ControlStyles.Opaque |
System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer,
true);
this.DoubleBuffered = true;
this.ResizeRedraw = true;
isShownHeight = this.Height;
this.Height = button.Height;
}
private void boton_MouseEnter(object sender, EventArgs e)
{
button.BackColor = Color.Gray;
button.Cursor = Cursors.Hand;
}
private void boton_MouseLeave(object sender, EventArgs e)
{
button.BackColor = Color.White;
button.Cursor = Cursors.Arrow;
}
private void button_OnSlideStateChangedEvent(bool isOpen)
{
Debug.WriteLine(isOpen);
if (!timerSlide.Enabled)
{
timerSlide.Start();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Brush b = new SolidBrush(this.BackColor))
e.Graphics.FillRectangle(b, 0, 0, this.Width, this.Height);
int rectWidth = this.Width / 10;
e.Graphics.FillRectangle(Brushes.LightGray, this.Width / 2 - rectWidth / 2, this.Height - 3, rectWidth, 3);
}
private void timerSlide_Tick(object sender, EventArgs e)
{
if (IsShown && this.Height + button.Height < isShownHeight)
{
int crecimiento = (isShownHeight - button.Height) / 35;
this.Height += this.Height + crecimiento >= isShownHeight ? isShownHeight - this.Height : crecimiento;
}
else if (!IsShown && this.Height > button.Height)
{
int decrecimiento = (isShownHeight - button.Height) / 35;
this.Height -= this.Height - decrecimiento <= button.Height ? this.Height - button.Height : decrecimiento;
}
else
{
timerSlide.Stop();
}
}
private void Slide_Resize(object sender, EventArgs e)
{
//this.Invalidate(new Rectangle(0, this.Height - 12, this.Width, 12));
}
}
}
As you can see, the only paint operation i make is the paint of a small rectagle in the bottom of the control. The controls just contain a custom buttom with a isOpen flag that changes when clicked and fires the OnSlideStateChangedEvent event. The code of the button is:
using DELTA.Util;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DELTA.UI.Search
{
class SlideButton : Button
{
private Boolean isOpen = true;
public Boolean IsOpen
{
get { return isOpen; }
set
{
isOpen = value;
/* Refresh the button with the new appearence */
this.Refresh();
/* Call the event */
if (OnSlideStateChangedEvent != null)
OnSlideStateChangedEvent(isOpen);
}
}
public SlideButton()
: base()
{
this.BackColor = Color.White;
if (!DesignMode)
IsOpen = false;
this.DoubleBuffered = true;
}
/// <summary>
/// Event for the Slide
/// </summary>
[Category("ClickCustomEvents")]
[Description("Fired when this is clicked")]
public event OnSlideStateChanged OnSlideStateChangedEvent;
/// <summary>
/// Delegate for the Change of state
/// </summary>
/// <param name="isOpen"></param>
public delegate void OnSlideStateChanged(Boolean isOpen);
/// <summary>
/// Override to avoid the default OnClick
/// </summary>
/// <param name="e"></param>
protected override void OnClick(EventArgs e)
{
IsOpen = !isOpen;
}
protected override void OnPaint(PaintEventArgs e)
{
// TODO Use the isOpen value to paint it with a filled rectagle or 2 borders
/* SETTINGS -> for open/close states of the button */
Color topColor = Color.LightGray;
Color otherSidesColor = Color.LightGray;
Color backgroud = Color.LightGray;
SingletonImages arrow = SingletonImages.ARROW_DOWN;
Rectangle arrowPosition = new Rectangle(this.Width - 30, this.Height - 26, 20, 20);
if (isOpen)
{
topColor = Color.LightGray;
otherSidesColor = Color.Transparent;
backgroud = Color.White;
arrow = SingletonImages.ARROW_UP;
}
/* PAINT ZONE */
/* Background */
using (SolidBrush brush = new SolidBrush(backgroud))
e.Graphics.FillRectangle(brush, new Rectangle(0, 0, this.Width, this.Height));
/* Border */
ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle,
otherSidesColor, 2, ButtonBorderStyle.Solid,
topColor, 2, ButtonBorderStyle.Solid,
otherSidesColor, 2, ButtonBorderStyle.Solid,
otherSidesColor, 2, ButtonBorderStyle.Solid);
/* Text -> Padding = 5 (This can be changed to use the padding property of the control if need) */
e.Graphics.DrawString(this.Text, this.Font, Brushes.Black, new Point(10, 8));
/* Arrow */
e.Graphics.DrawImage(arrow.Image, arrowPosition);
}
}
}
SingletonImages is just a "enum" to hold images in order to have just one instance of it.
I changed the DoubleBuffered property of the SlideButton class to true but that was unnecesary cause the SlideButton didn't flickr. The one that flickers is the Slide class. If you need more info ask for what you want. Any tip?
One day I ran into this problem as well. The simplest (maybe not the most elegant) solution was to remove the
OnPaintBackground()
and add it into the normalOnPaint()
method because otherwise the background is painted onto your last Gfx each time the control is invalidated what causes the flickering:You might give it a try, good luck.