Skip to content

DragLeave constructs DragEventArgs with coordinates relative to the new target instead of the event target #11774

Description

@mosyy098

Description

In WPF's drag-and-drop implementation, when the mouse moves from one AllowDrop element to another, the DragLeave event is raised for the previous one. However, the DragEventArgs passed to this event contains coordinates that are relative to the new element (element to enter), not the event target (element which mouse left).

This bug causes DragEventArgs.GetPosition(IInputElement) to return meaningless Point when called from a DragLeave's arg. Because the DragEventArgs._target stored is not the coordinate system of DragEventArgs._dropPoint that used for transformation.

Root cause:
https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DragDrop.cs
Line 978, within UnsafeNativeMethods.IOleDropTarget.OleDragOver

DependencyObject currentTarget = GetCurrentTarget(point, out targetPoint);
if (currentTarget != null)
{
  if (currentTarget != _lastTarget)
  {
    try
    {
      if (_lastTarget != null)
      {
        RaiseDragEvent(DragDrop.DragLeaveEvent, dragDropKeyStates, ref effects, _lastTarget, targetPoint);
      }
      RaiseDragEvent(DragDrop.DragEnterEvent, dragDropKeyStates, ref effects, currentTarget, targetPoint);
    }

DragLeaveEvent was raised using variable _lastTarget and targetPoint. But targetPoint is in the coordinate system of currentTarget, not the _lastTarget.

Reproduction Steps

Create a new .NET 10 WPF proj

MainWindow.xaml

<Window x:Class="DragLeaveCoordBug.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="450" Width="600">
    <ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch" AllowDrop="True" DragLeave="ScrollViewer_DragLeave">
        <Rectangle Fill="MediumAquamarine" Width="100" Height="999"/>
    </ScrollViewer>
</Window>

MainWindow.xaml.cs

using System.Diagnostics;
using System.Windows;

namespace DragLeaveCoordBug
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void ScrollViewer_DragLeave(object sender, DragEventArgs e)
        {
            if (e.KeyStates.HasFlag(DragDropKeyStates.ShiftKey))
            {
                Debug.WriteLine(new Point(ActualWidth, ActualHeight));
                Debug.WriteLine(e.GetPosition(this));
            }
        }
    }
}

Run the Window, drag something onto the scrollbar. Then hold shift and drag you mouse slowly out of the scrollbar from the left side.
You can see something like this in debug output:
600,450.40000000000003
1136.8000000000002,330.40000000000003

First line comes from: Debug.WriteLine(new Point(ActualWidth, ActualHeight));
Second line comes from: Debug.WriteLine(e.GetPosition(this));

Obviously, e.GetPosition(this) should not be outside of the bound of mainWindow (1136 > 600)

How do we get 1136.8?
scrollview.ActualWidth == 585.6, scrollbar.ActualWidth == 16.8, e._dropPoint.X == 568
e thinks _dropPoint is based on scrollbar (we just move our mouse out of it). But actually, _dropPoint is based on mainWindow (when your mouse get out of scrollbar from left side, it will be over mainWindow directly)
So e.GetPosition(this) will calc the x pos like this: 585.6 - 16.8 + 568 (slightly simplified because scrollview's HorizontalAlignment is stretch)

Expected behavior

DragLeave events should carry coordinates in the coordinate system of _target.

Actual behavior

DragLeave events stored wrong element as the coordinate system of _target.

Regression?

No response

Known Workarounds

No response

Impact

No response

Configuration

No response

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugProduct bug (most likely)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions