Как запустить MouseEnter для одного объекта, если у другого объекта есть mousecapture?

#wpf #vb.net #mouse

#wpf #vb.net #мышь

Вопрос:

У меня странный вопрос и vb.net 2010 и проект wpf4. У меня есть метка, которая при нажатии захватывает мышь (myLabel.CaptureMouse) и следует за ней по экрану, пока мышь снова не щелкнет, после чего объект и захват мыши освобождаются.

Тем не менее, мне все еще нужна функциональность mouseenter для другого объекта. Как мне заставить эти две вещи работать вместе?

РЕДАКТИРОВАТЬ: Похоже, есть два решения для этого, одно из которых я обнаружил. Однако, поскольку Rick’s также должен работать (хотя и не протестирован из-за моего дедлайна), и я ненавижу отвечать на свои собственные вопросы здесь, я принял его ответ.

Пока я ждал, пока он прокомментирует мою проблему, я нашел свое собственное решение. Таким образом, обязательно прочитайте как мой ответ, так и ответ Рика.

Ответ №1:

Если другой объект является одним из ваших объектов, то ваша метка и другой объект могут взаимодействовать, пока вы захватываете мышь с помощью синтетических событий. Например, в вашем обработчике перемещения мыши вы можете проверить, Mouse.DirectlyOver является ли это другим объектом, и если да, проведите небольшую бухгалтерию, а затем вызовите RaiseEvent либо MouseEnter , либо MouseLeave для другого объекта. Если у вас есть куча этих объектов, то вам просто нужно больше вести бухгалтерию.

Редактировать:

Выше указано, Mouse.DirectlyOver что конкретно не работает, когда мышь захвачена. Чтобы сделать вышесказанное более конкретным и исправить эту ошибку, вот полный рабочий пример.

Вот разметка, показывающая холст, прямоугольник с обработкой захвата мышью и эллипс с обработкой ввода / вывода:

 <Grid>
    <Canvas>
        <Rectangle Canvas.Left="0" Canvas.Top="0"
                   Fill="Red" Width="100" Height="100"
                   MouseLeftButtonDown="Rectangle_MouseLeftButtonDown"
                   MouseMove="Rectangle_MouseMove"
                   MouseLeftButtonUp="Rectangle_MouseLeftButtonUp"/>
        <Ellipse Canvas.Left="0" Canvas.Top="100"
                   Fill="Green" Width="100" Height="100"
                   MouseEnter="Ellipse_MouseEnter"
                   MouseLeave="Ellipse_MouseLeave">
            <Ellipse.RenderTransform>
                <ScaleTransform/>
            </Ellipse.RenderTransform>
        </Ellipse>
    </Canvas>
</Grid>
  

и вот обработчики событий, которые демонстрируют, как генерировать синтетические события ввода / вывода (но только для эллипса) во время захвата мыши:

     private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var capturer = sender as FrameworkElement;
        capturer.CaptureMouse();
    }

    bool over = false;
    UIElement element;

    private void Rectangle_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton != MouseButtonState.Pressed) return;
        var capturer = sender as FrameworkElement;
        var hit = VisualTreeHelper.HitTest(this, e.GetPosition(this));
        if (hit == null) return;
        var thisElement = hit.VisualHit as Ellipse;
        var nowOver = thisElement != null;
        if (nowOver) element = thisElement;
        var args = new MouseEventArgs(Mouse.PrimaryDevice, 0);
        if (!over amp;amp; nowOver) { args.RoutedEvent = UIElement.MouseEnterEvent; element.RaiseEvent(args); }
        if (over amp;amp; !nowOver) { args.RoutedEvent = UIElement.MouseLeaveEvent; element.RaiseEvent(args); }
        over = nowOver;
    }

    private void Rectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        var capturer = sender as FrameworkElement;
        capturer.ReleaseMouseCapture();
    }

    private void Ellipse_MouseEnter(object sender, MouseEventArgs e)
    {
        Debug.WriteLine("MouseEnter");
    }

    private void Ellipse_MouseLeave(object sender, MouseEventArgs e)
    {
        Debug.WriteLine("MouseLeave");
    }
  

Если вы запустите демонстрационную версию в отладчике, вы увидите, что обработчики ввода / вывода вызываются независимо от того, захвачена мышь или нет.

Комментарии:

