3

どんな状況でも MouseEnter イベントがバブリングする可能性はありますか?

MSDN によると、これは直接ルーティング戦略を使用した添付イベントであり、技術的にそのような可能性は排除されています。私はかなり複雑なコントロールを持っています (本質的には、グリッド、スタックパネル、およびコンテンツ コントロールで構成される階層です)。MouseEnter イベントが下から上に伝播されるようです。これは OnMouseEnter ハンドラーから取得したデバッグ ダンプです (MouseEnter を処理する階層のさまざまなレベルに同じカスタム コントロールが含まれているため、そのイベントをリッスンするための中心的な場所があります)。 :

中: 親:s7b、タイムスタンプ:37989609

中: 親:s2、タイムスタンプ:37989609

中: 親:ルート、タイムスタンプ:37989609

s7b、s2、および Root は FrameworkElement の名前で、timestamp は MosueEnter イベントの e.Timestamp です。

ルーティング戦略がダイレクトである場合、WPF はどのようにイベント オリジネーターを決定しますか? MouseEnter イベントが添付された最初の FrameworkElement が見つかるまで、ビジュアル ツリーを走査しますか?

私は問題の最小限の再現セットに取り組んでいますが、動作の原因を誰かが提案できますか?


そして、ここに再現があります:

  1. 2 つのカスタム コントロールを作成します。1 つは定数コントロール、もう 1 つはイベント レシーバーです。

1.1。MyContentControl

コード:

    public class MyContentControl : ContentControl
    {
        static MyContentControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyContentControl), 
                new FrameworkPropertyMetadata(typeof(MyContentControl)));
        }

        protected override void OnMouseEnter(MouseEventArgs e)
        {
            if (e.Source == e.OriginalSource
                && e.Source is MyContentControl)
            {
                Debug.Write(string.Format("mouseenter:{0}, timestamp:{1}\n",
                    (e.Source as MyContentControl).Name,
                    e.Timestamp));
            }

            base.OnMouseEnter(e);
        }
    }

XAML:

<Style TargetType="{x:Type local:MyContentControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyContentControl}">
                    <StackPanel Orientation="Horizontal">
                        <local:MouseEventReceiver />
                        <ContentPresenter />
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

1.2 マウスイベントレシーバー

コード:

public class MouseEventReceiver : Control
{
    static MouseEventReceiver()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MouseEventReceiver), 
            new FrameworkPropertyMetadata(typeof(MouseEventReceiver)));
    }
}

XAML:

<Style TargetType="{x:Type local:MouseEventReceiver}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Grid Background="LightGray" Width="20" Height="20" Margin="5"></Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
  1. 最後に、テスト ハーネスのマークアップ:

XAML:

<Window x:Class="MouseTricks.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MouseTricks"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:MyContentControl x:Name="c1">
            <local:MyContentControl x:Name="c2">
                <local:MyContentControl x:Name="c3" />
            </local:MyContentControl>
        </local:MyContentControl>
    </Grid>
</Window>

問題を再現するには、右端の灰色の四角にカーソルを合わせて [デバッグ出力] ウィンドウを確認します。そこには 3 つのエントリが表示されますが、1 つだけを期待しています。

乾杯。

4

3 に答える 3

2

おそらく、より詳細な説明が役立つでしょう。MSDNの記事でMouse.MouseEnterは、次の引用が行われています。

このイベントは、マウスが要素に入ったタイミングを追跡するために使用されますが、この要素の IsMouseOver プロパティが false から true に変更されたことも報告しています。

MSDN によると、false から trueMouse.MouseEnterに変わると起動します。次の引用についてMSDNの記事IsMouseOver見ると、次のようになります。IsMouseOver

マウス ポインターがこの要素 (その境界内にある視覚的な子要素を含む) 上にあるかどうかを示す値を取得します。

両者が同意するように、null 背景は対話をサポートしません。に関するnull バックグラウンドの問題には多くの警告がありますが、IsMouseOver実際のアプリケーションから、この値が null バックグラウンドに切り替えられないことは明らかです。ただし、定義では、マウスが要素の境界内の視覚的な子の「上にある」場合、IsMouseOverいくつかの奇妙な警告を除いて変更されると述べています。ただし、ヌル背景はこれらの警告の 1 つではありません。

snoop ユーティリティまたはを使用してコントロールのビジュアル ツリーをざっと見てみると、VisualTreeHelper3 つの灰色のグリッドすべてが のビジュアルの子でありc1、右端の 2 つのグリッドが のビジュアルの子でc2あり、右端のグリッドが のビジュアルの子であることがわかりc3ます。すべてのコンテンツ コントロールが相互にネストされているため、これは当然のことです。

