2016-01-06 42 views
4

我正在尝试在Visual Studio 2015中创建一个小的绘画应用程序。我的项目属于Windows窗体应用程序的类别。我有以下问题:如何在表单刷新后保留绘制的形状?

private void Form1_MouseMove(object sender, MouseEventArgs e) 
    { 
     if (a == 1) 
     { 
      if (r == 1 || el == 1) 
      { 
       int x = Math.Min(inX, e.X); 
       int y = Math.Min(inY, e.Y); 
       int width = Math.Max(inX, e.X) - Math.Min(inX, e.X); 
       int height = Math.Max(inY, e.Y) - Math.Min(inY, e.Y); 
       rect = new Rectangle(x, y, width, height); 
       Refresh(); 
      } 
      else if (l == 1) 
      { 
       ep = e.Location; 
       Refresh(); 
      } 
      else 
      { 
       ep = e.Location; 
       g = this.CreateGraphics(); 
       g.DrawLine(p, sp, ep); 
       sp = ep; 
      } 
     } 
    } 

我的代码这部分创建一个矩形(第二如果),线段(如果),只是一条线。它和MS Paint非常相似;直到用户释放鼠标左键(鼠标向上),矩形或线段才会完成。但是当一个矩形最终被创建时,当我再次尝试创建另一个时,表单刷新(Refresh();),并且我丢失了所有先前绘制的矩形或线条。我尝试更换Refresh();无效(rect);Update();,但我没有得到我想要的结果。

相反,我得到这样的:

enter image description here

+0

立即(就像您的情况下的GDI)vs保留模式解释 - https://msdn.microsoft.com/en-us/library/windows/desktop/ff684178(v=vs.85).aspx –

回答

6

你应该做你的绘图到一个单独的位图buffer你保持周围。将您的形状绘制到该位图上,然后在实际需要更新屏幕时,将缓冲区绘制到屏幕上。

此外,您随时拨打CreateGraphics您需要记住Dispose,否则它会像疯了一样泄露资源。

令人难以置信的是简单的例子

using System; 
using System.Drawing; 
using System.Drawing.Imaging; 
using System.Windows.Forms; 

namespace DrawExample 
{ 
    public partial class Form1 : Form 
    { 

     private Bitmap _canvas; //This is the offscreen drawing buffer 
     private Point _anchor; //The start point for click-drag operations 
     private Rectangle? _ghost; 
     private Brush _ghostBrush; 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      _ghostBrush = new SolidBrush(Color.FromArgb(200, 200, 200, 255)); //This creates a slightly blue, transparent brush for the ghost preview 
      ResizeCanvas(); 
     } 

     private void Form1_Resize(object sender, EventArgs e) 
     { 
      ResizeCanvas(); 
     } 

     /// <summary> 
     /// Resizes the offscreen bitmap to match the current size of the window, it preserves what is currently in the bitmap. 
     /// </summary> 
     private void ResizeCanvas() 
     { 
      Bitmap tmp = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppRgb); 
      using (Graphics g = Graphics.FromImage(tmp)) 
      { 
       g.Clear(Color.White); 
       if (_canvas != null) 
       { 
        g.DrawImage(_canvas, 0, 0); 
        _canvas.Dispose(); 
       } 
      } 
      _canvas = tmp; 
     } 

     private void Form1_MouseDown(object sender, MouseEventArgs e) 
     { 
      if (e.Button == MouseButtons.Left) 
      { 
       _anchor = new Point(e.X, e.Y); 
      } 
     } 
     private void Form1_MouseMove(object sender, MouseEventArgs e) 
     { 
      if (e.Button == MouseButtons.Left) 
      { 
       _ghost = new Rectangle(_anchor.X, _anchor.Y, e.X - _anchor.X, e.Y - _anchor.Y); 
       this.Invalidate(); 
      } 
     } 

     private void Form1_MouseUp(object sender, MouseEventArgs e) 
     { 
      if (e.Button == MouseButtons.Left) 
      { 
       //Create a Graphics for the offscreen bitmap 
       using (Graphics g = Graphics.FromImage(_canvas)) 
       { 
        Rectangle rect = new Rectangle(_anchor.X, _anchor.Y, e.X - _anchor.X, e.Y - _anchor.Y); 
        g.FillRectangle(Brushes.White, rect); 
        g.DrawRectangle(Pens.Black, rect); 
       } 

       _ghost = null; 

       //This queues up a redraw call for the form 
       this.Invalidate(); 
      } 
     } 

     private void Form1_Paint(object sender, PaintEventArgs e) 
     { 

      if (_ghost.HasValue) 
      { 
       using (Bitmap tmp = new Bitmap(_canvas)) 
       { 
        using (Graphics g = Graphics.FromImage(tmp)) 
        { 
         g.FillRectangle(_ghostBrush, _ghost.Value); 
         g.DrawRectangle(Pens.Black, _ghost.Value); 

         e.Graphics.DrawImage(tmp, 0, 0); 
        } 
       } 
      } 
      else 
      { 
       e.Graphics.DrawImage(_canvas, 0, 0); 
      } 
     } 


     //This stops the flickering 
     protected override void OnPaintBackground(PaintEventArgs e) 
     { 
      //Do nothing 
     } 
    } 
} 
+0

谢谢为了这样一个快速的答案。我正在寻找一个位图缓冲区教程,但我似乎无法找到有用和彻底的东西。你有没有一个指导? – Xaris

+0

我为你添加了一些笔记给我的答案。 –

+0

这是一个显示更完整代码的MSDN示例。它没有使用大卫和我建议的持久缓冲方法。但它确实给出了一个创建和绘制到屏幕外位图的好例子。 https://msdn.microsoft.com/en-us/library/ms172506(v=vs.90).aspx –

3

您直接绘制到窗体的绘图表面。该表面不是持久的。它持续到下一个绘画周期。

相反,你应该:

  1. 画上了屏幕外的位图。
  2. 将该位图绘制到例如图片框控件上。或者在其Paint事件中将其直接绘制到表单的绘图表面上。
+0

或任何其他类型的图片模型 - WPF对象树,自定义树/对象列表(如矩形数组)。 –

+0

@Alexei的确如此 –

+0

@DavidHeffernan首先,感谢这样一个直接的答案。在我看来,你的第二个提议更容易。但是如果我在表单中创建一个图片框,我可以在其中绘制一个位图。我仍在学习,所以感谢您的耐心。 – Xaris