2016-11-18 127 views
0

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

这是我的面板操作的类。 Road对象包含绘制线条的方法。在panel1_paint(对象发件人,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; 
    } 
} 

这是类:

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

听起来像你想要一些双缓冲。您正在渲染绘图区域的清除,因此闪烁。 https://msdn.microsoft.com/en-us/library/3t7htc9c(v=vs.110).aspx – Charleh

+0

研究双缓冲 – ChrisF

+1

我有一段时间没有做过这种事情,但我怀疑你应该只是记录鼠标坐标并在鼠标移动事件中调用“Invalidate”,然后在“Paint”事件中执行所有绘图。 –

回答

1

Panel控制具有DoubleBuffered属性,但它的保护。那么最好的解决方案是其扩展为覆盖属性为true,并使用新的类来绘制:

创建一个类从面板继承:

//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以前创建的类。

只有这个小小的变化,你应该看到零闪烁绘图。

希望这会有所帮助!

+0

谢谢,我会尝试。我不完全如何编辑Form类和放置新的位置这是因为我使用了visual studio自动工具来创建面板和方法 –

+0

Visual Studio设计器根据您的操作生成代码您可以在Form1.Desginer.cs中找到生成的代码(假设您的表单称为Form1 )在解决方案资源管理器中,展开嵌套在表单类下的文件,找到panel1(可以搜索它),并将类型更改为DoubleBuffered类,并找到它创建的部分在InitializeComponent方法中。 –

+1

非常感谢!有效。无论如何感谢大家回答我的问题:)。 –

0

我有同样的问题,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()方法。

希望这将有助于:)

+0

不幸的是,我做到了,但仍然是闪烁,也许是因为这个问题取决于每一个案例。 –

+0

为此,您应该覆盖面板类,如: –

+0

class MePanel:Panel {public MePanel():base (){InitializeComponent(); SetStyle(ControlStyles.OptimizedDoubleBuffer,true);} ... CreateParams ...} –