1. Я не совсем понимаю … не могли бы вы немного перефразировать? Что вы подразумеваете под «… вы можете проверить мышь. Наведите курсор мыши, чтобы увидеть, является ли это другим объектом … «? Будет ли это вообще работать, если мышь была захвачена другим объектом?

2. Конечно, мышь. DirectlyOver — лучший друг захваченной мыши. Вы просто вызываете его и смотрите, возвращает ли он тот класс, который вы ищете.

3. Упс, вы были правы; я был совершенно неправ. Мышь. DirectlyOver не функционирует при захвате мыши. Но есть и другие способы, например UIElement.IsMouseOver .

4. Хорошо, хвала за ЭТУ идею! 😀

5. Пока не сдавайтесь, VisualTreeHelper.HitTest могу сообщить вам все скрытые объекты, расположенные под данной точкой, с обратным вызовом: msdn.microsoft.com/en-us/library /…

Ответ №2:

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

Я решаю эту ситуацию, отслеживая кнопку мыши и движение и переводя элемент управления вместе.

Также можно использовать режим перетаскивания наложения.

Комментарии:

1. Это не перетаскивание. Это выглядит так только на графическом уровне, но я уже исключаю перетаскивание как жизнеспособный вариант для моих целей.

Ответ №3:

Поскольку код, который я использовал для имитации «перетаскивания» объекта, в любом случае включал получение положения мыши, я воспользовался этой возможностью и написал код, который проверял, находится ли положение мыши на холсте математически в пределах границ каждого из объектов, для которых мне нужен mouseenter / mouseleave. Это сработало особенно хорошо, поскольку объекты, для которых мне нужен был mouseover / mouseleave, никогда не меняли положения.

Вот урезанная версия моего окончательного кода.

 'Declare the left, right, top, and bottom boundaries of each of the "drop-spot" objects in relation to the canvas. This could also be determined programamtically for objects that change position.

Private Tile1PosL As Double = 55
Private Tile1PosT As Double = 30
Private Tile2PosL As Double = 164
Private Tile2PosT As Double = 69
Private Tile3PosL As Double = 282
Private Tile3PosT As Double = 41
Private Tile4PosL As Double = 405
Private Tile4PosT As Double = 69
Private Tile5PosL As Double = 514
Private Tile5PosT As Double = 12


Private Sub Tile1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles Tile1.MouseMove
    If IsDragging1 = True Then
        'My dragging functions go here.

        'Get the mouse position on the canvas (canv).
        Dim MousePosX As Double = e.GetPosition(canv).X
        Dim MousePosY As Double = e.GetPosition(canv).Y

        'Check to see if the mouse is within the boundaries of any of the "drop-spot" objects (Hole1, Hole2, Hole3, Hole4, Hole5).
        If MousePosX > Hole1L And MousePosX < Hole1R And MousePosY > Hole1T And MousePosY < Hole1B Then
            'Call the subroutine containing the code that would ordinarily go in "mouseenter".
            Hole1_TileEnter()
        ElseIf MousePosX > Hole2L And MousePosX < Hole2R And MousePosY > Hole2T And MousePosY < Hole2B Then
            'Call the subroutine containing the code that would ordinarily go in "mouseenter".
            Hole2_TileEnter()
        ElseIf MousePosX > Hole3L And MousePosX < Hole3R And MousePosY > Hole3T And MousePosY < Hole3B Then
            'Call the subroutine containing the code that would ordinarily go in "mouseenter".
            Hole3_TileEnter()
        ElseIf MousePosX > Hole4L And MousePosX < Hole4R And MousePosY > Hole4T And MousePosY < Hole4B Then
            'Call the subroutine containing the code that would ordinarily go in "mouseenter".
            Hole4_TileEnter()
        ElseIf MousePosX > Hole5L And MousePosX < Hole5R And MousePosY > Hole5T And MousePosY < Hole5B Then
            'Call the subroutine containing the code that would ordinarily go in "mouseenter".
            Hole5_TileEnter()
        Else
            'If the mouse is not within any of the "drop-spot" objects, call the subroutine containing the code that would ordinarily go in each object's "mouseleave". NOTE: This code contains statements that determine that the mouse had been inside one of the "drop-spots" before actually triggering the rest of its code.
            Hole_TileLeave()
        End If
    End If
    End If
    'This subroutine, with minor modifications, works for my Tile2, Tile3, Tile4, and Tile5 as well.
End Sub