How to use Overlay and mouse click with WPF display (c#)

Hello,
I have some difficulties to use overlay on wpf display.
I just want to get the position of the mouse (click or double click) in image coordinate to set an overlay (circle or arrow or …) on the display (and using coordinate mouse).
I tested :

private void Display_Cam_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    Stemmer.Cvb.Wpf.Controls.Display test = (Stemmer.Cvb.Wpf.Controls.Display)sender;
    Point t = Mouse.GetPosition(test);
    Point t1 =  test.PointFromScreen(t);
    Stemmer.Cvb.Wpf.Overlays.ImageOverlay overlay = new Stemmer.Cvb.Wpf.Overlays.ImageOverlay();
    Rectangle rect = new Rectangle();
    rect.StrokeThickness = 5;
    rect.Stroke = new SolidColorBrush(Color.FromRgb(255, 0, 0));
    rect.Height = 30;
    rect.Width = 30;
    rect.HorizontalAlignment = HorizontalAlignment.Left;
    rect.VerticalAlignment = VerticalAlignment.Top;
    overlay.Content = rect;
    overlay.OverlayX = t.X;
    overlay.OverlayY = t.Y;
}

But t1 is not the same as MouseOverInfo.

How to add overlay to the Display?

Can you help me ?

I do this :

Stemmer.Cvb.Wpf.Controls.Display disp = (Stemmer.Cvb.Wpf.Controls.Display)sender;
Point t = Mouse.GetPosition(disp);
         
double x = (t.X - disp.ImageViewportHorizontalOffset )/ disp.ActualZoomFactor;
double y = (t.Y- disp.ImageViewportVerticalOffset) / disp.ActualZoomFactor;

Stemmer.Cvb.Wpf.Overlays.ImageOverlay overlay = new Stemmer.Cvb.Wpf.Overlays.ImageOverlay();
Rectangle rect = new Rectangle();
rect.StrokeThickness = 5;
rect.Stroke = new SolidColorBrush(Color.FromRgb(255, 0, 0));
rect.Height = 30;
rect.Width = 30;
rect.HorizontalAlignment = HorizontalAlignment.Left;
rect.VerticalAlignment = VerticalAlignment.Top;
overlay.Content = rect;

overlay.OverlayX = x - (rect.Width / 2);
overlay.OverlayY = y - (rect.Height / 2);

disp.Items.Add(overlay);

It work but is it ok ?

Hi @algoptic

I will abbreviate Stemmer.Cvb.Wpf.Controls.Display to Display from here on :slightly_smiling_face:

Objects derived from Stemmer.Cvb.Wpf.Controls.Overlays have the properties OverlayX, OverlayY, OverlayWidth and OverlayHeight. These properties are always measured in image coordinates rather than screen coordinates, making it easier to place these objects to a specific location in an image. So your approach of instantiating a Stemmer.Cvb.Wpf.Overlay.ImageOverlay and setting the OverlayX and OverlayY property is basically ok.

However there are a two things to keep in mind:

  1. You do initially case your sender to a Stemmer.Cvb.Wpf.Controls.Display object - if the even bubbled up the visual tree from a nested overlay this line will raise and exception, so it would be better to use
    var disp = sender as Stemmer.Cvb.Wpf.Controls.Display;
    if (disp == null)
    return;
  1. Objects on the Display are subject to scaling transformations based on the zoom level. You already discovered the Display.ActualZoomFactor property, which is good. But what you should be aware of is that as the user zooms into the display, that zoom factor will change and affect your added overlay, scaling size but also stroke thicknesses accordingly. If you want your result to be resilient versus these transformations you’ll need to take that into account:
    • Instead of using a fixed StrokeThickness = 5; you might want to either bind overlay's EffectiveLineWidth property and set StandardLineWidth to 5 (that way, you will make sure that your rectangle’s line with on display is always 5). Or you may want to subscribe to the property changes of the ActualZoomFactor Dependency Property to get informed about changes and set your stroke thickness in code behind.
    • Similar measures will need to be taken to make sure that your overlay always has the correct size. The road to take here depends on whether you want your rectangle to always have the correct size in terms of image pixels (like e. g. always be 5 x 5 image pixels big) and change its display size when the zoom factor changes or you might want to always have the rectangle a display size of 5 x 5 pixels regardless of zoom state. For the first case I’d recommend to have a look at Stemmer.Cvb.Wpf.Overlays.RectangleOverlay - just set OverlayX, OverlayY, OverlayWidth and OverlayHeight as well as StrokeThickness as needed and you’re done (note that in this case you don’t need any extra treatment for stroke thickness as Stemmer.Cvb.Wpf.Overlays.RectangleOverlay automatically scales stroke thickness as needed for display…). For the second case scaling will need to be applied like described above for the stroke thickness.

If you opt for RectangleOverlay then try something like this (assuming you don’t want your overlay to be movable…):

      var aoi = new RectangleOverlay()
      {
        OverlayX = x - 15,
        OverlayY = y - 15,
        OverlayWidth = 30,
        OverlayHeight = 30,
        Stroke = new SolidColorBrush(Colors.Red),
        StrokeThickness = 5,
        Movable = false,
        Resizable = false,
        CanCenterDrag = false
      };

Another relatively simply usable object that might also fit your use case is Stemmer.Cvb.Wpf.Overlays.Crosshair - with that one you’d just set OverlayX and OverlayY as well as CrosshairSize (plust, of course, StrokeThickness if you want).

1 Like