drop目标如何检测WPF中已取消的拖放?

时间:2022-05-02 00:20:18

I'm trying to write some generic code for handling drops in WPF drop targets. AllowDrop is set to true, and I've hooked onto DragEnter, DragOver, DragLeave, & Drop on the drop target UIElement. Using the bubbling events enables nesting of drop targets.

我正在尝试编写一些通用代码来处理WPF丢弃目标中的丢弃。 AllowDrop设置为true,我已经挂接到放置目标UIElement上的DragEnter,DragOver,DragLeave和Drop。使用冒泡事件可以嵌套放置目标。

Note: I have no access to the drag source - this is inter-application drag & drop.

注意:我无法访问拖动源 - 这是应用程序间的拖放操作。

If I have some UI cleanup to perform at the end of a potential drop and the user presses Esc to cancel the drop, the drop target never seems to gets a specific event that I can differentiate from all the others. Drop is easy, but what indicates a cancel?

如果我有一些UI清理要在潜在的丢弃结束时执行,并且用户按Esc取消丢弃,则放置目标似乎永远不会得到一个特定事件,我可以区别于所有其他事件。丢弃很容易,但什么表示取消?

The problem I have is this:

我遇到的问题是:

  • DragLeave is a bubbling routed event.
  • DragLeave是一个冒泡的路由事件。

  • e.OriginalSource is always set for this event (and the corresponding Preview) via hittesting.
  • e.OriginalSource始终通过hittesting为此事件(以及相应的预览)设置。

  • The target is an ItemsControl (Listbox is what I've currently been testing with).
  • 目标是ItemsControl(Listbox是我目前正在测试的)。

As I drag over my intended drop target, I get loads of DragLeave events from the child visuals within the target. I never get any from the target itself. Grids, rectangles, borders, text blocks, they all happily send me DragLeave, but none from the actual ItemsControl I'm connected up to. I thought it might be a hit-testing problem, but I've set the Background of the ItemsControl to a colour, and it makes no difference.

当我拖动我想要的放置目标时,我从目标中的子视觉效果中获取大量的DragLeave事件。我从来没有从目标本身得到任何东西。网格,矩形,边框,文本块,他们都很高兴地发送给我DragLeave,但没有来自我连接的实际ItemsControl。我认为它可能是一个命中测试问题,但我已将ItemsControl的背景设置为一种颜色,并没有任何区别。

What am I missing? How am I supposed to determine that a drop operation has definitely finished?

我错过了什么?我怎么能确定放下操作已经完成了?

(The actual problem I'm trying to solve is that I'm implementing some custom dragging behaviour in a TreeView that expands folders when you hover over them, and cancels timers & undoes the expansion when the drop is finished, and more to come, but I can't even get the events to fire sensibly for a ListBox).

(我试图解决的实际问题是我在TreeView中实现一些自定义拖动行为,当您将鼠标悬停在文件夹上时会扩展文件夹,并在删除完成时取消计时器并取消扩展,以及更多内容,但我甚至无法为ListBox明智地触发事件。

2 个解决方案

#1


0  

You do have a complex scenario here so this will start basic in hopes of giving you a direction and hopefully a solution.

你确实有一个复杂的场景,所以这将开始基本,希望给你一个方向,并希望有一个解决方案。

The framework will only inform of a DragEnter event if the control is marked with AllowDrop = true. So make sure you've done that. It sounds like you have but I just want to be sure.

如果控件标记为AllowDrop = true,则框架将仅通知DragEnter事件。所以一定要确保你做到了。这听起来像你有,但我只是想确定。

I'm not sure why you need the DragLeave event but if it's to grab the selected data the easiest way to get the data is not to hook into DragLeave but to hook into PreviewMouseMove. You can then determine if the mouse is pressed and how far of a distance to move before enacting a DoDragDrop.

我不确定你为什么需要DragLeave事件,但是如果要获取所选数据,最简单的获取数据的方法不是挂钩到DragLeave而是挂钩到PreviewMouseMove。然后,您可以在执行DoDragDrop之前确定是否按下鼠标以及移动距离的距离。

In this event, you also have the ability to add and analyze Drag Data. When the drag starts you can create a new DataObject and send it with the DoDragDrop call:

在这种情况下,您还可以添加和分析拖动数据。拖动开始时,您可以创建一个新的DataObject并使用DoDragDrop调用发送它:

  private static void listBox_PreviewMouseMove(object sender, MouseEventArgs e)
  {
     // Get the current mouse position
     var mousePos = e.GetPosition(null);
     var diff = startPoint - mousePos;

     if (canScroll && e.LeftButton == MouseButtonState.Pressed &&
         (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
          Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
     {
        var dataObject = (from lbi in startList.Items.Cast<object>().Select((t, i) => (ListBoxItem)startList.ItemContainerGenerator.ContainerFromIndex(i)) where lbi != null && lbi.IsSelected select Convert.ToString(lbi.Content)).ToList();

        // Initialize the drag & drop operation
        var listBoxData = new ListBoxData(){ StartList = startList, Data = dataObject};
        var dragData = new DataObject("listBoxData", listBoxData);

        System.Windows.DragDrop.DoDragDrop(startList, dragData, DragDropEffects.Move);
     }
  }

This drag data is now accessible through the DragEventArgs object by using (if you don't rename the event parameter, it's e):

现在可以通过使用DragEventArgs对象访问此拖动数据(如果不重命名事件参数,则为e):

e.Data.GetData("listBoxData")

My suggestion is to add some unique information to the drag event data to differentiate the event based on the data. Either your data item from the listbox selection, or a new class holding your data item and another indicator if needed.

我的建议是向拖动事件数据添加一些独特的信息,以根据数据区分事件。列表框选择中的数据项,或者包含数据项的新类和其他指示符(如果需要)。

#2


0  

check for e.Source and not e.OriginalSource, if you have set DropTarget="True" on the ItemsControl & you have DropOver event attached to the ItemsControl, the event argument e.Source should be ItemsControl.

检查e.Source而不是e.OriginalSource,如果你在ItemsControl上设置DropTarget =“True”并且你有DropOver事件附加到ItemsControl,则事件参数e.Source应该是ItemsControl。

#1


0  

You do have a complex scenario here so this will start basic in hopes of giving you a direction and hopefully a solution.

你确实有一个复杂的场景,所以这将开始基本,希望给你一个方向,并希望有一个解决方案。

The framework will only inform of a DragEnter event if the control is marked with AllowDrop = true. So make sure you've done that. It sounds like you have but I just want to be sure.

如果控件标记为AllowDrop = true,则框架将仅通知DragEnter事件。所以一定要确保你做到了。这听起来像你有,但我只是想确定。

I'm not sure why you need the DragLeave event but if it's to grab the selected data the easiest way to get the data is not to hook into DragLeave but to hook into PreviewMouseMove. You can then determine if the mouse is pressed and how far of a distance to move before enacting a DoDragDrop.

我不确定你为什么需要DragLeave事件,但是如果要获取所选数据,最简单的获取数据的方法不是挂钩到DragLeave而是挂钩到PreviewMouseMove。然后,您可以在执行DoDragDrop之前确定是否按下鼠标以及移动距离的距离。

In this event, you also have the ability to add and analyze Drag Data. When the drag starts you can create a new DataObject and send it with the DoDragDrop call:

在这种情况下,您还可以添加和分析拖动数据。拖动开始时,您可以创建一个新的DataObject并使用DoDragDrop调用发送它:

  private static void listBox_PreviewMouseMove(object sender, MouseEventArgs e)
  {
     // Get the current mouse position
     var mousePos = e.GetPosition(null);
     var diff = startPoint - mousePos;

     if (canScroll && e.LeftButton == MouseButtonState.Pressed &&
         (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
          Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
     {
        var dataObject = (from lbi in startList.Items.Cast<object>().Select((t, i) => (ListBoxItem)startList.ItemContainerGenerator.ContainerFromIndex(i)) where lbi != null && lbi.IsSelected select Convert.ToString(lbi.Content)).ToList();

        // Initialize the drag & drop operation
        var listBoxData = new ListBoxData(){ StartList = startList, Data = dataObject};
        var dragData = new DataObject("listBoxData", listBoxData);

        System.Windows.DragDrop.DoDragDrop(startList, dragData, DragDropEffects.Move);
     }
  }

This drag data is now accessible through the DragEventArgs object by using (if you don't rename the event parameter, it's e):

现在可以通过使用DragEventArgs对象访问此拖动数据(如果不重命名事件参数,则为e):

e.Data.GetData("listBoxData")

My suggestion is to add some unique information to the drag event data to differentiate the event based on the data. Either your data item from the listbox selection, or a new class holding your data item and another indicator if needed.

我的建议是向拖动事件数据添加一些独特的信息,以根据数据区分事件。列表框选择中的数据项,或者包含数据项的新类和其他指示符(如果需要)。

#2


0  

check for e.Source and not e.OriginalSource, if you have set DropTarget="True" on the ItemsControl & you have DropOver event attached to the ItemsControl, the event argument e.Source should be ItemsControl.

检查e.Source而不是e.OriginalSource,如果你在ItemsControl上设置DropTarget =“True”并且你有DropOver事件附加到ItemsControl,则事件参数e.Source应该是ItemsControl。