我开发了一个Windows窗体应用程序,用于绘制与用户鼠标位置相关的矩形区域,因为他或她点击,抓住并拖动鼠标。
课程非常简单,如下:
public partial class MainForm : LayeredForm
{
private bool drawing = false;
private Point startLocation = Point.Empty;
public MainForm()
{
InitializeComponent();
Location = Screen.PrimaryScreen.Bounds.Location;
Size = Screen.PrimaryScreen.Bounds.Size;
Cursor = Cursors.Cross;
TopMost = true;
ShowInTaskbar = false;
DoubleBuffered = true;
}
private void MainForm_MouseDown(object sender, MouseEventArgs e)
{
drawing = true;
startLocation = e.Location;
}
private void MainForm_MouseMove(object sender, MouseEventArgs e)
{
if (drawing)
Invalidate();
}
private void MainForm_MouseUp(object sender, MouseEventArgs e)
{
drawing = false;
}
private void MainForm_Paint(object sender, PaintEventArgs e)
{
Rectangle r = new Rectangle(Math.Min(startLocation.X, Cursor.Position.X), Math.Min(startLocation.Y, Cursor.Position.Y),
Math.Abs(startLocation.X - Cursor.Position.X), Math.Abs(startLocation.Y - Cursor.Position.Y));
e.Graphics.FillRectangle(Brushes.Blue, r);
}
}
public class LayeredForm : Form
{
public new event PaintEventHandler Paint;
public LayeredForm()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
StartPosition = FormStartPosition.Manual;
}
protected override CreateParams CreateParams
{
get
{
if (DesignMode) return base.CreateParams;
CreateParams cParams = base.CreateParams;
cParams.ExStyle = cParams.ExStyle | 0x80000;
return cParams;
}
}
private void PaintNative(Bitmap bitmap)
{
IntPtr hdcDestination = Win32.GetDC(IntPtr.Zero);
IntPtr hdcSource = Win32.CreateCompatibleDC(hdcDestination);
IntPtr hdcBitmap = IntPtr.Zero;
IntPtr previousBitmap = IntPtr.Zero;
try
{
hdcBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
previousBitmap = Win32.SelectObject(hdcSource, hdcBitmap);
Win32.SIZE size = new Win32.SIZE(bitmap.Width, bitmap.Height);
Win32.POINT source = new Win32.POINT(0, 0);
Win32.POINT destination = new Win32.POINT(Left, Top);
Win32.BLENDFUNCTION blendFunc = new Win32.BLENDFUNCTION()
{
BlendOp = Win32.AC_SRC_OVER,
BlendFlags = 0,
SourceConstantAlpha = 50,
AlphaFormat = Win32.AC_SRC_ALPHA
};
Win32.UpdateLayeredWindow(Handle, hdcDestination, ref destination, ref size, hdcSource, ref source, 0, ref blendFunc, 2);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, hdcDestination);
if (hdcBitmap != IntPtr.Zero)
{
Win32.SelectObject(hdcSource, previousBitmap);
Win32.DeleteObject(hdcBitmap);
}
Win32.DeleteDC(hdcSource);
}
}
public new void Invalidate()
{
using (Bitmap bitmap = new Bitmap(ClientSize.Width, ClientSize.Height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
if (Paint != null)
Paint(this, new PaintEventArgs(graphics, Rectangle.Empty));
}
PaintNative(bitmap);
}
}
}
在我的电脑和大多数其他电脑上,一切运行正常。然而,在对各种机器进行了一些测试之后,我发现有些计算机在努力绘制矩形(明显缓慢且波涛汹涌)。我想我已经确定了这个问题,但我需要验证并解决这个问题。
我认为问题在于,每次引发鼠标移动事件时,都会再次绘制整个表单。在分辨率较高的弱电脑上,这非常费劲。
在进行一些研究后,我认为解决方案是仅绘制已更改的矩形部分而不是整个表格,尽管我对如何执行此操作毫无头绪。我非常感谢SO提供的任何帮助,提前感谢。
Ken的完整代码:
public sealed partial class RegionForm : LayeredWindow // : Form
{
private Bitmap bitmap;
private bool mouseDown;
private Point newPoint = Point.Empty;
private Point oldPoint = Point.Empty;
private Point startPoint = Point.Empty;
public RegionForm()
{
InitializeComponent();
Location = Screen.PrimaryScreen.Bounds.Location;
Size = Screen.PrimaryScreen.Bounds.Size;
Cursor = Cursors.Cross;
TopMost = true;
ShowInTaskbar = false;
}
private void regionPanel_MouseDown(object sender, MouseEventArgs e)
{
bitmap = new Bitmap(regionPanel.ClientSize.Width,
regionPanel.ClientSize.Height,
PixelFormat.Format32bppPArgb);
regionPanel.DrawToBitmap(bitmap, regionPanel.ClientRectangle);
startPoint = e.Location;
mouseDown = true;
}
private void regionPanel_MouseUp(object sender, MouseEventArgs e)
{
mouseDown = false;
if (bitmap != null)
{
bitmap.Dispose();
bitmap = null;
}
Rectangle region = new Rectangle(startPoint, new Size(oldPoint.X - startPoint.X + 1,
oldPoint.Y - startPoint.Y + 1));
regionPanel.Invalidate(region, true);
}
private void regionPanel_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown)
{
using (Graphics g = regionPanel.CreateGraphics())
{
g.SmoothingMode = SmoothingMode.None;
newPoint = e.Location;
ClearRegion(g);
oldPoint = newPoint;
DrawRegion(g);
}
}
}
private void DrawRegion(Graphics g)
{
int x1 = startPoint.X;
int y1 = startPoint.Y;
int x2 = newPoint.X;
int y2 = newPoint.Y;
//block "negative" selection
if (x1 > x2)
{
x2 = x1;
}
if (y1 > y2)
{
y2 = y1;
}
//Draw a red rectangle
g.FillRectangle(Brushes.Red, x1, y1, x2 - x1, y2 - y1);
}
private void ClearRegion(Graphics g)
{
int x1 = startPoint.X;
int y1 = startPoint.Y;
int x2 = oldPoint.X;
int y2 = oldPoint.Y;
if (x1 > x2)
{
x2 = x1;
}
if (y1 > y2)
{
y2 = y1;
}
//check left line
if (newPoint.Y < y2)
{
Rectangle rectdst = new Rectangle(x1, newPoint.Y, 1, oldPoint.Y - newPoint.Y);
g.DrawImage(bitmap, rectdst, rectdst, GraphicsUnit.Pixel);
}
//upper line
if (newPoint.X < x2)
{
Rectangle rectdst = new Rectangle(newPoint.X, y1, oldPoint.X - newPoint.X, 1);
g.DrawImage(bitmap, rectdst, rectdst, GraphicsUnit.Pixel);
}
//right line
if (newPoint.X != x2 || newPoint.Y < y2)
{
int h = 0;
int y = 0;
if (newPoint.X == x2)
{
y = newPoint.Y;
h = oldPoint.Y - newPoint.Y + 1;
}
else
{
y = startPoint.Y;
h = oldPoint.Y - startPoint.Y + 1;
}
Rectangle rectdst = new Rectangle(oldPoint.X, y, 1, h);
g.DrawImage(bitmap, rectdst, rectdst, GraphicsUnit.Pixel);
}
//bottom line
if (newPoint.Y != y2 || newPoint.X < x2)
{
int w = 0;
int x = 0;
if (newPoint.Y == y2)
{
x = newPoint.X;
w = oldPoint.X - newPoint.X + 1;
}
else
{
x = startPoint.X;
w = oldPoint.X - x1 + 1;
}
Rectangle rectdst = new Rectangle(x, oldPoint.Y, w, 1);
g.DrawImage(bitmap, rectdst, rectdst, GraphicsUnit.Pixel);
}
}
public class LayeredWindow : Form
{
public new event PaintEventHandler Paint;
public LayeredWindow()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
StartPosition = FormStartPosition.Manual;
}
protected override CreateParams CreateParams
{
get
{
if (DesignMode) return base.CreateParams;
CreateParams cParams = base.CreateParams;
cParams.ExStyle = cParams.ExStyle | 0x80000;
return cParams;
}
}
private void PaintNative(Bitmap bitmap)
{
IntPtr hdcDestination = Win32.GetDC(IntPtr.Zero);
IntPtr hdcSource = Win32.CreateCompatibleDC(hdcDestination);
IntPtr hdcBitmap = IntPtr.Zero;
IntPtr previousBitmap = IntPtr.Zero;
try
{
hdcBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
previousBitmap = Win32.SelectObject(hdcSource, hdcBitmap);
Size size = new Size(bitmap.Width, bitmap.Height);
Point source = new Point(0, 0);
Point destination = new Point(Left, Top);
Win32.BLENDFUNCTION blendFunc = new Win32.BLENDFUNCTION()
{
BlendOp = Win32.AC_SRC_OVER,
BlendFlags = 0,
SourceConstantAlpha = 50,
AlphaFormat = Win32.AC_SRC_ALPHA
};
Win32.UpdateLayeredWindow(Handle, hdcDestination, ref destination, ref size, hdcSource, ref source, 0, ref blendFunc, 2);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, hdcDestination);
if (hdcBitmap != IntPtr.Zero)
{
Win32.SelectObject(hdcSource, previousBitmap);
Win32.DeleteObject(hdcBitmap);
}
Win32.DeleteDC(hdcSource);
}
}
public new void Invalidate()
{
using (Bitmap bitmap = new Bitmap(ClientSize.Width, ClientSize.Height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
if (Paint != null)
Paint(this, new PaintEventArgs(graphics, Rectangle.Empty));
}
PaintNative(bitmap);
}
}
}
public sealed class Win32
{
[DllImport("user32.dll")]
public static extern bool HideCaret(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern short GetKeyState(int keyCode);
[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("user32.dll")]
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pptSrc, uint crKey, [In] ref BLENDFUNCTION pblend, uint dwFlags);
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
public const byte AC_SRC_OVER = 0;
public const byte AC_SRC_ALPHA = 1;
public const byte ULW_ALPHA = 2;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
答案 0 :(得分:3)
您需要从MouseMove
事件中提取它。在某些计算机/分辨率上,整个控件的无效将会很慢。
更新:
此代码将以高度优化的方式绘制和清除所选区域的矩形。
例程在MouseMove
上绘制一个矩形,但只根据需要清除行的片段。也就是说,如果该区域小于先前的大小,则仅从存储的位图中清除行的外部部分。
由于这个原因,不需要启用双缓冲。
为了将窗口的缓冲位图作为源副本放在正确的位置,我们需要实现一个小技巧:
Panel
(在此示例中为panFill)并将dock设置为fill。现在我们可以从Panel
而不是Form
复制我们稍后需要的位图。我们需要这个的原因是使用this.DrawBitmap()
也会为位图绘制边框和标题栏,我们不希望这样。使用Panel
将消除此问题。
在表格的全局范围内,我们设置:
Bitmap bmp = null;
bool inDrag = false;
Point regStart = Point.Empty;
Point regNew = Point.Empty;
Point regOld = Point.Empty;
在Panel的MouseDown / Up事件中:
private void panFill_MouseDown(object sender, MouseEventArgs e)
{
//Create a bitmap
bmp = new Bitmap(panFill.ClientSize.Width, _
panFill.ClientSize.Height, _
Imaging.PixelFormat.Format32bppPArgb);
panFill.DrawToBitmap(bmp, panFill.ClientRectangle);
//store origo/start point and mark that we're active
regStart = e.Location;
inDrag = true;
}
private void panFill_MouseUp(object sender, MouseEventArgs e)
{
inDrag = false;
//we're done, clean up resources if any
if (bmp != null) {
bmp.Dispose();
bmp = null; //use as marker for this check
}
//clean up by redrawing panel
Rectangle r = new Rectangle(regStart, _
new Size(regOld.X - regStart.X + 1, _
regOld.Y - regStart.Y + 1));
panFill.Invalidate(r, true);
}
在我们的MouseMove
事件中,我们调用clear并绘制一个新矩形:
private void panFill_MouseMove(object sender, MouseEventArgs e)
{
if (inDrag) {
using (Graphics g = panFill.CreateGraphics) {
g.SmoothingMode = Drawing2D.SmoothingMode.None;
//we store new pos. here as it's used to calculate
//delta for what we need to redraw
regNew = e.Location;
ClearRegion(g);
regOld = regNew;
DrawRegion(g);
}
}
}
绘制矩形的功能非常简单:
private void DrawRegion(Graphics g)
{
int x1 = regStart.X;
int y1 = regStart.Y;
int x2 = regNew.X;
int y2 = regNew.Y;
//block "negative" selection
if (x1 > x2) {
x2 = x1;
}
if (y1 > y2) {
y2 = y1;
}
//Draw a red rectangle
g.DrawRectangle(Pens.Red, x1, y1, x2 - x1, y2 - y1);
}
下一个方法包含所有优化的魔法,只绘制所需的内容。它通过检查origo中的两条主线以及长度是否缩小来实现此目的。如果是这样,它计算旧位置和新位置的delta,只重绘“间隙”。
对于其他两条线,如果切线位置相同则相同,否则重绘整条线。
private void ClearRegion(Graphics g)
{
int x1 = regStart.X;
int y1 = regStart.Y;
int x2 = regOld.X;
int y2 = regOld.Y;
if (x1 > x2) {
x2 = x1;
}
if (y1 > y2) {
y2 = y1;
}
//check left line
if (regNew.Y < y2) {
Rectangle rectdst = new Rectangle(x1, regNew.Y, 1, regOld.Y - regNew.Y);
g.DrawImage(bmp, rectdst, rectds, GraphicsUnit.Pixel);
}
//upper line
if (regNew.X < x2) {
Rectangle rectdst = new Rectangle(regNew.X, y1, regOld.X - regNew.X, 1);
g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel);
}
//right line
if (regNew.X != x2 || regNew.Y < y2) {
int h = 0;
int y = 0;
if (regNew.X == x2) {
y = regNew.Y;
h = regOld.Y - regNew.Y + 1;
} else {
y = regStart.Y;
h = regOld.Y - regStart.Y + 1;
}
Rectangle rectdst = new Rectangle(regOld.X, y, 1, h);
g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel);
}
//bottom line
if (regNew.Y != y2 || regNew.X < x2) {
int w = 0;
int x = 0;
if (regNew.Y == y2) {
x = regNew.X;
w = regOld.X - regNew.X + 1;
} else {
x = regStart.X;
w = regOld.X - x1 + 1;
}
Rectangle rectdst = new Rectangle(x, regOld.Y, w, 1);
g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel);
}
}
我想说,这是你在表格上获得的最多GDI +。不同的方法涉及DirectX和更低级别的东西。通过限制根据需要重绘的内容,速度是最佳的。
另请注意,填充面板的位图副本是PARG,这是最快的类型,因为alpha是预乘的。我虽然在这种情况下不是必需的,但是将平滑模式设置为无,这样线条就会保持清晰,没有任何混叠泄漏。
在Windows XP 32位,2 GB内存和Atom CPU上,它运行顺畅。
注意:原始代码用VB编写并转换为C#。可能会发生一些错误 - 根据需要进行调整。根据需要添加处理程序。
感兴趣的人的原始VB代码:
Public Class Form1
Private bmp As Bitmap = Nothing
Private inDrag As Boolean = False
Private regStart As Point = Point.Empty
Private regNew As Point = Point.Empty
Private regOld As Point = Point.Empty
Public Event RegionSelected(r As Rectangle)
Private Sub panfill_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles panFill.MouseDown
bmp = New Bitmap(panFill.ClientSize.Width, panFill.ClientSize.Height, Imaging.PixelFormat.Format32bppPArgb)
panFill.DrawToBitmap(bmp, panFill.ClientRectangle)
regStart = e.Location
inDrag = True
End Sub
Private Sub panFill_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles panFill.MouseMove
If inDrag Then
Using g As Graphics = panFill.CreateGraphics
g.SmoothingMode = Drawing2D.SmoothingMode.None
regNew = e.Location
ClearRegion(g)
regOld = regNew
DrawRegion(g)
End Using
End If
End Sub
Private Sub panFill_MouseUp(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles panFill.MouseUp
inDrag = False
If bmp IsNot Nothing Then
bmp.Dispose()
bmp = Nothing
End If
Dim r As New Rectangle(regStart, New Size(regOld.X - regStart.X + 1, regOld.Y - regStart.Y + 1))
panFill.Invalidate(r, True)
RaiseEvent RegionSelected(r)
End Sub
Private Sub DrawRegion(g As Graphics)
Dim x1, y1, x2, y2 As Integer
x1 = regStart.X
y1 = regStart.Y
x2 = regNew.X
y2 = regNew.Y
If x1 > x2 Then
x2 = x1
End If
If y1 > y2 Then
y2 = y1
End If
g.DrawRectangle(Pens.Red, x1, y1, x2 - x1, y2 - y1)
End Sub
Private Sub ClearRegion(g As Graphics)
Dim x1, y1, x2, y2 As Integer
x1 = regStart.X
y1 = regStart.Y
x2 = regOld.X
y2 = regOld.Y
If x1 > x2 Then
x2 = x1
End If
If y1 > y2 Then
y2 = y1
End If
'left line
If regNew.Y < y2 Then
Dim rectdst As New Rectangle(x1, regNew.Y, 1, regOld.Y - regNew.Y)
g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel)
End If
'upper line
If regNew.X < x2 Then
Dim rectdst As New Rectangle(regNew.X, y1, regOld.X - regNew.X, 1)
g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel)
End If
'right line
If regNew.X <> x2 OrElse regNew.Y < y2 Then
Dim h, y As Integer
If regNew.X = x2 Then
y = regNew.Y
h = regOld.Y - regNew.Y + 1
Else
y = regStart.Y
h = regOld.Y - regStart.Y + 1
End If
Dim rectdst As New Rectangle(regOld.X, y, 1, h)
g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel)
End If
'bottom line
If regNew.Y <> y2 OrElse regNew.X < x2 Then
Dim w, x As Integer
If regNew.Y = y2 Then
x = regNew.X
w = regOld.X - regNew.X + 1
Else
x = regStart.X
w = regOld.X - x1 + 1
End If
Dim rectdst As New Rectangle(x, regOld.Y, w, 1)
g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel)
End If
End Sub
End Class
答案 1 :(得分:2)
好的Alex,这里是你要求的代码示例。
首先,您必须将一些样式设置到您自己绘制的控件上。这是DoubleBuffered,AllPaintingInWmPaint和UserPaint。这可以避免闪烁(参见this example)。其次,不要在MouseMove事件本身中绘制。记住绘画所需的所有数据并调用Invalidate。在覆盖方法“Control.OnPaint”或事件“Cotrol.Paint”中进行所有绘制。我还添加了一个KeyPress处理程序,以便我可以再次关闭RegionForm。
在MouseDown事件中,我记得鼠标光标的位置和控件的当前外观(位图)。当MouseMove我计算鼠标按下位置和当前鼠标光标位置之间的向量(距离)。如果用户转到鼠标按下点的左上侧,这也有效。此矩形是必须绘制的新区域。但我们也必须使旧区域无效。这就是为什么我计算一个包含旧区域和新区域的联合矩形的原因。此联合用于调用“Control.Invalidate”。清除旧区域只需通过仅绘制已保存图像的特定部分即可完成。
public sealed partial class RegionForm : Form
{
private Bitmap bitmap;
private bool mouseDown;
private Rectangle newRegion;
private Rectangle oldRegion;
private Point startPoint;
public RegionForm()
{
InitializeComponent();
Location = Screen.PrimaryScreen.Bounds.Location;
Size = Screen.PrimaryScreen.Bounds.Size;
Cursor = Cursors.Cross;
TopMost = true;
ShowInTaskbar = false;
this.SetStyle(
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.DoubleBuffer, true);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
if (e.KeyChar == (char)Keys.Escape)
this.Close();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
bitmap = new Bitmap(this.ClientSize.Width,
this.ClientSize.Height,
PixelFormat.Format32bppPArgb);
this.DrawToBitmap(bitmap, this.ClientRectangle);
startPoint = e.Location;
mouseDown = true;
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
mouseDown = false;
if (bitmap != null)
{
bitmap.Dispose();
bitmap = null;
}
// reset regions
newRegion = Rectangle.Empty;
oldRegion = Rectangle.Empty;
// invalidate all
Invalidate(true);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (mouseDown)
{
// calculate new region
var vector = Point.Subtract(e.Location, new Size(startPoint));
newRegion = new Rectangle(System.Math.Min(startPoint.X, e.Location.X), System.Math.Min(startPoint.Y, e.Location.Y), System.Math.Abs(vector.X), System.Math.Abs(vector.Y));
// invalidate only the area of interest
var invalidate = Rectangle.Union(oldRegion, newRegion);
Invalidate(invalidate, true);
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.None;
ClearRegion(e.Graphics, bitmap, oldRegion);
DrawRegion(e.Graphics, newRegion);
// remember which region has been handled
oldRegion = newRegion;
}
static void DrawRegion(Graphics g, Rectangle region)
{
if (g == null || region == Rectangle.Empty)
return;
//Draw a red rectangle
g.FillRectangle(Brushes.Red, region);
}
static void ClearRegion(Graphics g, Bitmap bitmap, Rectangle region)
{
if (g == null || region == Rectangle.Empty || bitmap == null)
return;
// take only the selected region from the original image and draw that part
g.DrawImage(bitmap, region, region, GraphicsUnit.Pixel);
}
答案 2 :(得分:0)
这在很大程度上取决于表单上可见的内容。例如,如果您自己绘制了许多自己的对象,则需要在每次鼠标移动事件中使整个表单无效。
否则您只需要使特定区域无效。然后,您需要使用背景颜色清除该区域,并调用完全或部分位于该区域中的所有对象以重新绘制自己。之后,您可以绘制DragDrop光标/图形。
大多数情况下,确定无效区域中的对象太复杂了。