ユーザーがマウスをクリック、保持、およびドラッグするときに、ユーザーのマウス位置に関連する長方形の領域を描画する 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が提供できるヘルプを本当に感謝します。事前に感謝します。
アップデート
ケンの完全なコード:
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;
}
}
ソリューションのダウンロード: http://www.mediafire.com/?b9ql4pzh69u10n4