7

I'm trying to learn WPF, so here's a simple question, I hope:

I have a window that contains an Image element bound to a separate data object with user-configurable Stretch property

<Image Name="imageCtrl" Source="{Binding MyImage}" Stretch="{Binding ImageStretch}" />

When the user moves the mouse over the image, I would like to determine the coordinates of the mouse with respect to the original image (before stretching/cropping that occurs when it is displayed in the control), and then do something with those coordinates (update the image).

I know I can add an event-handler to the MouseMove event over the Image control, but I'm not sure how best to transform the coordinates:

void imageCtrl_MouseMove(object sender, MouseEventArgs e)
{
    Point locationInControl = e.GetPosition(imageCtrl);
    Point locationInImage = ???
    updateImage(locationInImage);
}

Now I know I could compare the size of Source to the ActualSize of the control, and then switch on imageCtrl.Stretch to compute the scalars and offsets on X and Y, and do the transform myself. But WPF has all the information already, and this seems like functionality that might be built-in to the WPF libraries somewhere. So I'm wondering: is there a short and sweet solution? Or do I need to write this myself?


EDIT I'm appending my current, not-so-short-and-sweet solution. Its not that bad, but I'd be somewhat suprised if WPF didn't provide this functionality automatically:

Point ImgControlCoordsToPixelCoords(Point locInCtrl, 
    double imgCtrlActualWidth, double imgCtrlActualHeight)
{
    if (ImageStretch == Stretch.None)
        return locInCtrl;

    Size renderSize = new Size(imgCtrlActualWidth, imgCtrlActualHeight);
    Size sourceSize = bitmap.Size;

    double xZoom = renderSize.Width / sourceSize.Width;
    double yZoom = renderSize.Height / sourceSize.Height;

    if (ImageStretch == Stretch.Fill)
        return new Point(locInCtrl.X / xZoom, locInCtrl.Y / yZoom);

    double zoom;
    if (ImageStretch == Stretch.Uniform)
        zoom = Math.Min(xZoom, yZoom);
    else // (imageCtrl.Stretch == Stretch.UniformToFill)
        zoom = Math.Max(xZoom, yZoom);

    return new Point(locInCtrl.X / zoom, locInCtrl.Y / zoom);
}
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Gabriel
  • 1,443
  • 15
  • 24
  • Personally I'd probably do it exactly as your have in your "not-so-short-and-sweet" solution. I haven't run into a built in library that would do this. – Alastair Pitts Jan 17 '10 at 02:17

1 Answers1

8

It would probably be easier if you used a ViewBox. For example:

<Viewbox Stretch="{Binding ImageStretch}">
    <Image Name="imageCtrl" Source="{Binding MyImage}" Stretch="None"/>
</Viewbox>

Then when you go and call GetPosition(..) WPF will automatically account for the scaling.

void imageCtrl_MouseMove(object sender, MouseEventArgs e) 
{ 
    Point locationInControl = e.GetPosition(imageCtrl);
}
Steven
  • 4,826
  • 3
  • 35
  • 44
  • Thanks. I found that I didn't need to set the HorizontalAlignment, but it was important to set the DPI for the Image source to 96 (I was using a non-standard bitmap). Oh, and the 'b' in `Viewbox` is lowercase :) – Gabriel Aug 27 '10 at 06:29
  • Amazing! It is just awesome how easy it can be done in WPF! Thanks a lot, saved my time. – Fedor Oct 04 '13 at 11:15