Может / Должно ли событие MouseEnter всплыть? - PullRequest
2 голосов
/ 22 февраля 2011

Возможно ли при каких-либо обстоятельствах вызвать всплывающее событие MouseEnter?

MSDN сообщает, что это прикрепленное событие со стратегией прямой маршрутизации, что технически исключает такую ​​возможность.У меня довольно сложный элемент управления (по сути, иерархия, состоящая из сеток, стековых панелей и элементов управления содержимым).Мне кажется, что событие MouseEnter распространяется снизу вверх, вот отладочный дамп, взятый из обработчика OnMouseEnter (у меня есть один и тот же пользовательский элемент управления, включенный на разных уровнях иерархии, который обрабатывает MouseEnter, поэтому у меня есть центральное место для прослушивания этого события):

In: родительский: s7b, метка времени: 37989609

In: родительский: s2, метка времени: 37989609

In: родительский: Root, метка времени: 37989609

s7b, s2 и Root - это имена FrameworkElement, а отметка времени - это e.Timestamp из события MosueEnter.

При условии, что стратегия маршрутизации является прямой, как WPF принимает решение об источнике событий?Проходит ли оно визуальное дерево до тех пор, пока не будет найден первый FrameworkElement с прикрепленным событием MouseEnter?

Пока я работаю над минималистичным набором репродукций для этой проблемы, кто-нибудь может подсказать, что может вызвать поведение?


А вот репродукция:

  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 MouseEventReceiver

Код:

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 ]

2 голосов
/ 23 февраля 2011

Возможно, поможет более подробное описание. В статье MSDN на Mouse.MouseEnter сделана следующая цитата:

Хотя это событие используется для отслеживания того, когда мышь входит в элемент, оно также сообщает, что свойство IsMouseOver изменилось с false на true для этого элемента

MSDN говорит, что Mouse.MouseEnter срабатывает, когда IsMouseOver меняется с ложного на истинное. Глядя на статью MSDN для IsMouseOver сделана следующая цитата:

Получает значение, указывающее, находится ли указатель мыши над этим элементом (включая визуальные дочерние элементы, которые находятся внутри его границ)

Как мы оба согласны, нулевой фон не поддерживает взаимодействие. Существует много предостережений по поводу нулевой фоновой проблемы в отношении IsMouseOver, но из практического применения очевидно, что это значение не переключается на нулевой фон. Тем не менее, определение говорит, что если мышь «расположена» над любым визуальным дочерним элементом в пределах элемента, то IsMouseOver изменится, за исключением нескольких странных предостережений. Тем не менее, нулевой фон не является одним из этих предостережений.

Быстрый просмотр визуального дерева вашего элемента управления с помощью утилиты snoop или VisualTreeHelper показывает, что все три серые сетки являются визуальными потомками c1, две самые правые сетки являются визуальными потомками c2, а самая правая сетка является визуальным потомком c3. Как и следовало ожидать, все ваши элементы управления содержимым вложены друг в друга.

Наблюдая за свойством IsMouseOver for c1, вы можете легко увидеть, что, когда мышь касается серого квадрата, значение свойства меняется на true. Вы можете убедиться в этом, добавив обратный вызов к событию перемещения мыши в главном окне. Я использовал следующий обратный вызов:

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

Вы заметите, что независимо от того, какой из трех серых квадратов, над которыми вы находитесь над IsMouseOver для c1, имеет значение true. Это указывает на то, что IsMouseOver изменяется на true для c1, когда оно находится над любым из трех квадратов, и поэтому утверждения MSDN верны. MouseEnter должен и действительно срабатывает для c1 независимо от того, к какому серому квадрату вы прикоснулись, так как все три серых квадрата находятся в визуальном дереве c1 s и не исключаются из проверки нажатия мыши предупреждением (таким как предупреждение о пустом фоне) ).

Событие MouseEnter отвечает как прямое событие в вашем приложении, как заявляет MSDN.

1 голос
/ 22 февраля 2011

Поскольку это сложный элемент управления, похоже, что при вводе элемента Root с помощью мыши вы одновременно вводите s7b и s2.Поскольку все три элемента зарегистрированы для события MouseEnter, все они должны отвечать ровно в одно и то же время, если мышь может ввести все три элемента одновременно.

Вероятно, событие всплывает на экранедерево, потому что вы случайно зарегистрировали MouseEnter для линии визуальных родителей аналогичного размера.Если я определяю кнопку в StackPanel с помощью кнопки, растягивающейся для заполнения StackPanel и регистрируя оба события MouseEnter, то всякий раз, когда мышь входит в кнопку, она по умолчанию также будет вводить родительский элемент (StackPanel).В этом случае может показаться, что событие пузырится в визуальном дереве, хотя на самом деле это просто прямое событие для двух отдельных элементов, которое происходит одновременно.

Если вы создаете сложный элемент управления, обычно выхотите один обратный вызов MouseEnter для всего элемента управления или определенные обратные вызовы MouseEnter для определенных частей элемента управления.Вы уверены, что вам нужны обратные вызовы для всего элемента управления, а также для отдельных элементов элемента управления?

-Edit

Только что увидели ваше новое сообщение.Я попробовал ваш код и заметил, что все экземпляры содержимого MyContentControl вложены.Поскольку класс MyContentControl является производным от управления контентом, элементы управления растягиваются, чтобы соответствовать доступному пространству.Это можно увидеть, добавив свойство border в класс MyContentControl.Так как фон MyContentControl по умолчанию равен нулю, MouseEnter срабатывает только при прикосновении к одному из серых полей.

Первый MyContentControl создает горизонтальную панель стека и добавляет серый ящик, а затем средство представления контента.Все, что находится справа от сетки с первым серым квадратом, автоматически будет в c2 и / или c3, потому что предъявитель контента из c1 будет растянут, чтобы соответствовать размеру окна с фиксированной высотой и шириной.Вот почему, когда вы наводите курсор мыши на c2, вы получаете MouseEnter для c1 и c2, потому что при касании серого прямоугольника мышь входит в предъявитель содержимого c1, а мышь также входит в серый прямоугольник c2.Аналогичная логика может быть использована для понимания случая с3.

0 голосов
/ 22 февраля 2011

Прозрачные элементы управления мышью (MTC) (я бы назвал их элементами управления макетом), имеющие непрозрачные дочерние элементы мыши (MOC), не могут правильно обрабатывать события мыши.

Я могу ошибаться, но для меня это похоже на ошибку.Я могу предположить, что виновником является тот факт, что МТС не способны обрабатывать ввод с помощью мыши, но делают вид, что делают это довольно непоследовательно.

Благодаря присоединенным событиям MTC становятся Source & OriginalSource событий мыши, а их IsMouseOver получает значение true, что плохо сочетается с другими частями системы.

Обходной путь - подписывайте только непрозрачные части мыши ваших элементов управления на события мыши.Звучит ужасно на первый взгляд, но при этом вы не должны терять большую гибкость, если используете команды.

Любые предложения очень ценятся.

...