Resolving Coordinate Misalignment in Zoomable PictureBox

17 views Asked by At

The issue involves a PictureBox control embedded within a custom UserControl in a Windows Forms application. The UserControl has its AutoScaleMode set to inherit, AutoScroll disabled, AutoSize disabled, and is anchored to a panel on all sides. The PictureBox within it is also anchored on all sides to the UserControl, has its Dock property set to None and uses a Zoom SizeMode.

The PictureBox displays an image (overlay image with a transparent background) alongside a background image. Although zooming functionality (triggered by the mouse wheel) appears to work correctly from a visual standpoint, the coordinates of the mouse cursor do not align with the expected values. Specifically, the coordinates are based on the PictureBox's top-left corner being (0,0), whereas the expectation is for (0,0) to correspond to the top-left corner of the image itself.

An attempt was made to correct this discrepancy by translating the coordinates based on the aspect ratio of the image relative to the PictureBox. However, this solution only corrected the x-coordinate, leaving the y-coordinate inaccurate. This issue is attributed to the presence of transparent gaps on the sides of the image upon its initial load, with no gaps at the top or bottom, and the aspect ratio used for the translation does not change with zooming.

Furthermore, creating a Rectangle on the PictureBox results in incorrect sizing and positioning.

The challenge is to accurately translate mouse coordinates to match the image's actual display area within the PictureBox, taking into account the dynamic scaling and aspect ratio adjustments caused by the Zoom SizeMode, and ensuring that shapes drawn on the PictureBox (like Rectangles) are correctly sized and positioned relative to the zoomed image.

Below is the method used to get the coordinates relative to the image:

Public Function TranslateZoomMousePosition(coordinates As Drawing.Point) As Drawing.Point
    ' Test to make sure our image is not null
    If truPictureBox.Image Is Nothing Then Return coordinates
    ' Make sure our control width and height are not 0 and our image width and height are not 0
    If truPictureBox.Width = 0 OrElse truPictureBox.Height = 0 OrElse truPictureBox.Image.Width = 0 OrElse truPictureBox.Image.Height = 0 Then 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

    Dim Image = truPictureBox.Image
    Dim imageAspectRatio As Single = CSng(Image.Width) / Image.Height
    Dim pbAspectRatio As Single = CSng(truPictureBox.Width) / truPictureBox.Height
    Dim newX As Single = coordinates.X
    Dim newY As Single = coordinates.Y
    If imageAspectRatio > pbAspectRatio Then
        ' This means that we are limited by width, 
        ' meaning the image fills up the entire control from left to right
        Dim ratioWidth As Single = CSng(Image.Width) / truPictureBox.Width
        newX *= ratioWidth
        Dim scale As Single = CSng(truPictureBox.Width) / Image.Width
        Dim displayHeight As Single = scale * Image.Height
        Dim diffHeight As Single = truPictureBox.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
        Dim ratioHeight As Single = CSng(Image.Height) / truPictureBox.Height
        newY *= ratioHeight
        Dim scale As Single = CSng(truPictureBox.Height) / Image.Height
        Dim displayWidth As Single = scale * Image.Width * zoomFactor
        Dim diffWidth As Single = truPictureBox.Width - displayWidth
        diffWidth /= 2
        newX -= diffWidth
        newX /= scale
    End If

    Return New Drawing.Point(CInt(Math.Floor(newX)), CInt(Math.Floor(newY)))
End Function
0

There are 0 answers