Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#
Article

Mouse Position over Image in a PictureBox

Rate me:
Please Sign up or sign in to vote.
4.78/5 (30 votes)
18 Mar 2008MIT2 min read 132.7K   6.1K   68   18
A custom control derived from the PictureBox control that demonstrates how to obtain the position of the mouse relative to the Image

Introduction

When using the default PictureBox in a Windows.Forms, it is often helpful to know what relative pixel of the image the mouse is currently over. Since the position returned in mouse events for the control are relative to the control's base, the position over the image can be vastly different, depending on how the SizeMode property is set. This class helps resolve these differences.

Background

In one of my applications, I needed to be able to find out the location of the mouse relative to the image, but my SizeMode was set to Zoom. In this mode, the image constantly rescales itself to maintain the proper aspect ratio that will fit into the area of the control. As such, I needed to be able to safely translate the point.

Determining the Position

There are five different size modes that the PictureBox control can be set to:

  • Normal
  • AutoSize
  • CenterImage
  • StretchImage
  • Zoom

In the Normal mode, the image is located at the top left corner and is not scaled or skewed, so we can simply return the point relative to the control.

In the AutoSize mode, the control itself is resized to fit the image (if possible), and the resulting image is not scaled or skewed, so we can simply return the point relative to the control again.

In the CenterImage mode, the image is displayed in the center of the control. If the image is smaller than the control, it is padded equally to center it. If the image is larger than the control, the image is cropped equally. The following shows how to determine the location relative to the image:

C#
/// <summary>
/// Gets the mouse position over the image when the <see cref="PictureBox">PictureBox's
/// </see> <see cref="PictureBox.SizeMode">SizeMode</see> is set to Center
/// </summary>
/// <param name="coordinates">Point to translate</param>
/// <returns>A point relative to the top left corner of the 
/// <see cref="PictureBox.Image">Image</see>
/// If the Image is null, no translation is performed
/// </returns>
protected Point TranslateCenterImageMousePosition(Point coordinates)
{
    // Test to make sure our image is not null
    if(Image==null) return coordinates;
    // First, get the top location (relative to the top left of the control) 
    // of the image itself
    // To do this, we know that the image is centered, so we get the difference in size 
    // (width and height) of the image to the control
    int diffWidth = Width - Image.Width;
    int diffHeight = Height - Image.Height;
    // We now divide in half to accommodate each side of the image
    diffWidth /= 2;
    diffHeight /= 2;
    // Finally, we subtract this number from the original coordinates
    // In the case that the image is larger than the picture box, this still works
    coordinates.X -= diffWidth;
    coordinates.Y -= diffHeight;
    return coordinates;
}  

In the StretchImage mode, the image is stretched or compressed, independently across axis, to take up the entire area of the control. To accommodate this, we must determine the ratio of the skew and apply it to the point:

C#
/// <summary>
/// Gets the mouse position over the image when the <see cref="PictureBox">PictureBox's
/// </see> <see cref="PictureBox.SizeMode">SizeMode</see> is set to StretchImage
/// </summary>
/// <param name="coordinates">Point to translate</param>
/// <returns>A point relative to the top left corner of the 
/// <see cref="PictureBox.Image">Image</see>
/// If the Image is null, no translation is performed
/// </returns>
protected Point TranslateStretchImageMousePosition(Point coordinates)
{
    // test to make sure our image is not null
    if (Image == null) return coordinates;
    // Make sure our control width and height are not 0
    if (Width == 0 || Height == 0) return coordinates;
    // First, get the ratio (image to control) the height and width
    float ratioWidth = (float)Image.Width/Width;
    float ratioHeight = (float)Image.Height / Height;
    // Scale the points by our ratio
    float newX = coordinates.X;
    float newY = coordinates.Y;
    newX *= ratioWidth;
    newY *= ratioHeight;
    return new Point((int)newX, (int)newY);
}

In the last mode, Zoom, the image is scaled evenly across axis in such a way that the entire image is displayed in the control, centered when appropriate. To manage this method, we first need to determine what our limiting dimension is (height or width). After that, we need to determine the padding and the scaling to be applied to the point:

