【问题标题】:C#: Avoid flickering when painting?C#:绘画时避免闪烁?
【发布时间】:2016-11-18 14:12:49
【问题描述】:

我想制作一个允许画线的简单应用程序。当你用鼠标点击时,你设置了线的初始坐标。然后,通过移动鼠标,您可以延长或缩短线条。因此,每次我的面板(我使用 System.Windows.Forms)检测到我的鼠标在移动时,它都应该画一条新线,因为这将与前一个不同(甚至相差一个像素)。主要问题是,首先,我不知道如何在 C# 中处理重绘(我曾经使用 Java,在某些方面重绘更容易),其次,当我调用 panel.Invalidate( ) 方法,一切都在闪烁。我还尝试通过使用矩形作为参数来使用 panel.Invalidate(Region r) 方法,但它仍然闪烁。

这是我的面板操作的类。 Road 对象包含绘制线条的方法。在 panel1_paint(object sender, PaintEventArgs e) 方法中,我只为背景着色。在 panel1_MouseMove(object sender, MouseEventArgs e) 方法中,我画线。

partial class Form1 : Form
{
    Manager manager;

    Graphics g;

    Road road;
    Point initPosition;

    bool roadOn;
    bool mouseDown;

    public Form1(Manager manager)
    {
        InitializeComponent();

        this.manager = manager;

        g = panel1.CreateGraphics();

        road = new Road(0, 0, 0, 0, new Pen(Color.Gray, 10));
        initPosition = new Point(0, 0);

        roadOn = false;
        mouseDown = false;
   }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        panel1.BackColor = Color.LightGray;
    }

    private void Form1_KeyPress(object sender, KeyPressEventArgs e)
    {
        Point position = Cursor.Position;

        if(panel1.ClientRectangle.Contains(position))
        {
            if(e.KeyChar.ToString() == Keys.R.ToString().ToLower())
            {
                roadOn = true;
            }
        }
    }

    private void panel1_MouseDown(object sender, MouseEventArgs e)
    {
        mouseDown = true;

        initPosition = Cursor.Position;
        road.X = initPosition.X;
        road.Y = initPosition.Y;
    }

    private void panel1_MouseMove(object sender, MouseEventArgs e)
    {
        panel1.Invalidate();
        panel1.Dispose();
        panel1.Update();

        Point position = Cursor.Position;

        if(roadOn)
        {
            if(mouseDown)
            {
                road.X2 = position.X;
                road.Y2 = position.Y;

                road.paint(g);
            }
        }
    }

    private void panel1_MouseUp(object sender, MouseEventArgs e)
    {
        mouseDown = false;
    }
}

这是Road类:

class Road : GameObject
{
    Pen pen;
    public Pen Pen
    {
        get { return pen; }
        set { pen = value; }
    }

    int x2;
    public int X2
    {
        get { return x2; }
        set { x2 = value; }
    }

    int y2;
    public int Y2
    {
        get { return y2; }
        set { y2 = value; }
    }

    public Road(int x1, int y1, int x2, int y2, Pen pen) : base(x1, y1)
    {
        this.pen = pen;
    }

    override public void paint(Graphics g)
    {
        g.DrawLine(pen, x, y, x2, y2);
    }
}

【问题讨论】:

  • 听起来你想要一些双缓冲。您正在清除绘图区域,因此会闪烁。 msdn.microsoft.com/en-us/library/3t7htc9c(v=vs.110).aspx
  • 查看双缓冲
  • 我已经有一段时间没有做这种事情了,但我怀疑你应该只是记录鼠标坐标并在鼠标移动事件中调用Invalidate,然后在其中完成所有绘图Paint 事件。
  • 不妨看看this answer
  • 另外CreateGraphics 太可怕了,您应该始终在控件Paint 事件中进行绘画

标签: c# panel paint flicker invalidation


【解决方案1】:

Panel 控件有一个DoubleBuffered 属性,但它受到保护。最好的解决方案是扩展它以将属性覆盖为 true 并使用新类进行绘制:

创建一个继承自 Panel 的类:

//Make it sealed so you can call DoubleBuffered from the constructor safely.
public sealed class DoubleBufferedPanel : Panel
{
    public DoubleBufferedPanel()
    {
        //I want this class only for drawing so force the value to true here
        DoubleBuffered = true;
    }
}

将表单中的面板替换为之前创建的类的实例。

仅通过这个微小的更改,您应该会看到零闪烁绘图。

希望这会有所帮助!

【讨论】:

  • 谢谢,我试试。我不知道如何编辑 Form 类以及将新方法放在哪里。这是因为我使用了 Visual Studio 自动工具来创建面板和方法。
  • Visual Studio 设计器会根据您的操作生成代码。您可以在 Form1.Desginer.cs 中找到生成的代码(假设您的表单名为 Form1)。在解决方案资源管理器中展开嵌套在您的表单类下的文件。找到 panel1(您可以搜索它)并将类型更改为 DoubleBuffered 类。还要找到它在 InitializeComponent 方法中创建的部分。
  • 非常感谢!!!有效。无论如何感谢所有回答我问题的人:)。
【解决方案2】:

两周前我遇到了同样的问题。问题是您必须设置自定义控件样式以允许双缓冲。

SetStyle( ControlStyles.OptimizedDoubleBuffer, true );

您可以像这样覆盖 CreateParams 属性:

protected override CreateParams CreateParams
{
    get {
        CreateParams cp = base.CreateParams; cp.ExStyle = cp.ExStyle | 0x20;
        return cp;
    }
}

另一个问题是您使用 Invalidate() 方法请求对控件进行完全验证。改用 Refresh() 方法并删除 Invalidate() 和 Update() 方法。

希望这会有所帮助:)

【讨论】:

  • 不幸的是没有:(。我这样做了,但仍然闪烁。也许是因为这个问题取决于每个案例。
  • 为此,您应该覆盖面板类,例如:
  • 类 MePanel : 面板 { public MePanel() : base() { InitializeComponent(); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); } ... CreateParams ... }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
相关资源
最近更新 更多