Мерцание в С# при выполнении SlideButton (очевидно, DoubleBuffer не работает)

у меня возникла проблема с мерцанием в проекте С# WinForms.

Я просто сделал элемент управления 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 — это просто "перечисление" для хранения изображений, чтобы иметь только один экземпляр.

Я изменил свойство DoubleBuffered класса SlideButton на true, но это было ненужным, потому что SlideButton не мерцал. Тот, который мерцает, — это класс Slide. Если вам нужна дополнительная информация, спросите, что вы хотите. Любой совет?


person Oscar Vicente Perez    schedule 19.08.2015    source источник
comment
Я добавил к кнопке свойство DoubleBuffered, но раньше кнопка не мерцала. ???   -  person TaW    schedule 19.08.2015
comment
Я имел в виду, что изменил свойство DoubleBuffered класса SlideButton на true, но это было ненужным, потому что SlideButton не мерцал. Тот, который мерцает, — это класс Slide. Отредактировано.   -  person Oscar Vicente Perez    schedule 19.08.2015


Ответы (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 ...
    }

Можете попробовать, удачи.

person Waescher    schedule 23.08.2015