Запуск событий MouseDown для перекрывающихся прямоугольников на холсте - PullRequest
2 голосов
/ 11 марта 2019

У меня есть окно WPF, содержащее Canvas, которое заполнено повернутыми прямоугольниками в коде.Каждый из прямоугольников имеет событие MouseDown, и их положения будут распределены в соответствии с координатами, предоставленными пользователем.Часто два или более перекрываются, частично перекрывая прямоугольник под ним.

Мне нужно, чтобы событие MouseDown вызывалось для каждого прямоугольника, который находится под мышью, когда он нажат, даже если этот прямоугольник перекрыт другим прямоугольником,но я получаю событие MouseDown только для самого верхнего прямоугольника.

Я попытался установить e.Handled для прямоугольника, по которому щелкнули, и безуспешно маршрутизировать события через Canvas, и даже зашел так далеко, что пыталсяРасположите объекты под мышью на основе их координат, но вращение прямоугольников затрудняет их вычисление.

    public MainWindow()
    {
        InitializeComponent();

        Rectangle r1 = new Rectangle() {Width = 80, Height = 120, Fill = Brushes.Blue };
        r1.MouseDown += r_MouseDown;
        RotateTransform rt1 = new RotateTransform(60);
        r1.RenderTransform = rt1;
        Canvas.SetLeft(r1, 150);
        Canvas.SetTop(r1, 50);
        canvas1.Children.Add(r1);

        Rectangle r2 = new Rectangle() { Width = 150, Height = 50, Fill = Brushes.Green };
        r2.MouseDown += r_MouseDown;
        RotateTransform rt2 = new RotateTransform(15);
        r2.RenderTransform = rt2;
        Canvas.SetLeft(r2, 100);
        Canvas.SetTop(r2, 100);
        canvas1.Children.Add(r2);
    }


    private void r_MouseDown(object sender, MouseButtonEventArgs e)
    {
        Console.WriteLine("Rectangle Clicked");
    }
}

enter image description here

1 Ответ

0 голосов
/ 12 марта 2019

Есть еще один вопрос, похожий на этот, но он не имеет принятого ответа, и совершенно непонятно, каким должно быть окончательное решение этой проблемы. Давайте посмотрим, сможем ли мы быть немного более ясными.

Прежде всего, решение, описанное ниже, будет использовать метод VisualTreeHelper.HitTest, чтобы определить, нажала ли мышь на ваши прямоугольники. VisualTreeHelper позволяет нам находить прямоугольники, даже если они перемещаются из-за таких вещей, как Canvas.SetTop и различных операций .RenderTransform.

Во-вторых, мы собираемся захватывать событие нажатия на элементе canvas, а не на отдельных прямоугольниках. Это позволяет нам обрабатывать вещи на уровне холста и проверять сразу все прямоугольники.

public MainWindow()
    {
        InitializeComponent();
        //Additional rectangle for testing.
        Rectangle r3 = new Rectangle() { Width = 175, Height = 80, Fill = Brushes.Goldenrod };
        Canvas.SetLeft(r3, 80);
        Canvas.SetTop(r3, 80);
        canvas1.Children.Add(r3);

        Rectangle r1 = new Rectangle() { Width = 80, Height = 120, Fill = Brushes.Blue };
        RotateTransform rt1 = new RotateTransform(60);
        r1.RenderTransform = rt1;
        Canvas.SetLeft(r1, 100);
        Canvas.SetTop(r1, 100);
        canvas1.Children.Add(r1);

        Rectangle r2 = new Rectangle() { Width = 150, Height = 50, Fill = Brushes.Green };
        RotateTransform rt2 = new RotateTransform(15);
        r2.LayoutTransform = rt2;
        Canvas.SetLeft(r2, 100);
        Canvas.SetTop(r2, 100);
        canvas1.Children.Add(r2);
        //Mouse 'click' event.
        canvas1.PreviewMouseDown += canvasMouseDown;
    }

    //list to store the hit test results
    private List<HitTestResult> hitResultsList = new List<HitTestResult>();

Используемый метод HitTest является более сложным, поскольку самая простая версия этого метода возвращает только «самый верхний» элемент. И, самое главное, они означают первый нарисованный элемент, так что на самом деле он визуально находится в нижней части стопки прямоугольников. Чтобы получить все прямоугольники, нам нужно использовать сложную версию метода HitTest, показанную ниже.

    private void canvasMouseDown(object sender, MouseButtonEventArgs e)
    {
        if (canvas1.Children.Count > 0)
        {
            // Retrieve the coordinates of the mouse position.
            Point pt = e.GetPosition((UIElement)sender);

            // Clear the contents of the list used for hit test results.
            hitResultsList.Clear();

            // Set up a callback to receive the hit test result enumeration.
            VisualTreeHelper.HitTest(canvas1,
                                new HitTestFilterCallback(MyHitTestFilter),
                                new HitTestResultCallback(MyHitTestResult),
                                new PointHitTestParameters(pt));

            // Perform actions on the hit test results list.
            if (hitResultsList.Count > 0)
            {
                string msg = null;
                foreach (HitTestResult htr in hitResultsList)
                {
                    Rectangle r = (Rectangle)htr.VisualHit;
                    msg += r.Fill.ToString() + "\n";
                }
                //Message displaying the fill colors of all the rectangles 
                //under the mouse when it was clicked.
                MessageBox.Show(msg);
            }
        }
    }

    // Filter the hit test values for each object in the enumeration.
    private HitTestFilterBehavior MyHitTestFilter(DependencyObject o)
    {
        // Test for the object value you want to filter.
        if (o.GetType() == typeof(Label))
        {
            // Visual object and descendants are NOT part of hit test results enumeration.
            return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
        }
        else
        {
            // Visual object is part of hit test results enumeration.
            return HitTestFilterBehavior.Continue;
        }
    }

   // Add the hit test result to the list of results.
    private HitTestResultBehavior MyHitTestResult(HitTestResult result)
    {
        //Filter out the canvas object.
        if (!result.VisualHit.ToString().Contains("Canvas"))
        {
            hitResultsList.Add(result);
        }
        // Set the behavior to return visuals at all z-order levels.
        return HitTestResultBehavior.Continue;
    }

В приведенном выше тестовом примере просто отображается окно сообщения, в котором отображаются цвета заливки всех прямоугольников под указателем мыши при нажатии; проверка того, что VisualTreeHelper действительно извлек все прямоугольники в стеке.

enter image description here

...