IsMouseOverforプロパティを監視するc1と、マウスが灰色の四角形に触れると、プロパティ値が true に変わることが簡単にわかります。これは、メイン ウィンドウのマウス移動イベントにコールバックを追加することで確認できます。次のコールバックを使用しました。

    private void MouseMove_Callback(Object sender, MouseEventArgs e)
    {
        if (c1.IsMouseOver)
            MessageBox.Show("Mouse is Over c1!");
    }

上の 3 つの灰色の四角のうちどれがtrue に設定されてIsMouseOverいるかに関係なく、気付くでしょう。c1これは、3 つの正方形のいずれかを超えるIsMouseOverと真に変化することを示してc1いるため、MSDN の主張は真です。 3 つの灰色の四角形はすべてビジュアル ツリーにあり、警告 (null バックグラウンド警告など) によってマウス ヒット テストから除外されないため、どの灰色の四角形に触れても発生するMouseEnterはずです。c1c1

イベントは、MouseEnterMSDN が主張しているように、アプリケーションで直接イベントとして応答しています。

于 2011-02-23T17:26:21.940 に答える
1

これは複雑なコントロールであるため、マウスで Root 要素に入ると、同時に s7b と s2 にも入る可能性があります。3 つの要素はすべて MouseEnter イベントに登録されているため、マウスが 3 つの要素すべてに同時に入ることが可能であれば、それらはすべてまったく同時に応答する必要があります。

同様のサイズの視覚的な親の行に MouseEnter が登録されているため、おそらくイベントが視覚的なツリーをバブルアップしているように見えます。StackPanel に Button を定義し、ボタンを伸ばして StackPanel を埋め、両方を MouseEnter イベントに登録すると、マウスが Button に入ると、デフォルトで親 (StackPanel) にも入ることになります。この場合、イベントがビジュアル ツリーをバブリングしているように見えるかもしれませんが、実際には同時に発生している 2 つの別個の要素の直接的なイベントにすぎません。

複雑なコントロールを作成する場合、通常、コントロール全体に対して 1 つの MouseEnter コールバックが必要になるか、コントロールの特定の部分に対して特定の MouseEnter コールバックが必要になります。コントロール全体とコントロールの個々の部分のコールバックが必要ですか?

-編集

あなたの新しい投稿を見ました。コードを試してみたところ、コンテンツの MyContentControl インスタンスがすべてネストされていることがわかりました。MyContentControl クラスはコンテンツ コントロールから派生するため、コントロールは使用可能なスペースに合わせて引き伸ばされます。これは、境界プロパティを MyContentControl クラスに追加することで確認できます。MyContentControl の背景はデフォルトで null であるため、MouseEnter は灰色のボックスの 1 つがタッチされたときにのみ起動されます。

最初の MyContentControl は、水平の Stackpanel を作成し、灰色のボックスとコンテンツ プレゼンターを追加します。グリッドの右側に最初の灰色のボックスがあるものはすべて、自動的に c2 および / または c3 になります。これは、c1 のコンテンツ プレゼンターが、高さと幅が固定されているウィンドウのサイズに合わせて引き伸ばされるためです。これが、c2 にカーソルを合わせると、c1 と c2 の MouseEnter を取得する理由です。これは、灰色のボックスに触れると、マウスが c1 のコンテンツ プレゼンターに入り、マウスも c2 の灰色のボックスに入ったためです。同様のロジックを使用して、c3 のケースを理解できます。

于 2011-02-22T15:06:02.797 に答える
0

マウスの不透明な子 (MOC) を持つマウス透過コントロール (MTC) (私はレイアウト コントロールと呼ぶ傾向があります) は、マウス イベントを正しく処理できません。

私は間違っているかもしれませんが、私にはバグのように見えます。犯人は、MTC がマウス入力を処理することができないという事実であると推測できますが、かなり一貫性がないふりをしています。

アタッチされたイベントのおかげで、MTC はマウス イベントの Source および OriginalSource になり、IsMouseOver も true に設定され、システムの他の部分とはうまくいきません。

回避策は、コントロールのマウス不透明部分のみをマウス イベントにサブスクライブすることです。一見すさまじいように思えますが、コマンドを使用する場合は、柔軟性が大幅に失われることはありません。

どんな提案でも大歓迎です。

于 2011-02-22T16:23:22.940 に答える