-2

I am now really desperate. I just can't get any further and in principle it shouldn't be that difficult. I tried solving it the last 2 weeks. The following task/problem: I have a canvas in my C# WPF project called “MarkPointsMap”, on which there are different kind of mark points (3 different kind of rectangles with different colors). One MarkPointsMap can contain round about 3000-4000 different MarkPoints (Children of that canvas) all over across the MarkPointsMap. The positions of that MarkPoints are loaded from a .tab-file, which is converted into a list called “MarkPoints” and added as Children on the “MarkPointsMap” canvas after loading the respective .tab-file.

This a screenshot of the MarkPointsMap: This a screenshot of the MarkPointsMap:

There are e.g. mostly small rectangles (the green ones), a few middle rectangles (the yellow ones) and two big rectangles (the red ones). Mulitple markpoints should be selected with a selection-rectangle. In my project I can already draw the selection rectangle, but the “if this.selectRect.Contains(MarkPointsMap.Children)” (see below) part does not work.

The selection-rectangle looks as follows:

The selection-rectangle looks as follows:

The following method “drawMarkPoints”, which is called after loading the .tab file in the program, draws the markpoints on the canvas “MarkPointsMap”:

public void drawMarkPoints()
    {
        MarkPointsMap.Children.Clear();

        foreach (MarkPoint markpoint in this.Marks.markpoints)
        {
            if (markpoint.type.relevant)
            {
                Rectangle markpointArea = new Rectangle();
                markpointArea.Tag = markpoint;
                //!!
                SolidColorBrush mySolidColorBrush = new SolidColorBrush();
                mySolidColorBrush.Color = markpoint.fillColor;
                markpointArea.Fill = mySolidColorBrush;
                markpointArea.StrokeThickness = 0.2;
                markpointArea.Stroke = Brushes.Black;

                markpointArea.Width = markpoint.symbol.Width;
                markpointArea.Height = markpoint.symbol.Height;
                //set the markpoints in canvas 
                Canvas.SetLeft(markpointArea, getCanvasCoordinates(markpoint.center).X - 0.5 * markpoint.symbol.Width); 
                Canvas.SetTop(markpointArea, getCanvasCoordinates(markpoint.center).Y - 0.5 * markpoint.symbol.Height);

                MarkPointsMap.Children.Add(markpointArea);
          
            }
        }
    }

The selection-rectangle is drawn in the canvas “MarkPointsMap” with the three Mouse-Events (MarkPointsMap_MouseDown, MarkPointsMap_MouseMove, MarkPointsMap_MouseUp):

public Rect itemRect;
public Rect selectRect;
private point anchorPoint; 
private Rectangle manualDragRect = new Rectangle(); 

private void MarkPointsMap_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ChangedButton == MouseButton.Left)
        {
            isLeftMouseButtonDownOnWindow = true;
            anchorPoint = e.MouseDevice.GetPosition(MarkPointsMap);
            MarkPointsMap.CaptureMouse();
            e.Handled = true;
            manualDragRect = new Rectangle
            {
                Stroke = Brushes.Red,
                StrokeThickness = 2
            };
            MarkPointsMap.Children.Add(manualDragRect); 
        }       
    }

private void MarkPointsMap_MouseMove(object sender, MouseEventArgs e)
    {
        if (!MarkPointsMap.IsMouseCaptured)
            return;

        Point location = e.MouseDevice.GetPosition(MarkPointsMap);
        double minX = Math.Min(location.X, anchorPoint.X);
        double minY = Math.Min(location.Y, anchorPoint.Y);
        double maxX = Math.Max(location.X, anchorPoint.X);
        double maxY = Math.Max(location.Y, anchorPoint.Y);

        Canvas.SetTop(manualDragRect, minY);
        Canvas.SetLeft(manualDragRect, minX);

        double height = maxY - minY;
        double width = maxX - minX;

        manualDragRect.Height = Math.Abs(height);
        manualDragRect.Width = Math.Abs(width);
        Point xy_2 = new Point((Canvas.GetLeft(manualDragRect) + 0.5 * width), (Canvas.GetTop(manualDragRect) + 0.5 * height));
        this.selectRect = new Rect(xy_2.X, xy_2.Y, width, height);
    }


private void MarkPointsMap _MouseUp(object sender, MouseButtonEventArgs e)
    {
        MarkPointsMap.ReleaseMouseCapture();

        foreach (MarkPoint markpoint in this.Marks.markpoints) 
        {
            Rect itemRect = new Rect(markpoint.center.X, markpoint.center.Y, markpoint.symbol.Width, markpoint.symbol.Height);
            if (selectRect.Contains(itemRect))
            {
                MessageBox.Show("Test!"); // it does not reach this code, if I circle several markpoints with the red selection-rectangle called “selectRect”
                
            }

        }
    }

