【发布时间】:2011-11-22 09:12:58
【问题描述】:
我有一个用户控件,其中包含许多自己绘制的对象(从 OnPaint 调用)的完全自定义绘制的图形,背景是一个大位图。我内置了缩放和平移功能,画布上绘制的对象的所有坐标都在位图坐标中。
因此,如果我的用户控件是 1000 像素宽,位图是 1500 像素宽,并且我以 200% 的缩放比例缩放,那么在任何给定时间我只会看到位图宽度的 1/3。如果您滚动到最左侧,一个从位图上的点 100,100 开始的矩形对象将出现在屏幕上的点 200,200 处。
基本上我需要做的是创建一种仅重绘需要重绘的有效方法。例如,如果我移动一个对象,我可以将该对象的旧剪辑矩形添加到一个区域,并将该对象的新剪辑矩形合并到同一区域,然后调用 Invalidate(region) 重绘这两个区域。
但是,这样做意味着我必须不断地将对象位图坐标转换为屏幕坐标,然后再将它们提供给 Invalidate。我必须始终假设 PaintEventArgs 中的 ClipRectangle 位于屏幕坐标中,以防其他窗口使我的窗口无效。
有没有一种方法可以利用 Region.Transform 和 Region.Translate 功能,这样我就不需要从位图转换为屏幕坐标?在某种程度上它不会干扰在屏幕坐标中接收 PaintEventArgs?我应该使用多个区域还是有更好的方法来完成这一切?
我现在正在做的示例代码:
invalidateRegion.Union(BitmapToScreenRect(SelectedItem.ClipRectangle));
SelectedItem.UpdateEndPoint(endPoint);
invalidateRegion.Union(BitmapToScreenRect(SelectedItem.ClipRectangle));
this.Invalidate(invalidateRegion);
在 OnPaint() 中...
protected override void OnPaint(PaintEventArgs e)
{
invalidateRegion.Union(e.ClipRectangle);
e.Graphics.SetClip(invalidateRegion, CombineMode.Union);
e.Graphics.Clear(SystemColors.AppWorkspace);
e.Graphics.TranslateTransform(AutoScrollPosition.X + CanvasBounds.X, AutoScrollPosition.Y + CanvasBounds.Y);
DrawCanvas(e.Graphics, _ratio);
e.Graphics.ResetTransform();
e.Graphics.ResetClip();
invalidateRegion.MakeEmpty();
}
【问题讨论】:
-
你正在做不需要做的工作。 Windows 在剪辑方面已经非常高效,您无需提供帮助。如果您有性能问题,请关注位图的像素格式。 32bppPArgb 比其他任何速度快十倍。
-
我认为您不了解剪贴画的工作原理。我正在使用 GDI+ 绘制可以移动、调整大小、旋转等的对象。我需要重绘 MouseMove 等事件,并且我需要确保只重绘实际需要更新的部分。例如,如果我更改一个对象,它不会自行重绘,我必须调用 Invalidate()。如果我没有指定要剪辑到的剪辑区域,那么它将重绘整个场景。例如,您不想在 MouseMove 事件上重绘整个场景。
-
哇!告诉 Hans Passant 他不了解窗口剪辑的工作原理就像告诉 Jon Skeet 他不了解 C# 的工作原理! :-)
-
很公平。我不知道 Windows 根据之前的失效预设了 e.Graphics.Clip 区域。我以为它给了你 e.ClipRectangle,你必须用它来手动剪辑。尽管如此,为我说 Windows 剪辑并不能告诉我如何有效地使无效:)
标签: c# clipping region invalidation onpaint