2013-08-24 55 views
0

我一直在C#中创建一个应用程序,该应用程序旨在模仿Windows的“截断工具”应用程序的外观和行为。一切都很好,除了双缓冲(用于停止屏幕闪烁)显得缓慢和滞后。这并没有太大的滞后,但是在比较我的程序和Snipping Tool之间的性能时,这足以让人注意。我还可以做些什么来提高性能,并使其看起来像没有滞后,就像在剪切工具中一样?C#双缓冲导致绘制滞后

public Image Image { get; set; } 

    private Rectangle selection; 
    private Point startPoint; 

    public static Image Snip() 
    { 
     using (var bmp = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)) 
     { 
      using (var graphics = Graphics.FromImage(bmp)) graphics.CopyFromScreen(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top, 0, 0, bmp.Size); 
      using (var snipper = new CaptureScreen(bmp, new Point(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top))) 
      { 
       if (snipper.ShowDialog() == DialogResult.OK) return snipper.Image; 
      } 
      return null; 
     } 
    } 

    public CaptureScreen(Image screenShot, Point startPos) 
    { 
     InitializeComponent(); 

     Cursor = Cursors.Cross; 
     BackgroundImage = screenShot; 
     ShowInTaskbar = false; 
     FormBorderStyle = FormBorderStyle.None; 
     StartPosition = FormStartPosition.Manual; 
     Size = screenShot.Size; 
     Location = startPos; 
     SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true); 
    } 

    protected override void OnMouseDown(MouseEventArgs e) 
    { 
     if (e.Button != MouseButtons.Left) return; 
     startPoint = e.Location; 
     selection = new Rectangle(e.Location, new Size(0, 0)); 
     Invalidate(); 
    } 

    protected override void OnMouseMove(MouseEventArgs e) 
    { 
     if (e.Button != MouseButtons.Left) return; 
     var x1 = Math.Min(e.X, startPoint.X); 
     var y1 = Math.Min(e.Y, startPoint.Y); 
     var x2 = Math.Max(e.X, startPoint.X); 
     var y2 = Math.Max(e.Y, startPoint.Y); 
     selection = new Rectangle(x1, y1, x2 - x1, y2 - y1); 
     Invalidate(); 
    } 

    protected override void OnMouseUp(MouseEventArgs e) 
    { 
     if (selection.Width <= 0 || selection.Height <= 0) return; 
     Image = new Bitmap(selection.Width, selection.Height); 
     using (var gr = Graphics.FromImage(Image)) 
     { 
      gr.DrawImage(BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height), 
       selection, GraphicsUnit.Pixel); 
     } 
     DialogResult = DialogResult.OK; 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     using (var br = new SolidBrush(Color.FromArgb(127, Color.White))) 
     { 
      var x1 = selection.X; 
      var x2 = selection.X + selection.Width; 
      var y1 = selection.Y; 
      var y2 = selection.Y + selection.Height; 

      e.Graphics.FillRectangle(br, new Rectangle(0, 0, x1, Height)); 
      e.Graphics.FillRectangle(br, new Rectangle(x2, 0, Width - x2, Height)); 
      e.Graphics.FillRectangle(br, new Rectangle(x1, 0, x2 - x1, y1)); 
      e.Graphics.FillRectangle(br, new Rectangle(x1, y2, x2 - x1, Height - y2)); 
     } 
     using (var pen = new Pen(Color.Red, 1)) 
     { 
      e.Graphics.DrawRectangle(pen, selection); 
     } 
    } 

我认为它落后于这种方式的原因是因为该应用程序创建一个屏幕截图并调整捕捉窗口,以匹配所有屏幕的尺寸。我有一种感觉,这就是截屏工具,但它仍然执行得更快。

回答

0

而不是使用Invalidate()每当您执行鼠标移动操作时,将会使控件的整个绘图区域失效,从而导致更多的绘画工作被完成...您可以通过仅使区域失效来提高效率您需要使用指定的Rectangle使用Invalidate()进行更改。