Why doesn’t this work? I guess it has to do with the converting from rectangle (System.Windows.Shapes using derictive) to struct Rect : IFormattable. The “if (selectRect.Contains(itemRect)”, which is not reached should in perspective color all mark points, which are inside the selection-rectangle in red, then compute the Point (x-Coordinate, y-Coordinate) of the middle-point of the selection-rectangle and add this middle point back to the original .tab-file.
Any ideas or hints, how I could continue? Thanks in advance. Best regards!

1 Answers1

1

It looks like your calculations are wrong. You are offsetting the selectRect. You are centering it relative to the rendered manualDragRect. This way you are testing a totally different area than the one you have defined by the rendered selection bounds.

Additionally, you are capturing the mouse on the Canvas but release the capture on some different control. It should be released on the MarkPointsMap too. Also be careful with marking mouse events as handled. If the input handling is not part of a custom control, you should generally avoid it.

You should consider to use an ItemsControl with a Canvas as panel. You can then move the drawing to the markup and define a DataTemplate for the MarkPoint data.

I recommend to use the original Rect returned from the manualDragRect shape. This means you should remove the selectRect field and related computations:

private void MarkPointsMap_MouseUp(object sender, MouseButtonEventArgs e)
{
  MarkPointsMap.ReleaseMouseCapture();

  foreach (MarkPoint markpoint in this.Marks.markpoints)
  {
    Rect itemRect = new Rect(markpoint.center.X, markpoint.center.Y, markpoint.symbol.Width, markpoint.symbol.Height);

    Rect selectionBounds = manualDragRect.RenderedGeometry.Bounds;
    selectionBounds.Offset(Canvas.GetLeft(manualDragRect), Canvas.GetTop(manualDragRect));
    if (selectionBounds.Contains(itemRect))
    {
      MessageBox.Show("Test!");
    }

    // TODO::Calculate center of selectionBounds
    Point selectionBoundsCenter = ...;
  }
}
BionicCode
  • 1
  • 4
  • 28
  • 44
  • Thanks for your fast reply and input. In the real project the ReleaseMouseCapture(); is applied on the correct control. I forgot to rename the real name of the canvas to the edited name for stackoverflow. For this stackoverflow post I renamed the whole variable and control names, so that the real project is still anonymous. I changed it now in my original Post. I tried your code, but I still have the same weird behavior of the selection-rectangle. – s950mpc2000 Jan 18 '22 at 03:09
  • I really wonder where and why you struggle. This is really a simple problem. I have updated my answer to give you a small and simple but full working example. It also shows you how to utilize the ItemsControl as Canvas host and DataTemplates to separate your calculations/data from the rendering. This should be a good starting point for you. – BionicCode Jan 18 '22 at 21:41
  • the problem was a scaling problem. The width of the MarkPointsCanvas was another as the width of the sheet of paper (the .tab-file, which is imported) and on which the marekpoints are drawn. With the correct scaling it now works. And also with your code. – s950mpc2000 Jan 18 '22 at 22:18
  • Fine. So can I delete the example UserControl? – BionicCode Jan 18 '22 at 22:19
  • The problem with your posted code is that it doesn't show any scaling. It is also not mentioned in the question. – BionicCode Jan 18 '22 at 22:21
  • yes, never mind. You answered my post as far as it could be answered. I marked it as the answer. I can also delete my original post if it is better for the clarity of the stackoverflow platform. – s950mpc2000 Jan 18 '22 at 22:33
  • Of course, I can not upload the whole project here, because it should stay anonymous. The problem was, that I had to continue the C# WPF project from a coworker. I thought that I was missing a basic concept concerning Selection-Rectangle in my knowledge. But the main problem, was that the selection rectangle could not catch the markpoints because the coordination-system-scaling of the markpoints was a whole different as the canvas and the selection-rectangle. I recognized it with debugging. My coworker was not aware of it anymore because he programmed it 2 years ago. – s950mpc2000 Jan 18 '22 at 22:33
  • I see. We all have been through this. This can really spoil the day, especially when the author is no longer with your company. The moments we value any kind of documentation. I will then remove the latest example to keep the answer clean. Good luck with your project. – BionicCode Jan 18 '22 at 22:51
  • Yes, the documentation is unfortunately in need of improvement at our company. Thank you! :) – s950mpc2000 Jan 19 '22 at 08:59