WPF如何处理拖放和左键单击?

6

我在使用DragDrop.DoDragDrop时遇到了一些问题,它无法很好地与左键单击事件配合使用。

我的控件有几个链接,可以拖动或左键单击以访问。

我目前订阅了预览鼠标移动事件,如果按下左键,则会启动拖放事件。

在另一个回调中,我处理鼠标左键按下和抬起事件以检查是否进行了单击。有没有办法检查DragDrop是否实际上发生了拖放事件?

2个回答

22

请参考此链接中关于WPF拖放的全面解释,并向下滚动到“检测拖放”一节。

以下代码在博客丢失时插入...

从 [http://msdn2.microsoft.com/en-us/library/aa289508(vs.71).aspx] 可以看出典型拖放操作的事件序列:

通过为源控件调用DoDragDrop方法来启动拖动。

DoDragDrop方法采用两个参数: data,指定要传递的数据;allowedEffects,指定允许哪些操作(复制和/或移动)。

将自动创建一个新的DataObject对象。 这又会引发GiveFeedback事件。在大多数情况下,您不需要担心GiveFeedback事件,但如果您想在拖动期间显示自定义鼠标指针,则可以在这里添加代码。

任何其AllowDrop属性设置为True的控件都是潜在的放置目标。AllowDrop属性可以在设计时在属性窗口中设置,也可以在Form_Load事件中以编程方式设置。

当鼠标经过每个控件时,该控件的DragEnter事件会被触发。GetDataPresent方法用于确保数据格式适合目标控件,Effect属性用于显示适当的鼠标指针。

如果用户在有效的放置目标上释放鼠标按钮,则触发DragDrop事件。在DragDrop事件处理程序中的代码从DataObject对象中提取数据,并在目标控件中显示它。

检测拖放

在调用DoDragDrop之前,我们必须检测源上的鼠标拖动操作......通常鼠标拖动由MouseLeftButtonDown + MouseMove(在MouseLeftButton抬起之前)组成。

因此,我们的拖放源控件需要订阅这两个事件:

void Window1_Loaded(object sender, RoutedEventArgs e)
{
    this.DragSource.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown);
    this.DragSource.PreviewMouseMove += new MouseEventHandler(DragSource_PreviewMouseMove);
}

为了防止用户意外拖动而启动虚假的拖放操作,您可以使用

SystemParameters.MinimumHorizontalDragDistance 和 SystemParameters.MinimumVerticalDragDistance

其中一种方式是在 MouseLeftButtonDown 事件中记录起始位置,然后在 onMouseMove 事件中检查鼠标是否移动了足够远的距离。

void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
    {
        Point position = e.GetPosition(null);

        if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
                Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
        {
            StartDrag(e); 

        }
    }   
}

void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    _startPoint = e.GetPosition(null);
}

这是一个拖放操作,现在怎么办?

数据!你需要找出拖动鼠标时下面是什么。 我会忽略简单的方法,并假设触发MouseMove的人就是我想拖的人。所以看一下MouseEventArgs.OriginalSource.. [或者您可以使用VisualTreeHelper进行一些2D HitTesting...在本文第3部分中,我们将尝试为您介绍ListBox的HitTesting-这是我遇到的另一种常见情况。

一旦您有了要拖动的对象,就需要将您要发送的内容打包成一个DataObject,描述您正在传递的数据。 DataObject是一个包装器,用于将通用数据(使用可扩展格式标识)推入拖放操作中。只要源和目标都理解该格式,您就可以设置。因此,DataObject具有几个有趣的方法:

SetData (Type format, object data) ///格式是您传递的“格式”的当天情况(例如Formats.Text、Formats.Image等),您可以传递任何自定义类型。

GetDataPresent (Type format) ///是投放目标将用于查询和提取数据的方式。如果它是它可以处理的类型,它将调用GetData()并处理它。

这里没有太多有趣的东西...在示例中,我只将我的数据硬编码为字符串类型...这使得它更容易粘贴到外部容器中(例如Word,您可以用它来测试本文的这一部分)。我必须强调拖放应该是关于数据的......在拖放操作期间提供视觉反馈。

在调用DoDragDrop()之前,我们需要在提供反馈和d&d的“范围”方面做出一些“选择”。

我们是否想要在执行拖动操作时显示自定义光标?如果要使用光标,则应该是什么样式?

我们希望拖动多远?在应用程序内还是跨窗口应用程序?

最简单的情况:无自定义光标,并且我们希望它跨越应用程序拖动:

如果您不想要花哨的光标,那么您已经完成了!!您可以直接调用DoDragDrop...

private void StartDrag(MouseEventArgs e)
{
    IsDragging = true;
    DataObject data = new DataObject(System.Windows.DataFormats.Text.ToString(), "abcd");
    DragDropEffects de = DragDrop.DoDragDrop(this.DragSource, data, DragDropEffects.Move);
    IsDragging = false;
}

注意:这段代码允许您在进程间拖放,它使用默认的操作系统反馈(例如+表示复制)。


3
5年后我被投了一票反对,但没有任何解释,这个系统太愚蠢了。 - Paul Zahra
为了防止奇怪的鼠标右键单击行为,我不得不添加以下内容:private void DataGrid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { _startPoint = default; } 和:&& _startPoint != default after: && !IsDragging - IngoB

0

有拖放事件的 Drag Over/Enter/Leave Attached。您可以在被拖曳的 UIElement 上订阅这些 (或一个) 事件的方法,以检查是否正在进行拖动。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接