Reputation: 1056
I simply want to display the mouse position on a copy of the desktop.
I have a form that is borderless, maximized and contains a PictureBox with DockStyle.Fill. In the form constructor I create a bitmap from the PrimaryScreen to set the PictureBox's image and add a handler for the PictureBox.MouseMove event. This code works if the event handler is commented out.
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();
}
}
Here is the code for the MouseMove event handler:
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);
}
}
When I compile and debug, the Application.Run(new Form1()); line gets "ArgumentException was unhandled". The detail is "{System.ArgumentException: Parameter is not valid. at System.Drawing.Image.get_Width()}".
Upvotes: 0
Views: 124
Reputation: 32248
The main problem with the code presented here is that the Bitmap object generated in the MouseMove event handler is declared with a using
statement.
When the using
block exits, the Bitmap is disposed. This is the game-stopper. Different kind of exceptions can be generated at this point, which one depends on what other code is trying to do after.
Image
Property of a PictureBox, the bitmap is not copied or otherwise cached in any way. It just stores the reference to the object specified.Anyway, generating a new Bitmap - presented in full screen - on every mouse move event, just to paint some text, is a very expensive procedure, bound to generate quite visible stuttering (and, possibly, other forms of exceptions).
My suggestion is to copy the content of the Screen, set the generated Bitmap to the BackgroundImage
property (the Image
property would also do), then draw the text in the Paint
event handler of your PictureBox.
The text is rendered onto the surface of the Control, it doesn't affect the Bitmap.
As a note, the BackgroundImage, the Image and the Control's surface (its Device Context) constitute three different, independent, layers. You can modify one without affecting the others.
The Screen object that Screen.PrimaryScreen
returns can be null.
Modified code based on these suggestions:
The GetTextBoundingBox()
method is used to clip the rendering of the text inside the bounds of the visible screen capture, so it doesn't move outside the screen when the mouse pointer (eventually) is moved outside of these bounds.
In case the text is not visible enough, TextRenderer.DrawText()
allows to specify a background color (it cannot use the alpha channel, in this context, though)
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) {
var 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;
}
}
Upvotes: 2