Я создаю элемент управления canvas. У этого корневого холста есть несколько перекрывающихся дочерних элементов (также и холста). Это сделано для того, чтобы каждый ребенок мог обрабатывать свой собственный рисунок, а затем я могу составить окончательный результат с любой комбинацией детей, чтобы получить желаемое поведение.
Это работает очень хорошо, что касается рендеринга. Однако это не очень хорошо работает с событиями мыши. Вот как работают события мыши (на примере previewmousemove):
1 - Если корневой холст находится под мышью, событие пожара
2- Проверьте всех детей, если один из них находится под мышью, событие пожара и остановка
Таким образом, только первый добавленный мной ребенок получит событие перемещения мыши. Событие распространяется не на всех детей, потому что они перекрываются.
Чтобы преодолеть это, я попытался сделать следующее:
1- переопределить события мыши в корневом холсте
2- Для каждого события найдите всех потомков, которые хотят обработать событие, используя VisualTreeHelper.HitTest.
3- Для всех детей, которые вернули действительный результат теста на попадание (то есть: под мышкой и готовы обработать событие (IsHitTestVisible == true)), ???
Вот где я застрял, мне как-то нужно отправить событие мыши всем дочерним элементам и остановить нормальный поток события, чтобы убедиться, что первый дочерний элемент не получил его дважды (через handled = true в событии ).
При использовании RaiseEvent с тем же событием, передаваемым дочерним элементам, кажется, что все работает, но каким-то образом оно также вызывает событие на родительском объекте (корневой холст). Чтобы обойти это, мне нужно было создать копию события и установить принудительно установить источник, хотя это скорее хак, чем решение. Есть ли правильный способ сделать то, что я пытаюсь сделать? Пример кода приведен ниже.
public class CustomCanvas : Canvas
{
private List<object> m_HitTestResults = new List<object>();
public new event MouseEventHandler MouseMove;
public CustomCanvas()
{
base.PreviewMouseMove += new MouseEventHandler(CustomCanvas_MouseMove);
}
private void CustomCanvas_MouseMove(object sender, MouseEventArgs e)
{
// Hack here, why is the event raised on the parent as well???
if (e.OriginalSource == this)
{
return;
}
Point pt = e.GetPosition((UIElement)sender);
m_HitTestResults.Clear();
VisualTreeHelper.HitTest(this,
new HitTestFilterCallback(OnHitTest),
new HitTestResultCallback(OnHitTest),
new PointHitTestParameters(pt));
MouseEventArgs tmpe = new MouseEventArgs(e.MouseDevice, e.Timestamp, e.StylusDevice);
tmpe.RoutedEvent = e.RoutedEvent;
tmpe.Source = this;
foreach (object hit in m_HitTestResults)
{
UIElement element = hit as UIElement;
if (element != null)
{
// This somehow raises the event on us as well as the element here, why???
element.RaiseEvent(tmpe);
}
}
var handlers = MouseMove;
if (handlers != null)
{
handlers(sender, e);
}
e.Handled = true;
}
private HitTestFilterBehavior OnHitTest(DependencyObject o)
{
UIElement element = o as UIElement;
if (element == this)
{
return HitTestFilterBehavior.ContinueSkipSelf;
}
else if (element != null && element.IsHitTestVisible && element != this)
{
return HitTestFilterBehavior.Continue;
}
return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
}
private HitTestResultBehavior OnHitTest(HitTestResult result)
{
// Add the hit test result to the list that will be processed after the enumeration.
m_HitTestResults.Add(result.VisualHit);
// Set the behavior to return visuals at all z-order levels.
return HitTestResultBehavior.Continue;
}