Get image coordinates from mouse cursor position on screen (WPF Image control)
Asked Answered
S

2

5

I was looking for a solution to transparently add panning & zooming capability to a WPF Image control and I have found the solution https://mcmap.net/q/158415/-pan-amp-zoom-image, developed by Wiesław Šoltés and Konrad Viltersten, which is outstanding.

Now I would like to add a 'mouse click' event to the control so that I can the coordinates of the clicked point in the original image coordinate system, so I can use them to retrieve the pixel color.

I understand there will be some rounding and if the image is zoomed out the color will not correspond to the actual one displayed on screen. I also understand that the user may click outside the image borders, in that case I expect a null Point or negative coords to be returned.

I am not an expert of the C# way of doing transforms and at the moment I am stuck with this (to be added inside the ZoomBorder.cs class):

public Point GetImageCoordsAt(MouseButtonEventArgs e)
{
    if (child != null)
    {
        var tt = GetTranslateTransform(child);
        var mousePos = e.GetPosition(this);
        var transformOrigin = new Point(tt.X, tt.Y);

        return mousePos; // Wrong: how do I transform this?
    }

    return null;
}
Scepter answered 16/6, 2017 at 9:13 Comment(3)
Pass a reference to the Image to the GetPosition method if you want to get the coordinates relative to it: var mousePos = e.GetPosition(theImage); Is this what you want?Allahabad
You mean that the image control will take care of mapping the screen position to the image position? Image may have been zoomed and translated.Scepter
You should probably clarify your requirements if you want someone to be able to help you out. You can start by changing the title of your question as this one seems to have nothing to do with your actual issue...?Allahabad
L
1

As mm8 suggests you can get the location you want using e.GetPosition(child); , there's no need to perform any transformations. For testing purposes I've overwritten the reset behaviour. Using the code from the link you provided, change

void child_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    this.Reset();
}

to

public Point GetImageCoordsAt(MouseButtonEventArgs e)
{
    if (child != null && child.IsMouseOver)
    {
        return e.GetPosition(child);
    }
    return new Point(-1, -1);
}

void child_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    MessageBox.Show(GetImageCoordsAt(e).ToString());
}

If you rightclick at the same location in the image, you'll get (approximately) the same coordinates, regardless of the pan and zoom.

Lancastrian answered 16/6, 2017 at 12:53 Comment(1)
That still gives me the coordinates based on the size of the Image control, not the original BitmapSource, but I can do the conversion myself. ThanksScepter
S
12

For future reference, what I was trying to achieve is this:

    public Point GetImageCoordsAt(MouseButtonEventArgs e)
    {
        if (child != null && child.IsMouseOver)
        {
            var controlSpacePosition = e.GetPosition(child);
            var imageControl = this.Child as Image;
            var mainViewModel = ((MainViewModel)base.DataContext);
            if (imageControl != null && imageControl.Source != null)
            {
                // Convert from control space to image space
                var x = Math.Floor(controlSpacePosition.X * imageControl.Source.Width / imageControl.ActualWidth);
                var y = Math.Floor(controlSpacePosition.Y * imageControl.Source.Height / imageControl.ActualHeight);

                return new Point(x, y);
            }
        }
        return new Point(-1, -1);
    }

Which returns the coordinates of the mouse pointer in the original image system.

Scepter answered 16/6, 2017 at 13:18 Comment(2)
Like this, it works with all Stretch modes and inside a ScrollViewer too.Interlard
This saved the day!Oberammergau
L
1

As mm8 suggests you can get the location you want using e.GetPosition(child); , there's no need to perform any transformations. For testing purposes I've overwritten the reset behaviour. Using the code from the link you provided, change

void child_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    this.Reset();
}

to

public Point GetImageCoordsAt(MouseButtonEventArgs e)
{
    if (child != null && child.IsMouseOver)
    {
        return e.GetPosition(child);
    }
    return new Point(-1, -1);
}

void child_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    MessageBox.Show(GetImageCoordsAt(e).ToString());
}

If you rightclick at the same location in the image, you'll get (approximately) the same coordinates, regardless of the pan and zoom.

Lancastrian answered 16/6, 2017 at 12:53 Comment(1)
That still gives me the coordinates based on the size of the Image control, not the original BitmapSource, but I can do the conversion myself. ThanksScepter

© 2022 - 2024 — McMap. All rights reserved.