我只是想在桌面的副本上显示鼠标位置。
我有一个无边框、最大化的表单,并包含一个带 DockStyle.Fill 的 PictureBox。在表单构造函数中,我从 PrimaryScreen 创建位图来设置 PictureBox 的图像并为 PictureBox.MouseMove 事件添加处理程序。如果事件处理程序被注释掉,此代码将起作用。
public partial class Form1 : Form
{
// The original image.
Bitmap ScreenCaptureBitmap = null;
public Form1()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Maximized;
picDesktop.Dock = DockStyle.Fill;
Graphics graphics = null;
picDesktop.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
try
{
ScreenCaptureBitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
graphics = Graphics.FromImage(ScreenCaptureBitmap);
graphics.CopyFromScreen(0, 0, 0, 0, ScreenCaptureBitmap.Size);
picDesktop.Image = ScreenCaptureBitmap;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
graphics.Dispose();
}
}
这是 MouseMove 事件处理程序的代码:
private void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
try
{
// Make a Bitmap to display the location text.
using (Bitmap bmTemp = new Bitmap(ScreenCaptureBitmap))
{
// Draw the location text on the bitmap.
using (Graphics gr = Graphics.FromImage(bmTemp))
{
Brush theBrush =
SystemBrushes.FromSystemColor(SystemColors.WindowText);
gr.DrawString(String.Format("({0}, {1})", e.X, e.Y),
this.Font, theBrush, e.X + 10, e.Y - 30);
// Display the temporary bitmap.
picDesktop.Image = bmTemp;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
当我编译和调试时,Application.Run(new Form1());行得到“ArgumentException 未处理”。详细信息是“{System.ArgumentException:参数无效。 在 System.Drawing.Image.get_Width()}"。
此处提供的代码的主要问题是 MoouseMove 事件处理程序中生成的 Bitmap 对象是使用
using
语句声明的。using
块退出时,Bitmap 就会被释放。这是游戏的终结者。 此时可能会生成不同类型的异常,具体取决于其他代码之后尝试执行的操作。
Image
属性时,该位图不会被复制或以任何方式 cached 。仅存储对指定 Bitmap 对象的引用。无论如何,在每次鼠标移动事件上生成一个新的位图(以全屏显示),只是为了绘制一些文本,是一个非常昂贵的过程,必然会产生相当明显的卡顿(并且可能会产生其他形式的异常)。
我的建议是复制屏幕的内容,将生成的位图设置为
BackgroundImage
属性(Image 属性也可以),然后在 PictureBox 的 Paint
事件处理程序中绘制文本。需要注意的是,BackgrounImage、Image 和 Control 的表面(其设备上下文)构成了三个不同的、独立的层。您可以修改其中一个而不影响其他。
Screen.PrimaryScreen
返回的Screen对象可以为null。
根据这些建议修改代码:
GetTextBoundingBox()
方法用于剪辑可见屏幕捕获的边界内的文本,因此当鼠标指针(可能)移出这些边界时,文本不会移出屏幕。
public partial class Form1 : Form {
Bitmap screenCaptureBitmap = null;
Point mousePosition = Point.Empty;
public Form1() {
InitializeComponent();
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
picDesktop.Dock = DockStyle.Fill;
try {
if (Screen.PrimaryScreen != null) {
Size imageSize = Screen.PrimaryScreen.Bounds.Size;
screenCaptureBitmap = new Bitmap(imageSize.Width, imageSize.Height);
using (var g = Graphics.FromImage(screenCaptureBitmap)) {
g.CopyFromScreen(0, 0, 0, 0, imageSize);
picDesktop.BackgroundImage = screenCaptureBitmap;
}
}
}
catch (Exception ex) {
Debug.WriteLine(ex.Message);
}
picDesktop.MouseMove += PicDesktop_MouseMove;
picDesktop.Paint += PicDesktop_Paint;
}
private void PicDesktop_MouseMove(object? sender, MouseEventArgs e) {
mousePosition = e.Location;
picDesktop.Invalidate();
}
private void PicDesktop_Paint(object sender, PaintEventArgs e) {
string positionText = $"(x: {mousePosition.X}, y: {mousePosition.Y})";
var textBounds = GetTextBoundingBox(
e.Graphics, Font, positionText, new Point(mousePosition.X + 10, mousePosition.Y - 10));
TextRenderer.DrawText(e.Graphics, positionText, Font, textBounds, SystemColors.WindowText);
}
private Rectangle GetTextBoundingBox(Graphics g, Font font, string text, Point location) {
Rectangle textBox = new Rectangle(location, TextRenderer.MeasureText(g, text, font));
var screenBox = g.VisibleClipBounds;
textBox.Location = new Point(
Math.Max(Math.Min((int)screenBox.Width - textBox.Width, location.X), 0),
Math.Max(Math.Min((int)screenBox.Height - textBox.Height, location.Y), 0));
return textBox;
}
}