mouse coordinates in image go below 0 and above width

53 views Asked by At

So I have this WPF ReactiveUI Application that displays an image and on hover you should see the pixel value under mouse position.

https://github.com/SebiCiuca/Image-Pixels

I posted here a fully working example because the code necessary for this is a bit much to post here in the question, so I will try and stick here with the relevant code and whoever wants to try it out, just needs to clone the repository.

I have a Grid that contains an image inside

In my project there is a tif image squares 10.tif that has 251 pixels both on width and height and is at 96 dpi (windows default DPI).

I am using four events to get mouse move, mouse enter, mouse leave and preview mouse down events on the image and save the coordonates Point of that position relative to the image itself.

Observable.FromEventPattern<MouseEventArgs>(RenderingImage, nameof(RenderingImage.MouseMove))
            .Subscribe(s => ViewModel.ImageMouseMoveEventForGrid(s.EventArgs.GetPosition(RenderGrid)))
            .DisposeWith(cleanup);

Observable.FromEventPattern<MouseButtonEventArgs>(RenderingImage, nameof(RenderingImage.PreviewMouseDown))
            .Subscribe(s => ViewModel.ImageMouseDownEventForImage(s.EventArgs, s.Sender))
            .DisposeWith(cleanup);

Observable.FromEventPattern<MouseEventArgs>(RenderingImage, nameof(RenderingImage.MouseEnter))
            .Subscribe(s => ViewModel.ImageMouseEnterEventForImage(s.EventArgs.GetPosition(RenderingImage)))
            .DisposeWith(cleanup);

Observable.FromEventPattern<MouseEventArgs>(RenderingImage, nameof(RenderingImage.MouseLeave))
            .Subscribe(s => ViewModel.ImageMouseLeaveEventForImage(s.EventArgs.GetPosition(RenderingImage)))
            .DisposeWith(cleanup);

Everything works fine, expect that I would exact less values and let me a bit more specific.

I would expect that the range of values from left to right to be in a total of 251 values (as I have 251 pixels in the image), but

MouseLeave on left would return a value of -0.12340216 MouseLeave on right would return a value of 251.123402140216

I take MouseLeave as the event that defines the size of my image so from this I deduct that I can have values ranging from 0 to 251 including, which adds up to 252 values for my 251 pixels.

My real scenario measures things with high precision on image and now I am in a dilemma if I did something wrong with the way app was build or am I doing something wrong.

MouseEnter on left would return a value of +0.1234021632251796 MouseEnter on right would return a value of 250.8765978367748

Those two add up to 250.9999999999999796 which is close to 251 and good enough for me, but not sure how do I apply the logic here as my main interest here is MouseMove which I assume at some point could also return me 251.123402140216 which is the MouseLeave position.

As per request full code here

My XAML looks like this

