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 с использованием реактивных форм. Вот как я решил это для моей ситуации:
Вкратце, я отключил обнаружение изменений для этого компонента во время перетаскивания.
- import ChangeDetectorRef:
import { ChangeDetectorRef } from '@angular/core';
- введите его в конструктор:
constructor(private chngDetRef: ChangeDetectorRef) { //...
- отключить его на dragStart:
private onDragStart(event, dragSource, dragIndex) {
// ...
this.chngDetRef.detach();
// ...
- подключите его по капле и перетащите:
private onDrop(event, dragSource, dragIndex) {
// ...
this.chngDetRef.reattach();
// ...
private onDragEnd(event, dragIndex) {
// ...
this.chngDetRef.reattach();
// ...
Если у вас много родительских или многоуровневых компонентов, вам, возможно, придется что-то делать с обнаружением их изменений, чтобы увидеть существенное улучшение.
Удачи!