6

キャンバスコントロールを作成しています。このルートキャンバスには、いくつかの重複する子があります(キャンバスも同様)。これは、各子が独自の描画を処理できるようにするために行われ、その後、子の任意の組み合わせを使用して最終結果を作成し、目的の動作を得ることができます。

これは、レンダリングに関する限り、非常にうまく機能しています。ただし、これはマウスイベントではうまく機能しません。マウスイベントの動作は次のとおりです(例として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;
        }
4

3 に答える 3

3

プレビュー イベントを使用する必要があると思います。これは Window から Z オーダーの最も高いコントロールへの RoutingStrategy.Tunnel であり、通常のイベントは RoutingStrategy.Bubble です。

この RoutedEvents にはプロパティ Handle があり、true の場合、誰かがこのイベントを使用したため、システムはビジュアル ツリーのトラバースを停止します。

于 2010-01-05T19:03:20.507 に答える
0

あなたのコード例が面白いと思ったので、試してみました...しかし、私のもので適切に動作させるには、小さな変更を加える必要がありました。

HitTestFilter メソッドの 2 番目の「if」を次のように変更する必要がありました。

if (element == null || element.IsHitTestVisible)

ご覧のとおり、最後に不要な「要素 != this」を削除し (最初の「if」でその条件を既にテストしました)、最初に「要素 == null」を追加しました。

なんで?フィルタリング中のある時点で、パラメーターの型が UIElement から継承されない System.Windows.Media.ContainerVisual であったため、要素が null に設定され、ContinueSkipSelfAndChildren が返されます。ただし、Canvas はその「Children」コレクション内に含まれており、ヒットテストしたい UIElements は Canvas に含まれているため、子をスキップしたくありません。

于 2009-11-26T16:40:28.557 に答える