<reactiveui:ReactiveWindow x:Class="ReactveUI_WPF_StartPoint.MainWindow"
                           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                           xmlns:local="clr-namespace:ReactveUI_WPF_StartPoint"
                           x:TypeArguments="local:MainWindowViewModel"
                           xmlns:reactiveui="http://reactiveui.net"
                           mc:Ignorable="d"
                           Title="MainWindow"
                           Height="251"
                           Width="451">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="200" />
        </Grid.ColumnDefinitions>
        <Grid x:Name="RenderGrid"
              Grid.Column="0"
              ClipToBounds="False"
              Background="Purple">
            <Image x:Name="RenderingImage"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Top"
                   Stretch="None"
                   ClipToBounds="True"
                   Panel.ZIndex="101"></Image>
            <Grid x:Name="DrawingGrid"
                  Width="{Binding ActualWidth, ElementName=RenderingImage}"
                  HorizontalAlignment="Left"
                  Panel.ZIndex="100">
                <Grid.Background>
                    <SolidColorBrush Color="Transparent"
                                     Opacity=".11" />
                </Grid.Background>
                <Canvas Grid.Row="0"
                        x:Name="ImageDrawingCanvas"
                        ClipToBounds="False"
                        SnapsToDevicePixels="True"
                        VerticalAlignment="Top"
                        Width="{Binding ActualWidth, ElementName=RenderingImage}"
                        MaxWidth="{Binding ActualWidth, ElementName=RenderingImage}"
                        Panel.ZIndex="10">
                    <Canvas.Background>
                        <SolidColorBrush Color="Transparent"
                                         Opacity="0.4" />
                    </Canvas.Background>
                </Canvas>
            </Grid>
        </Grid>
        <StackPanel Grid.Column="1">
            <TextBlock>
                <Run Text="Position Grid on Grid" /></TextBlock>
            <TextBlock>
                <Run x:Name="MousePositionGridOnGridX" /></TextBlock>
            <TextBlock>
                <Run x:Name="MousePositionGridOnGridY" /></TextBlock>
            <TextBlock>
                <Run Text="Position Image on Grid" /></TextBlock>
            <TextBlock> 
                <Run x:Name="MousePositionImageOnGridX" /></TextBlock>
            <TextBlock>  
                <Run x:Name="MousePositionImageOnGridY" /></TextBlock>
            <TextBlock>
                <Run Text="=======================" /></TextBlock>
            <TextBlock>
                <Run Text="Position Image on Image" /></TextBlock>
            <TextBlock>
                <Run x:Name="MousePositionImageOnImageX" /></TextBlock>
            <TextBlock>
                <Run x:Name="MousePositionImageOnImageY" /></TextBlock>
            <TextBlock>
                <Run Text="Position Grid on Image" /></TextBlock>
            <TextBlock>
                <Run x:Name="MousePositionGridOnImageX" /></TextBlock>
            <TextBlock>
                <Run x:Name="MousePositionGridOnImageY" /></TextBlock>
            <TextBlock>
                <Run Text="=======================" /></TextBlock>
            <TextBlock>
                <Run Text="On Demand Position" /></TextBlock>
            <TextBlock>
                <Run x:Name="MousePositionOnDemandX" /></TextBlock>
            <TextBlock>
                <Run x:Name="MousePositionOnDemandY" /></TextBlock>
            <TextBlock>
                <Run Text="=======================" /></TextBlock>
            <TextBlock>
                <Run Text="Mouse Enter Position" /></TextBlock>
            <TextBlock>
                <Run x:Name="MouseEnterPositionX" /></TextBlock>
            <TextBlock>
                <Run x:Name="MouseEnterPositionY" /></TextBlock>
            <TextBlock>
                <Run Text="=======================" /></TextBlock>
            <TextBlock>
                <Run Text="Mouse Leave Position" /></TextBlock>
            <TextBlock>
                <Run x:Name="MouseLeavePositionX" /></TextBlock>
            <TextBlock>
                <Run x:Name="MouseLeavePositionY" /></TextBlock>
            <TextBlock>
                <Run Text="=======================" /></TextBlock>
            <TextBlock>
                <Run Text="CurrentZoom" /></TextBlock>
            <TextBlock>
                <Run x:Name="CurrentZoom" /></TextBlock>
        </StackPanel>
    </Grid>
</reactiveui:ReactiveWindow>

My MainWindow.xaml.cs looks like this

