DragDrop - DragEnter / DragLeave事件继续触发

时间:2010-04-13 20:06:45

标签: wpf drag-and-drop

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

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

5 个答案:

答案 0 :(得分:17)

以下是事件序列:

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

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

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

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

    以下是简单的解决方案:

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

答案 1 :(得分:2)

这有点旧,但我有同样的问题。我正在使用一个装饰器来指示被拖动的东西被拖到哪里。我会在DragEnter中启用该装饰,然后立即注册DragLeave并停用该装饰,然后DragEnter ...

IsEnabled = false对我不起作用,但IsHitTestVisible = false做了。我把它放在我的装饰者的构造函数中,现在一切都很好用了:

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

答案 2 :(得分:2)

我最近有一个类似的问题,尽管它在Angular 6框架中,使用了反应形式。这是我针对自己的情况解决的方法:

基本上,我在拖动时关闭了该组件上的更改检测。

  1. 导入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();
        // ...

如果您有很多父级或分层组件,则可能还必须对它们的更改检测进行一些操作才能看到实质性的改进。

祝你好运!

答案 3 :(得分:0)

我发现有必要为正在发生的事情添加更准确的答案,我也会在与此问题相关的其他问题中添加相同的答案。

好的,这就是发生的事情:

  1. 您可以创建拖放操作。
  2. 当你拖放时,你想要在你的鼠标上显示一些视觉效果。
  3. 该视觉元素正在添加到Drag-Enter事件的网格或面板上,并在Drag-Leave事件中被删除。
  4. 当它被放置在鼠标下方时,你会得到一个拖拽离开事件,因为你绘制的元素实际上窃取了拖放操作..
  5. 解决方案: 确保您在鼠标下绘制的元素为IsEnabled=false
    这样它就不会捕获拖放操作,也不会产生闪烁效果。

答案 4 :(得分:0)

我遇到了类似的问题,而解决该问题的魔力是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;
    }