キャンバスコントロールを作成しています。このルートキャンバスには、いくつかの重複する子があります(キャンバスも同様)。これは、各子が独自の描画を処理できるようにするために行われ、その後、子の任意の組み合わせを使用して最終結果を作成し、目的の動作を得ることができます。
これは、レンダリングに関する限り、非常にうまく機能しています。ただし、これはマウスイベントではうまく機能しません。マウスイベントの動作は次のとおりです(例としてpreviewmousemoveを使用)。
1-ルートキャンバスがマウスの下にある場合は、イベントを発生させます2-すべての子をチェックし、1つがマウスの下にある場合は、イベントを発生させて停止します
そのため、私が追加した最初の子だけがマウス移動イベントを受け取ります。イベントは重複しているため、すべての子に伝播されるわけではありません。
これを克服するために、次のことを試みました。1-ルートキャンバスでマウスイベントをオーバーライドする2-すべてのイベントについて、VisualTreeHelper.HitTestを使用してイベントを処理するすべての子を検索する3-有効なヒットテスト結果を返したすべての子について(すなわち:マウスの下で、イベントを処理する用意があります(IsHitTestVisible == true))、???
これは私が立ち往生している場所です。どういうわけか、マウスイベントをすべての子に送信し、イベントの通常のフローを停止して、最初の子が2回受信しないようにする必要があります(イベントで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;
}