namespace ReactveUI_WPF_StartPoint
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
    {
        private IDisposable _renderingGridMouseMove;
        private IDisposable _imageMouseMove;

        public MainWindow(MainWindowViewModel mainWindowViewModel)
        {
            InitializeComponent();

            ViewModel = mainWindowViewModel;

            DataContextChanged += (sender, args) => ViewModel = DataContext as MainWindowViewModel;

            //set initial size using notifyimagecontainersizechanged



            this.WhenActivated(cleanup =>
            {
                Observable.FromEventPattern<SizeChangedEventArgs>(RenderGrid, nameof(RenderGrid.SizeChanged))
                          .Subscribe(s => ViewModel.NotifyImageContainerSizeChanged(s.EventArgs))
                          .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                             vm => vm.ImageSource,
                             v => v.RenderingImage.Source)
                 .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                             vm => vm.TransformGroup,
                             v => v.RenderingImage.RenderTransform)
                 .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                             vm => vm.MousePositionGridX,
                             v => v.MousePositionGridOnGridX.Text)
                 .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                             vm => vm.MousePositionGridY,
                             v => v.MousePositionGridOnGridY.Text)
                 .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                                vm => vm.MousePositionGridRelativeToImageX,
                                v => v.MousePositionImageOnGridX.Text)
                .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                                vm => vm.MousePositionGridRelativeToImageY,
                                v => v.MousePositionImageOnGridY.Text)
                .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                    vm => vm.MousePositionImageX,
                    v => v.MousePositionImageOnImageX.Text)
                     .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                             vm => vm.MousePositionImageY,
                             v => v.MousePositionImageOnImageY.Text)
                 .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                                vm => vm.MousePositionImageRelativeToGridX,
                                v => v.MousePositionGridOnImageX.Text)
                .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                                vm => vm.MousePositionImageRelativeToGridY,
                                v => v.MousePositionGridOnImageY.Text)
                .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                              vm => vm.TransformGroup,
                              v => v.DrawingGrid.RenderTransform)
                  .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                                vm => vm.OnDemandXPosition,
                                v => v.MousePositionOnDemandX.Text)
                .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                              vm => vm.OnDemandYPosition,
                              v => v.MousePositionOnDemandY.Text)
                  .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                                vm => vm.MouseEnterPositionX,
                                v => v.MouseEnterPositionX.Text)
                .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                              vm => vm.MouseEnterPositionY,
                              v => v.MouseEnterPositionY.Text)
                  .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                                vm => vm.MouseLeavePositionX,
                                v => v.MouseLeavePositionX.Text)
                .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                              vm => vm.MouseLeavePositionY,
                              v => v.MouseLeavePositionY.Text)
                  .DisposeWith(cleanup);

                this.OneWayBind(ViewModel,
                              vm => vm.CurrentZoom,
                              v => v.CurrentZoom.Text)
                  .DisposeWith(cleanup);

                Observable.FromEventPattern<MouseEventArgs>(RenderGrid, nameof(RenderGrid.MouseMove))
                          .Subscribe(s => ViewModel.RenderGridMouseMoveEventForGrid(s.EventArgs.GetPosition(RenderGrid)))
                          .DisposeWith(cleanup);

                Observable.FromEventPattern<MouseEventArgs>(RenderGrid, nameof(RenderGrid.MouseMove))
                            .Subscribe(s => ViewModel.RenderGridMouseMoveEventForImageSource(s.EventArgs.GetPosition(RenderingImage)))
                            .DisposeWith(cleanup);

                Observable.FromEventPattern<MouseEventArgs>(RenderingImage, nameof(RenderingImage.MouseMove))
                          .Subscribe(s => ViewModel.ImageMouseMoveEventForImage(s.EventArgs.GetPosition(RenderingImage)))
                          .DisposeWith(cleanup);

                Observable.FromEventPattern<MouseEventArgs>(RenderingImage, nameof(RenderingImage.MouseMove))
                            .Subscribe(s => ViewModel.ImageMouseMoveEventForGrid(s.EventArgs.GetPosition(RenderGrid)))
                            .DisposeWith(cleanup);

                Observable.FromEventPattern<MouseButtonEventArgs>(RenderingImage, nameof(RenderingImage.PreviewMouseDown))
                            .Subscribe(s => ViewModel.ImageMouseDownEventForImage(s.EventArgs, s.Sender))
                            .DisposeWith(cleanup);

                Observable.FromEventPattern<MouseEventArgs>(RenderingImage, nameof(RenderingImage.MouseEnter))
                            .Subscribe(s => ViewModel.ImageMouseEnterEventForImage(s.EventArgs.GetPosition(RenderingImage)))
                            .DisposeWith(cleanup);

                Observable.FromEventPattern<MouseEventArgs>(RenderingImage, nameof(RenderingImage.MouseLeave))
                            .Subscribe(s => ViewModel.ImageMouseLeaveEventForImage(s.EventArgs.GetPosition(RenderingImage)))
                            .DisposeWith(cleanup);
            });
        }
    }
}

And in my ViewModel I have the following

