Есть еще один вопрос, похожий на этот, но он не имеет принятого ответа, и совершенно непонятно, каким должно быть окончательное решение этой проблемы. Давайте посмотрим, сможем ли мы быть немного более ясными.
Прежде всего, решение, описанное ниже, будет использовать метод 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 действительно извлек все прямоугольники в стеке.