DragDrop - DragEnter/DragLeave事件继续触发

Rac*_*hel 13 wpf drag-and-drop

我在Canvas上有一个拖放操作,当一个对象被拖入和拖出时,它应该做某事.我的问题是DragEnter/DragLeave事件在鼠标移动对象时继续触发,而不仅仅是在进入/退出时.鼠标移动得越快,事件发生的频率就越高.

Canvas DragOver事件移动了DraggedObject的Canvas.Top/Left,我认为这可能是我的问题,但我不知道如何解决这个问题.

Ray*_*rns 17

以下是事件序列:

  1. 您的鼠标从您在面板上单击它的位置移动.没有事件发生.
  2. 您的鼠标到达Panel的边缘并进入Canvas.该DragEnter事件在画布上触发.
  3. 您的DragEnter处理程序移动Panel,使其现在位于鼠标下方.由于鼠标不再位于Canvas上(它位于Panel上方),因此DragLeave事件将触发.
  4. 鼠标移动得更远,再次到达面板边缘.重复步骤2和3.

移动鼠标的速度越快,您收到的事件就越多.

你的问题的本质是拖放使用命中测试,并且通过移动你的面板,你正在击败命中测试的能力,看看你的面板"后面"知道它被丢弃的容器.

解决方案是使用您自己的代码进行拖放处理,这实际上并不困难.WPF的命中测试引擎足够强大,可以在当前对象后面进行命中测试,但拖放功能不会使用此功能.你可以直接使用它,但是使用VisualTreeHelper.HitTest带a HitTestFilterCallback和a 的重载HitTestResultCallback.只需传递一个过滤器,忽略被拖动面板内的任何命中.

我发现对于你描述的场景,通过处理鼠标事件进行拖放实际上比使用内置的DoDragDrop更容易,因为你不必处理复杂性(DataObject,DragDropEffects,QueryContinueDrag等).这种额外的复杂性对于启用应用程序和进程之间的拖动方案非常重要,但对您正在执行的操作没有帮助.

这是简单的解决方案:

  1. 在鼠标左键上按下左键,记录位置(在MouseLeave上擦除位置)
  2. 在MouseMove上{左按钮向下,记录位置,当前鼠标位置相差超过delta}设置一个标志,表示正在进行拖动操作并捕获鼠标
  3. 在正在进行拖动操作的MouseMove上,使用命中测试来确定面板的位置(忽略面板本身)并相应地调整其父级和位置.
  4. 在正在进行拖动操作的MouseUp上,释放鼠标捕获并清除"拖动操作正在进行中"标志

请享用.