Несмотря на двойную буферизацию, тикер все еще мигает - PullRequest
5 голосов
/ 19 августа 2011

Кто-нибудь имеет представление о том, как избавиться от мерцания?Я исследовал SO, Интернет и попробовал много разных вещей, таких как помещение TickerControl в панель с двойной буферизацией а-ля Двойная буферизация, когда не рисуется в OnPaint (): почему это не работает? и т. Д., Кромемного других вещей.Он по-прежнему мерцает не при каждом перекрашивании, а пару раз в секунду.

Кроме того, даже после удаления «g.Clear (BackColor)» в OnPaint что-то должно по-прежнему очищать фон, как текстпродолжает читать читабельно.

Здесь соответствующие части моего класса TickerControl:

class TickerControl : Control
{
    private static readonly StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap);

    private const int padding = 40;
    private const int scrollSleep = 10;
    private const int scrollAdvancePixels = 1;

    private float textWidth;
    private float currentX;

    private static readonly Timer scrollTimer = new Timer();

    public TickerControl()
    {
        this.SetStyle(ControlStyles.UserPaint |
                      ControlStyles.AllPaintingInWmPaint |
                      ControlStyles.OptimizedDoubleBuffer
                      , true);
        scrollTimer.Tick += AdvanceText;
        scrollTimer.Interval = scrollSleep;
        scrollTimer.Enabled = true;
    }

    private void AdvanceText(object sender, EventArgs e)
    {
        if (IsDisposed)
            return;

        currentX -= scrollAdvancePixels;
        if (currentX <= -textWidth)
            currentX = 0;
        Invalidate();
    }

    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Graphics g = e.Graphics;

        g.Clear(BackColor);

        using (SolidBrush brush = new SolidBrush(ForeColor))
        {
            g.DrawString(Text, Font, brush, currentX, 0, stringFormat);
            g.DrawString(Text, Font, brush, currentX + textWidth, 0, stringFormat);
        }
    }

Любые идеи?

Обновление:

Поскольку sallushan предложил ручную двойную буферизацию, позвольте мне добавить, что я пробовал это раньше, используя код ниже.Но на экране это выглядит точно так же, как и выше, поэтому проблема, похоже, не в моем методе OnPaint.Я предполагаю, что это должно быть где-то в настройке моего Контроля.

    private Bitmap backBuffer;

    protected override void OnPaint(PaintEventArgs e)
    {
        if (backBuffer == null)
            backBuffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);

        Graphics g = Graphics.FromImage(backBuffer);

        g.Clear(BackColor);

        using (SolidBrush brush = new SolidBrush(ForeColor))
        {
            g.DrawString(Text, Font, brush, currentX, 0, stringFormat);
            g.DrawString(Text, Font, brush, currentX + textWidth, 0, stringFormat);
        }

        g.Dispose();

        e.Graphics.DrawImageUnscaled(backBuffer, 0, 0);
    }

    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
        // Don't call base!
    }

    protected override void OnSizeChanged(EventArgs e)
    {
        if (backBuffer != null)
        {
            backBuffer.Dispose();
            backBuffer = null;
        }
        base.OnSizeChanged(e);
    }

Ответы [ 6 ]

5 голосов
/ 19 августа 2011

Установите этот код в вашей форме.Это уберет мерцание

 protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x02000000;

                return cp;
            }
        }

Надеюсь, это поможет

1 голос
/ 19 августа 2011

Создать постоянное растровое изображение в виде кадра. И внесите свои изменения в это растровое изображение, а затем визуализируйте это растровое изображение в элементе управления. Это должно устранить мерцание.

class MyControl : System.Windows.Forms.Control
{
    Bitmap bmp;
    Graphics g;
    int x = 100;

    public MyControl()
    {
        this.SetStyle(System.Windows.Forms.ControlStyles.AllPaintingInWmPaint | System.Windows.Forms.ControlStyles.UserPaint | System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer , true);

        bmp = new Bitmap(100, 100);
        g = Graphics.FromImage(bmp);
    }        

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
        this.g.FillRectangle(Brushes.White, new Rectangle(0, 0, 100, 100));
        this.g.DrawString("Hello", this.Font, Brushes.Black, (float)x, 10);

        e.Graphics.DrawImage(bmp, new Point(0, 0));

        x--;
    }
}
0 голосов
/ 20 августа 2011

Превращение комментария Ганса Пассанта в ответ, чтобы я мог его принять:

Этот эффект не называется мерцанием, он называется разрывом. Вы видите часть старого растрового изображения и часть нового растрового изображения. Который становится очень заметны на движущихся объектах, они кажутся нервными. Не исправимо в Winforms, Google "вертикальный интервал гашения". - Ганс Пассант

0 голосов
/ 19 августа 2011

Похоже, что вы рисуете 2 строки рядом друг с другом:

g.DrawString(Text, Font, brush, currentX, 0, stringFormat);
g.DrawString(Text, Font, brush, currentX + textWidth, 0, stringFormat);

Это просто случай, когда Invalidate используется только та часть элемента управления, которая имеетизменилось.Простейшим способом было бы кэшировать / вычислить высоту строки и сделать это:

 Invalidate(new Rectangle((int)currentX, 0, (int)textWidth*2, (int)textHeight));

На моей машине это заметно плавнее, чем аннулирование всего элемента управления.

0 голосов
/ 19 августа 2011

Ну, кроме идеи тройной буферизации, которая, честно говоря, я никогда не использовал в своих пользовательских элементах управления, и у меня были довольно сложные элементы, я бы сказал, что аннулирование элемента управления каждые 10 мс как-то связано с этим. Это 100 раз в секунду. Фильмы с 25 кадрами в секунду удобны для человеческого глаза с точки зрения плавности движения, но вы перекрашиваете свой контроль в 4 раза больше.

Попробуйте более высокое значение: скажем, 40.

Кроме того, когда вы перерисовываете элемент управления, вы можете перерисовать только его область, те части, которые изменились: объединение двух областей, которые образуют старое текстовое местоположение и новое текстовое местоположение. Вам не нужно перекрашивать любые окружающие области, которые все еще на месте (будь то фон или что-то в этом роде).

DrawString () довольно медленный по сравнению с DrawText TextRenderer - примерно в 6 раз, вы можете использовать это. Вы потеряете некоторые функции (использует GDI вместо GDI +), но, возможно, это соответствует вашему сценарию.

О, я бы также микро-оптимизировал и вытащил ту кисть SolidBrush, которую вы заново создаете в каждом OnPaint () снаружи как член класса, обновлял ее только при изменении ForeColor.

0 голосов
/ 19 августа 2011

Попробуйте использовать методы Invalidate (прямоугольник) или Invalidate (Region) , чтобы ограничить объем перерисовки тем, что фактически необходимо сделать.В настоящее время вы перерисовываете полный контроль во время каждого события OnPaint.

...