OnPaint被称为... e.ClipRectangle ...等同于需要更新的“脏”区域.....您可以使用它来优化您在OnPaint中执行的绘画工作。

您也可以缓存创建红色Pen和白色SolidBrush而不是在每次发生OnPaint时创建它们......您可能会也可能不会看到有任何区别。

这里是你的榜样的更新:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 

namespace WindowsFormsApplication3 
{ 
    public partial class CaptureScreen : Form 
    { 
     public Image Image { get; set; } 

     private Rectangle selection; 
     private Rectangle previousselection; 
     private Point startPoint; 

     public static Image Snip() 
     { 
      using (var bmp = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)) 
      { 
       using (var graphics = Graphics.FromImage(bmp)) graphics.CopyFromScreen(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top, 0, 0, bmp.Size); 
       using (var snipper = new CaptureScreen(bmp, new Point(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top))) 
       { 
        if (snipper.ShowDialog() == DialogResult.OK) return snipper.Image; 
       } 
       return null; 
      } 
     } 

     public CaptureScreen(Image screenShot, Point startPos) 
     { 
      InitializeComponent(); 

      Cursor = Cursors.Cross; 
      BackgroundImage = screenShot; 
      ShowInTaskbar = false; 
      FormBorderStyle = FormBorderStyle.None; 
      StartPosition = FormStartPosition.Manual; 
      Size = screenShot.Size; 
      Location = startPos; 
      SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true); 
     } 

     protected override void OnMouseDown(MouseEventArgs e) 
     { 
      if (e.Button != MouseButtons.Left) return; 
      startPoint = e.Location; 
      selection = new Rectangle(e.Location, new Size(0, 0)); 
      previousselection = selection; 
      Invalidate(); 
     } 

     protected override void OnMouseMove(MouseEventArgs e) 
     { 
      if (e.Button != MouseButtons.Left) return; 
      var x1 = Math.Min(e.X, startPoint.X); 
      var y1 = Math.Min(e.Y, startPoint.Y); 
      var x2 = Math.Max(e.X, startPoint.X); 
      var y2 = Math.Max(e.Y, startPoint.Y); 
      Invalidate(previousselection); // invalidate old rect area so it gets blanked out 
      previousselection = selection; 
      selection = new Rectangle(x1, y1, x2 - x1, y2 - y1); 
      Invalidate(selection); // invalidate new rect area so it gets drawn 
     } 

     protected override void OnMouseUp(MouseEventArgs e) 
     { 
      if (selection.Width <= 0 || selection.Height <= 0) return; 
      Image = new Bitmap(selection.Width, selection.Height); 
      using (var gr = Graphics.FromImage(Image)) 
      { 
       gr.DrawImage(BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height), 
        selection, GraphicsUnit.Pixel); 
      } 
      DialogResult = DialogResult.OK; 
     } 

     protected override void OnPaint(PaintEventArgs e) 
     { 
      using (var br = new SolidBrush(Color.FromArgb(127, Color.White))) 
      using (var pen = new Pen(Color.Red, 1)) 
      using (Region region = new Region(new Rectangle(0,0,Width,Height))) 
      { 
       region.Exclude(selection); 
       region.Intersect(e.ClipRectangle); 
       e.Graphics.FillRegion(br, region); 
       e.Graphics.DrawRectangle(pen, selection.X, selection.Y, selection.Width - 1, selection.Height - 1); 
      } 
     } 
    } 
} 
+0

这无疑增加了十倍的性能。我现在注意到的唯一情况是,如果选区变得太大,选区会再次滞后。我使用的是双显示器,当我从两个屏幕上拖动时,效果会模糊地开始重复(并不是很糟糕)。它不像以前那么明显,但它绝对好得多。谢谢! – Dragonphase

+0

其实,从头开始,我只是将它与Windows的“剪切工具”进行比较,它几乎是一样的!再一次,谢谢! – Dragonphase

+0

请参阅通过使用处理Region的版本。 –