C#
/// <summary>
/// Gets the mouse position over the image when the <see cref="PictureBox">PictureBox's
/// </see> <see cref="PictureBox.SizeMode">SizeMode</see> is set to Zoom
/// </summary>
/// <param name="coordinates">Point to translate</param>
/// <returns>A point relative to the top left corner of the 
/// <see cref="PictureBox.Image">Image</see>
/// If the Image is null, no translation is performed
/// </returns>
protected Point TranslateZoomMousePosition(Point coordinates)
{
    // test to make sure our image is not null
    if (Image == null) return coordinates;
    // Make sure our control width and height are not 0 and our 
    // image width and height are not 0
    if (Width == 0 || Height == 0|| Image.Width==0||Image.Height==0) return coordinates;
    // This is the one that gets a little tricky. Essentially, need to check 
    // the aspect ratio of the image to the aspect ratio of the control
    // to determine how it is being rendered
    float imageAspect = (float)Image.Width / Image.Height;
    float controlAspect = (float)Width / Height;
    float newX = coordinates.X;
    float newY = coordinates.Y;
    if(imageAspect>controlAspect)
    { 
        // This means that we are limited by width, 
        // meaning the image fills up the entire control from left to right
        float ratioWidth = (float)Image.Width / Width;
        newX *= ratioWidth;
        float scale = (float)Width / Image.Width;
        float displayHeight = scale * Image.Height;
        float diffHeight = Height - displayHeight;
        diffHeight /= 2;
        newY -= diffHeight;
        newY /= scale;
    }
    else
    {
        // This means that we are limited by height, 
        // meaning the image fills up the entire control from top to bottom
        float ratioHeight = (float)Image.Height / Height;
        newY *= ratioHeight;
        float scale = (float)Height / Image.Height;
        float displayWidth = scale * Image.Width;
        float diffWidth = Width - displayWidth;
        diffWidth /= 2;
        newX -= diffWidth;
        newX /= scale;
    }
    return new Point((int)newX, (int)newY);
}

Using the Code

The attached class utilizes these methods to determine the location of the point within the image. Additionally, the class supplies a couple methods and properties to obtain the Point regardless of the display mode (by checking the mode and calling the appropriate method) and an event that is called if the mouse is moved over the image.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
CEO Bambit Technolgoies
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionText on printout Pin
robert1127-Mar-23 18:40
robert1127-Mar-23 18:40 
QuestionMy version debugged. Pin
SNDThunderfist18-Jan-19 15:02
SNDThunderfist18-Jan-19 15:02 
Praise5 out of 5, Perfect Pin
ghammock3-Mar-16 5:11
ghammock3-Mar-16 5:11 
GeneralMy vote of 5 Pin
ghammock3-Mar-16 5:05
ghammock3-Mar-16 5:05 
GeneralMy vote of 5 Pin
Maxwolf Goodliffe12-Dec-14 7:05
Maxwolf Goodliffe12-Dec-14 7:05 
QuestionMy vote of 5 Pin
HyperBug5-Dec-14 2:07
HyperBug5-Dec-14 2:07 
AnswerThanks Pin
Sergey Medgaus11-Jul-14 21:51
Sergey Medgaus11-Jul-14 21:51 
Generalgreaat Pin
greydmar29-Aug-13 11:06
greydmar29-Aug-13 11:06 
GeneralMy vote of 5 Pin
MAP Tiger11-May-12 12:27
MAP Tiger11-May-12 12:27 
GeneralMy vote of 5 Pin
Huj Tamás11-Apr-11 8:12
Huj Tamás11-Apr-11 8:12 
GeneralDoes not work for me Pin
BuggingMe23-Mar-10 9:25
BuggingMe23-Mar-10 9:25 
Questionhow can i get the mouse position over any control in vb Pin
omermsdn6-Oct-09 23:09
omermsdn6-Oct-09 23:09 
i have a form with several controls
i would like to get the mouse position everywhere in the screen.
i mange to get it only when the mouse is not over a control
please help
thx
AnswerRe: how can i get the mouse position over any control in vb Pin
shuailonely10-May-12 3:06
shuailonely10-May-12 3:06 
GeneralAdd delegate setter for MouseMoveOverImage event [modified] Pin
gojumpinthelake28-May-08 16:24
gojumpinthelake28-May-08 16:24 
GeneralRe: Add delegate setter for MouseMoveOverImage event [modified] Pin
Member 1239579817-Sep-18 18:34
Member 1239579817-Sep-18 18:34 
QuestionHow should I use this class? Pin
Lanc3lot10-May-08 4:10
Lanc3lot10-May-08 4:10 
AnswerRe: How should I use this class? Pin
bambitlaw10-May-08 13:33
bambitlaw10-May-08 13:33 
GeneralNice... Pin
cacalex23-Oct-07 1:41
cacalex23-Oct-07 1:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.