DragDrop - события DragEnter/DragLeave продолжают стрелять

У меня есть операция перетаскивания на Canvas, которая должна что-то делать, когда объект втягивается и вынимается из него. Моя проблема в том, что события DragEnter/DragLeave продолжают стрелять, когда мышь перемещает объект поверх него, а не только при входе/выходе. Чем быстрее перемещается мышь, тем чаще происходит огонь.

Событие Canvas DragOver перемещает Canvas.Top/Left объекта DraggedObject, и я думаю, что это может быть моей проблемой, но я не уверен, как это исправить.

Ответы

Ответ 1

Вот последовательность событий:

  • Ваша мышь перемещается от того места, где вы щелкнули ее по панели. Нет событий.
  • Ваша мышь достигает края панели и входит в холст. Событие DragEnter запускается на холсте.
  • Обработчик DragEnter перемещает панель так, чтобы она теперь находилась под мышью. Поскольку мышь больше не находится над холстом (это над панелью), срабатывает событие DragLeave.
  • Ваша мышь движется дальше, снова дойдя до края панели. Повторяются шаги 2 и 3.

Чем быстрее вы перемещаете мышь, тем больше событий вы получите.

Суть вашей проблемы заключается в том, что drag-drop использует тестовое тестирование, и, перемещая вашу панель, вы побеждаете способность теста на попадание, чтобы увидеть "позади" вашей панели, чтобы узнать, в каком контейнере она падает.

Решение состоит в том, чтобы использовать собственный код для обработки перетаскивания, что совсем не сложно. Тест-драйв WPF достаточно прост, чтобы выполнять тестирование с ударами по текущему объекту, но перетаскивание не использует эту функцию. Вы можете использовать его напрямую, но используя перегрузку VisualTreeHelper.HitTest, которая принимает значения HitTestFilterCallback и HitTestResultCallback. Просто передайте ему фильтр, который игнорирует любые удары в панели, которые перетаскиваются.

Я обнаружил, что для сценариев, подобных описанию, выполнение перетаскивания путем обработки событий мыши на самом деле проще, чем использование встроенного DoDragDrop, потому что вам не нужно справляться со сложностью (DataObject, DragDropEffects, QueryContinueDrag и т.д.). Эта дополнительная сложность очень важна для включения сценариев перетаскивания между приложениями и процессами, но не помогает вам в том, что вы делаете.

Здесь простое решение:

  • В MouseDown с левой кнопкой вниз запишите позицию (на MouseLeave удалите позицию)
  • В MouseMove с нажатием левой кнопки, позицией, текущая позиция мыши отличается более чем на дельту} установите флаг, в котором выполняется операция перетаскивания, и запустите мышь
  • В MouseMove с продолжением операции перетаскивания используйте тестовое тестирование, чтобы определить, где должна находиться ваша панель (игнорируя панель) и соответствующим образом отрегулируйте ее родительские позиции и позицию.
  • В MouseUp при выполнении операции перетаскивания отпустите захват мыши и снимите флажок "операция перетаскивания".

Enjoy.

Ответ 2

Это немного старо, но у меня была та же проблема. Я использовал adorner, чтобы указать, куда перетаскивали перетаскиваемую вещь. Я бы включил adorner в DragEnter, а затем сразу зарегистрировал DragLeave и отключил adorner, затем a DragEnter...

IsEnabled = false не работал у меня, но IsHitTestVisible = false сделал. Я положил это в конструктор моего поклонника, и теперь все это прекрасно работает:

public DragDropAdorner(UIElement adornedElement) : base(adornedElement)
{
    _layer = AdornerLayer.GetAdornerLayer(AdornedElement);
    _layer.IsEnabled = false;
    _layer.IsHitTestVisible = false;
}

Ответ 3

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

Хорошо, это то, что происходит:

  • Вы создаете операцию перетаскивания.
  • Во время перетаскивания вы хотите, чтобы что-то визуальное отображалось вдоль мыши.
  • Этот визуальный элемент добавляется к сетке или панели при событии Drag-Enter и удаляется из события Drag-Leave.
  • Когда он помещается под мышью, вы получите событие drag-leave, поскольку элемент, который вы рисуете, фактически перехватывает операцию перетаскивания.

Решение: Убедитесь, что IsEnabled=false для элемента, который вы рисуете под мышью.
Таким образом, вы не будете захватывать операцию перетаскивания, и вы не получите мерцающего эффекта.

Ответ 4

У меня была похожая проблема, и магия, которая исправила ее, была e.Handled = true;

Я хотел выбрать TabItem, когда над ним что-то перетаскивали. Это моё решение.

В словаре ресурсов определите:

<Style TargetType="TabItem">
    <Setter Property="AllowDrop" Value="True" />
    <EventSetter Event="DragEnter" Handler="tabDragEnter" />
    <EventSetter Event="DragOver" Handler="tabDragOver" />
</Style>

Затем обработчик tabDragEnter:

private void tabDragEnter(object sender, DragEventArgs e)
    {            
        TabItem m_TabItem = sender as TabItem;
        m_TabItem.IsSelected = true;
    }

и, наконец, обработчик tabDragOver:

private void tabDragOver(object sender, DragEventArgs e)
    {
        e.Effects = DragDropEffects.None;
        e.Handled = true;
    }

Ответ 5

Недавно у меня была похожая проблема, хотя она была в рамках Angular 6 с использованием реактивных форм. Вот как я решил это для моей ситуации:

Вкратце, я отключил обнаружение изменений для этого компонента во время перетаскивания.

  1. import ChangeDetectorRef:
    import { ChangeDetectorRef } from '@angular/core';
  1. введите его в конструктор:
    constructor(private chngDetRef: ChangeDetectorRef) { //...
  1. отключить его на dragStart:
    private onDragStart(event, dragSource, dragIndex) {
        // ...
        this.chngDetRef.detach();
        // ...
  1. подключите его по капле и перетащите:
    private onDrop(event, dragSource, dragIndex) {
        // ...
        this.chngDetRef.reattach();
        // ...

    private onDragEnd(event, dragIndex) {
        // ...
        this.chngDetRef.reattach();
        // ...

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

Удачи!