使用SlideButton(DoubleBuffer显然无法正常工作)在C#中闪烁

时间:2015-08-19 06:26:51

标签: c# winforms graphics slide doublebuffered

我遇到了C#WinForms proyect闪烁的问题。

我只是制作了一个SlideButton控件:这是代码:

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

正如您所看到的,我所做的唯一油漆操作是控件底部的小型直立喷漆。这些控件只包含一个带有isOpen标志的自定义按钮,该标志在单击时会发生更改并触发OnSlideStateChangedEvent事件。按钮的代码是:

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只是一个“枚举”来保存图像,以便只有一个实例。

我将SlideButton类的DoubleBuffered属性更改为true,但这是不必要的,因为SlideButton没有flickr。闪烁的是Slide类。如果您需要更多信息,请询问您的需求。有提示吗?

1 个答案:

答案 0 :(得分:1)

有一天我也遇到了这个问题。最简单(也许不是最优雅)的解决方案是删除OnPaintBackground()并将其添加到普通OnPaint()方法中,因为否则每次控件失效时背景都会绘制到最后一个Gfx上导致闪烁:

    public ctor()
    {
        // this does just work with OptimizedDoubleBuffer set to true
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    }

    protected override void OnPaintBackground(PaintEventArgs e)
    {
        // do NOT paint the background here but in OnPaint() to prevent flickering!
        //base.OnPaintBackground(e);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        // do the background and the base stuff first (if required)
        base.OnPaintBackground(e);
        base.OnPaint(e);

        // ... custom paint code goes here ...
    }

你可以尝试一下,祝你好运。