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
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
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
MainWindow.xaml.cs
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