C#使用BufferedGraphics实现GDI+双缓冲绘图 转

使用双缓冲的图形可以减少或消除重绘显示图面时产生的闪烁。使用双缓冲时,更新的图形首先被绘制到内存的缓冲区中,然后,此缓冲区的内容被迅速写入某些或所有显示的图面中。显示图形的重写相对简短,这通常可以减少或消除有时在更新图形时出现的闪烁。

使用C# GDI+绘图,实现双缓冲绘图有几种方法,在这篇文章中,将介绍其中的一种——使用BufferedGraphics实现GDI+双缓冲绘图。

下面的代码示例演示如何使用 BufferedGraphics 对象绘制以下图形,这些图形使用几种类型的缓冲实现。单击窗体将启动或停止一个计时器,该计时器将引起绘制更新。绘制更新使您可以观察双缓冲的效果。右击窗体将循环使用下列绘制模式:

  • 对于 Form,直接绘制到 Handle。

  • 对使用 OptimizedDoubleBuffer 控件样式的 OnPaint 方法进行重写,以进行绘制

  • 对于不使用 OptimizedDoubleBuffer 控件样式的窗体方法,重写 OnPaint 方法以进行绘制。

在每种模式下都将绘制文本,以标识当前模式并描述按下每个鼠标按钮时发生的行为。

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace DoubleBufferShown
{
    public partial class BufferingExample : Form
    {
        private BufferedGraphicsContext context;
        private BufferedGraphics grafx;

        private byte bufferingMode;
        private string[] bufferingModeStrings =  
        { "Draw to Form without OptimizedDoubleBufferring control style", 
          "Draw to Form using OptimizedDoubleBuffering control style", 
          "Draw to HDC for form" };

        private System.Windows.Forms.Timer timer1;
        private byte count;

        public BufferingExample()
            : base()
        {
            // Configure the Form for this example. 
            this.Text = "User double buffering";
            this.MouseDown += new MouseEventHandler(this.MouseDownHandler);
            this.Resize += new EventHandler(this.OnResize);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);

            // Configure a timer to draw graphics updates. 
            timer1 = new System.Windows.Forms.Timer();
            timer1.Interval = 200;
            timer1.Tick += new EventHandler(this.OnTimer);

            bufferingMode = 2;
            count = 0;

            // Retrieves the BufferedGraphicsContext for the  
            // current application domain. 
            context = BufferedGraphicsManager.Current;

            // Sets the maximum size for the primary graphics buffer 
            // of the buffered graphics context for the application 
            // domain.  Any allocation requests for a buffer larger  
            // than this will create a temporary buffered graphics  
            // context to host the graphics buffer. 
            context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);

            // Allocates a graphics buffer the size of this form 
            // using the pixel format of the Graphics created by  
            // the Form.CreateGraphics() method, which returns a  
            // Graphics object that matches the pixel format of the form. 
            grafx = context.Allocate(this.CreateGraphics(),
                 new Rectangle(0, 0, this.Width, this.Height));

            // Draw the first frame to the buffer. 
            DrawToBuffer(grafx.Graphics);
        }

        private void MouseDownHandler(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                // Cycle the buffering mode. 
                if (++bufferingMode > 2)
                    bufferingMode = 0;

                // If the previous buffering mode used  
                // the OptimizedDoubleBuffering ControlStyle, 
                // disable the control style. 
                if (bufferingMode == 1)
                    this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

                // If the current buffering mode uses 
                // the OptimizedDoubleBuffering ControlStyle, 
                // enabke the control style. 
                if (bufferingMode == 2)
                    this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);

                // Cause the background to be cleared and redraw. 
                count = 6;
                DrawToBuffer(grafx.Graphics);
                this.Refresh();
            }
            else
            {
                // Toggle whether the redraw timer is active. 
                if (timer1.Enabled)
                    timer1.Stop();
                else
                    timer1.Start();
            }
        }

        private void OnTimer(object sender, EventArgs e)
        {
            // Draw randomly positioned ellipses to the buffer. 
            DrawToBuffer(grafx.Graphics);

            // If in bufferingMode 2, draw to the form's HDC. 
            if (bufferingMode == 2)
                // Render the graphics buffer to the form's HDC. 
                grafx.Render(Graphics.FromHwnd(this.Handle));
            // If in bufferingMode 0 or 1, draw in the paint method. 
            else
                this.Refresh();
        }

        private void OnResize(object sender, EventArgs e)
        {
            // Re-create the graphics buffer for a new window size. 
            context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
            if (grafx != null)
            {
                grafx.Dispose();
                grafx = null;
            }
            grafx = context.Allocate(this.CreateGraphics(),
                new Rectangle(0, 0, this.Width, this.Height));

            // Cause the background to be cleared and redraw. 
            count = 6;
            DrawToBuffer(grafx.Graphics);
            this.Refresh();
        }

        private void DrawToBuffer(Graphics g)
        {
            // Clear the graphics buffer every five updates. 
            if (++count > 5)
            {
                count = 0;
                grafx.Graphics.FillRectangle(Brushes.Black, 0, 0, this.Width, this.Height);
            }

            // Draw randomly positioned and colored ellipses. 
            Random rnd = new Random();
            for (int i = 0; i < 20; i++)
            {
                int px = rnd.Next(20, this.Width - 40);
                int py = rnd.Next(20, this.Height - 40);
                g.DrawEllipse(new Pen(Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255)), 1),
                    px, py, px + rnd.Next(0, this.Width - px - 20), py + rnd.Next(0, this.Height - py - 20));
            }

            // Draw information strings. 
            g.DrawString("Buffering Mode: " + bufferingModeStrings[bufferingMode], new Font("Arial", 8), Brushes.White, 10, 10);
            g.DrawString("Right-click to cycle buffering mode", new Font("Arial", 8), Brushes.White, 10, 22);
            g.DrawString("Left-click to toggle timed display refresh", new Font("Arial", 8), Brushes.White, 10, 34);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            grafx.Render(e.Graphics);
        }

    }
}