public class MainWindowViewModel : ReactiveObject
 {
     private string _filePath = "squares 10.tif";
     private const int DEVICE_INDEPENDENT_DPI_X = 96;
     private const int DEVICE_INDEPENDENT_DPI_Y = 96;
     private const double ZOOM_STEPS = 0.1;
     private const double MIN_IMAGE_SIZE = 256;
     private const double MIN_IMAGE_MARGIN = 0.5f;

     private WriteableBitmap _imageSource;
     private TransformGroup _transformGroup;
     private double _actualSizeZoom;
     private double _currentZoom;
     private Size _zoomContainer;
     private bool _mouseDown = false;

     private BitmapDecoder Decoder { get; set; }

     private BitmapFrame _frame;
     private Size _imageContainerSize;
     private string _mousePositionX;
     private string _mousePositionY;
     private string _mouseDPIPositionX;
     private string _mouseDPIPositionY;
     private string _mousePositionGridRelativeToImageX;
     private string _mousePositionGridRelativeToImageY;
     private string _mousePositionImageX;
     private string _mousePositionImageY;
     private string _mousePositionImageRelativeToGridX;
     private string _mousePositionImageRelativeToGridY;
     private double _onDemandXPosition;
     private double _onDemandYPosition;
     private string _mouseEnterPositionX;
     private string _mouseEnterPositionY;
     private string _mouseLeavePositionX;
     private string _mouseLeavePositionY;

     public WriteableBitmap ImageSource { get => _imageSource; set => this.RaiseAndSetIfChanged(ref _imageSource, value); }
     public TransformGroup TransformGroup { get => _transformGroup; set => this.RaiseAndSetIfChanged(ref _transformGroup, value); }

     public string MousePositionGridX { get => _mousePositionX; set => this.RaiseAndSetIfChanged(ref _mousePositionX, value); }
     public string MousePositionGridY { get => _mousePositionY; set => this.RaiseAndSetIfChanged(ref _mousePositionY, value); }
     public string MousePositionGridRelativeToImageX {  get => _mousePositionGridRelativeToImageX; set => this.RaiseAndSetIfChanged(ref _mousePositionGridRelativeToImageX, value); }
     public string MousePositionGridRelativeToImageY { get => _mousePositionGridRelativeToImageY; set => this.RaiseAndSetIfChanged(ref _mousePositionGridRelativeToImageY, value); }

     public string MousePositionImageX { get => _mousePositionImageX; set => this.RaiseAndSetIfChanged(ref _mousePositionImageX, value); }
     public string MousePositionImageY { get => _mousePositionImageY; set => this.RaiseAndSetIfChanged(ref _mousePositionImageY, value); }

     public string MousePositionImageRelativeToGridX { get => _mousePositionImageRelativeToGridX; set => this.RaiseAndSetIfChanged(ref _mousePositionImageRelativeToGridX, value); }
     public string MousePositionImageRelativeToGridY { get => _mousePositionImageRelativeToGridY; set => this.RaiseAndSetIfChanged(ref _mousePositionImageRelativeToGridY, value); }




     public string MouseDPIPositionX { get => _mouseDPIPositionX; set => this.RaiseAndSetIfChanged(ref _mouseDPIPositionX, value); }
     public string MouseDPIPositionY { get => _mouseDPIPositionY; set => this.RaiseAndSetIfChanged(ref _mouseDPIPositionY, value); }
     public double OnDemandXPosition { get => _onDemandXPosition; set => this.RaiseAndSetIfChanged(ref _onDemandXPosition, value); }
     public double OnDemandYPosition { get => _onDemandYPosition; set => this.RaiseAndSetIfChanged(ref _onDemandYPosition, value); }

     public string MouseEnterPositionX {  get => _mouseEnterPositionX; set => this.RaiseAndSetIfChanged(ref _mouseEnterPositionX, value); }
     public string MouseEnterPositionY { get => _mouseEnterPositionY; set => this.RaiseAndSetIfChanged(ref _mouseEnterPositionY, value); }
     public string MouseLeavePositionX { get => _mouseLeavePositionX; set => this.RaiseAndSetIfChanged(ref _mouseLeavePositionX, value); }
     public string MouseLeavePositionY { get => _mouseLeavePositionY; set => this.RaiseAndSetIfChanged(ref _mouseLeavePositionY, value); }
              

     public double CurrentZoom { get => _currentZoom; set => this.RaiseAndSetIfChanged(ref _currentZoom, value); }

     public MainWindowViewModel()
     {
         LoadFile();
         TransformGroup = new TransformGroup();
         TransformGroup.Children.Add(new ScaleTransform(1, 1));
         TransformGroup.Children.Add(new TranslateTransform(0, 0));
     }

     private void LoadFile()
     {
         using (var imageStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
         {
             Decoder = new TiffBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);

             _frame = Decoder.Frames[0];

             ImageSource = new WriteableBitmap(_frame);
         }

         _actualSizeZoom = _frame.DpiX / DEVICE_INDEPENDENT_DPI_X;
         _currentZoom = 1;

         _zoomContainer = new Size(_frame.Width, _frame.Height);
     }

     private void FitZoom()
     {
         if (ImageSource == null)
         {
             return;
         }

         var deltaZoom = Math.Min(_imageContainerSize.Height / _zoomContainer.Height, _imageContainerSize.Width / _zoomContainer.Width) - _currentZoom;
         while ((_currentZoom + deltaZoom) * DEVICE_INDEPENDENT_DPI_X / ImageSource.DpiX > 8)
         {
             deltaZoom -= ZOOM_STEPS;
         }
         var newZoom = _currentZoom + deltaZoom;
         var translateX = (_imageContainerSize.Width - newZoom * _zoomContainer.Width) / 2.0;
         var translateY = (_imageContainerSize.Height - newZoom * _zoomContainer.Height) / 2.0;

         translateX = Math.Round(translateX, 4);
         translateY = Math.Round(translateY, 4);

         AutoTransform(0, 0, deltaZoom, deltaZoom, translateX, translateY);
     }

     internal void NotifyImageContainerSizeChanged(SizeChangedEventArgs eventArgs)
     {
         _imageContainerSize = eventArgs.NewSize;

         FitZoom();
     }

     private void AutoTransform(double originX, double originY, double deltaScaleX, double deltaScaleY, double translateX, double translateY)
     {
         if (TransformGroup == null)
         {
             return;
         }

         var scaleTransform = GetScaleTransform();
         var translateTransform = GetTranslateTransform();

         scaleTransform.CenterX = originX;
         scaleTransform.CenterY = originY;

         //auto limit zoom if image is smaller than container
         //auto limits zoom is image scale is larger than 800%
         //else respect request
         if (_zoomContainer.Width * (scaleTransform.ScaleX + deltaScaleX) < MIN_IMAGE_SIZE &&
             _zoomContainer.Height * (scaleTransform.ScaleY + deltaScaleY) < MIN_IMAGE_SIZE)
         {
             return;
         }
         else if ((scaleTransform.ScaleX + deltaScaleX) * DEVICE_INDEPENDENT_DPI_X / ImageSource.DpiX > 8)
         {
             return;
         }
         else
         {
             scaleTransform.ScaleX += deltaScaleX;
             scaleTransform.ScaleY += deltaScaleY;
         }

         //auto limit placement away from the container center
         if (translateX > (1 - MIN_IMAGE_MARGIN) * _imageContainerSize.Width)
         {
             translateX = (1 - MIN_IMAGE_MARGIN) * _imageContainerSize.Width;
         }
         if (translateY > (1 - MIN_IMAGE_MARGIN) * _imageContainerSize.Height)
         {
             translateY = (1 - MIN_IMAGE_MARGIN) * _imageContainerSize.Height;
         }
         if (_zoomContainer.Width * scaleTransform.ScaleX + translateX < MIN_IMAGE_MARGIN * _imageContainerSize.Width)
         {
             translateX = MIN_IMAGE_MARGIN * _imageContainerSize.Width - _zoomContainer.Width * scaleTransform.ScaleX;
         }
         if (_zoomContainer.Height * scaleTransform.ScaleY + translateY < MIN_IMAGE_MARGIN * _imageContainerSize.Height)
         {
             translateY = MIN_IMAGE_MARGIN * _imageContainerSize.Height - _zoomContainer.Height * scaleTransform.ScaleY;
         }

         //auto center if image is smaller than container, else respect request
         if (ImageSource.Width * scaleTransform.ScaleX <= _imageContainerSize.Width && _zoomContainer.Height * scaleTransform.ScaleY <= _imageContainerSize.Height)
         {
             translateTransform.X = (_imageContainerSize.Width - scaleTransform.ScaleX * _zoomContainer.Width) / 2.0;
             translateTransform.Y = (_imageContainerSize.Height - scaleTransform.ScaleY * _zoomContainer.Height) / 2.0;
         }
         else
         {
             translateTransform.X = translateX;
             translateTransform.Y = translateY;
         }

         CurrentZoom = scaleTransform.ScaleX;
     }

     private TranslateTransform GetTranslateTransform()
     {
         return (TranslateTransform)TransformGroup?.Children.First(tg => tg is TranslateTransform);
     }

     private ScaleTransform GetScaleTransform()
     {
         return (ScaleTransform)TransformGroup?.Children.First(tg => tg is ScaleTransform);
     }

     public Point GetDPIRelatedPoint(Point point)
     {
         var dpiX = DEVICE_INDEPENDENT_DPI_X / _frame.DpiX;
         var dpiY = DEVICE_INDEPENDENT_DPI_Y / _frame.DpiY;

         var actualX = point.X / dpiX;
         var actualY = point.Y / dpiY;

         return new Point(actualX, actualY);
     }

     public void ImageMouseMove(Point currentPosition)
     {
         //MousePositionX = $"X: {currentPosition.X}";
         //MousePositionY = $"Y: {currentPosition.Y}";
         //var dpiRelatedPoint = GetDPIRelatedPoint(currentPosition);
         //MouseDPIPositionX = $"X: {dpiRelatedPoint.X}";
         //MouseDPIPositionY = $"Y: {dpiRelatedPoint.Y}";
     }

     public void RenderGridMouseMoveEventForGrid(Point currentPosition)
     {
         MousePositionGridX = $"X: {currentPosition.X}";
         MousePositionGridY = $"Y: {currentPosition.Y}";
         var dpiRelatedPoint = GetDPIRelatedPoint(currentPosition);
         //MouseDPIPositionX = $"X: {dpiRelatedPoint.X}";
         //MouseDPIPositionY = $"Y: {dpiRelatedPoint.Y}";
     }
     
     public void RenderGridMouseMoveEventForImageSource(Point currentPosition)
     {
         MousePositionGridRelativeToImageX = $"X: {currentPosition.X}";
         MousePositionGridRelativeToImageY = $"Y: {currentPosition.Y}";
         var dpiRelatedPoint = GetDPIRelatedPoint(currentPosition);
         //MouseDPIPositionX = $"X: {dpiRelatedPoint.X}";
         //MouseDPIPositionY = $"Y: {dpiRelatedPoint.Y}";
     }

     public void ImageMouseMoveEventForImage(Point point)
     {
         MousePositionImageX = $"X: {point.X}";
         MousePositionImageY = $"Y: {point.Y}";
     }

     public void ImageMouseMoveEventForGrid(Point point)
     {
         MousePositionImageRelativeToGridX = $"X: {point.X}";
         MousePositionImageRelativeToGridY = $"Y: {point.Y}";
     }

     internal void ImageMouseDownEventForImage(object value, object sender)
     {
         var image = sender as UIElement;

         if (image == null)
         {
             return;
         }

         var position = Mouse.GetPosition(image);

         OnDemandXPosition = position.X;
         OnDemandYPosition = position.Y;
     }

     internal void ImageMouseEnterEventForImage(Point position)
     {


         MouseEnterPositionX = $"X: {position.X}";
         MouseEnterPositionY = $"Y: {position.Y}";
     }

     internal void ImageMouseLeaveEventForImage(Point position)
     {
         MouseLeavePositionX = $"X: {position.X}";
         MouseLeavePositionY = $"Y: {position.Y}";
     }
0

There are 0 answers