Sitelet https://github.com/dotnet/maui/pull/39016
Skip to content

[net11.0] Add opt-in Move support to drag and drop - #39016

Merged
kubaflo merged 1 commit into
net11.0from
jfversluis-issue-16472-implementation
Oct 1, 2026
Merged

kubaflo merged 1 commit into
net11.0from
jfversluis-issue-16472-implementation

Conversation

@jfversluis

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

Adds DataPackageOperation.Move = 2 so a drop target can request Move through the shared DragEventArgs.AcceptedOperation API.

On iOS and Mac Catalyst, preserve and aggregate accepted operations rather than reducing them to a Boolean. Propose native Move only when the drag session permits it. Default Copy behavior, Copy preference for Copy | Move, acceptance across multiple recognizers, and explicit native drop-proposal precedence are preserved.

Windows and Android framework implementations are unchanged. The Windows sample explicitly opts its source into native Copy | Move permissions, and the Apple sample uses shared Move acceptance. Updates API declarations and documents the application-owned data changes.

This does not automatically remove source data, reorder collections, or implement CollectionView reordering. Applications still own those changes. It also does not guarantee specific preview dimensions.

Release Target and Regression Risk

This should still be safe to consider for .NET 11: the behavior is opt-in, the implementation is narrow, and the existing supported None/Copy behavior is preserved. Independent reviews with GPT-6 Astra and Claude Opus 4.8 found no concrete regression for those existing supported inputs, supported by the local results below.

If this is too late in the .NET 11 release cycle, we can defer it to .NET 12. Preserving existing behavior takes priority over landing the feature before the cutoff. This is not an unconditional last-minute merge recommendation: normal candidate CI and affected-platform validation should still gate acceptance, particularly Mac Catalyst, which shares the changed Apple delegate, and the Windows sample's source-operation negotiation.

One compatibility caveat is explicit: on Apple platforms, previously undefined numeric enum values can behave differently. For example, (DataPackageOperation)4 previously fell back to Copy and now results in Cancel when no native proposal override is provided. This is outside the previously declared None/Copy contract, but is not a claim of identical behavior for every numeric cast.

Validation

Local suite Result
Shared gesture/data-package tests 169 passed, including 159 pre-existing cases
Existing iOS drag/drop NUnit/Appium UI tests 6 passed
Native iOS device tests 41 passed, including 38 drag/drop cases
Controls product build targeting net11.0 Passed with 0 warnings and 0 errors

The existing UI tests exercise real gestures, cross-layout drops, event delivery, platform event arguments, and drag/drop coordinates. Additional local Sandbox/Appium checks covered eight scenarios, including default Copy, explicit Move, rejected drops, cancellation outside a target, native overrides, and requesting full-size previews.

Native regression coverage includes source Move restrictions; None, Copy, Move, and combined flags; missing/disabled recognizers; recognizer ordering and passive recognizers; custom native proposals; per-update state reset; and disabling, removing, and replacing recognizers between updates.

Validation limits: iOS ran on an iPhone 17 Pro simulator with iOS 26.4 using the worktree-local .NET 11 RC2 SDK and iOS workload. Local toolchain workarounds included an app-only runtime override, MtouchLink=SdkOnly, and ValidateXcodeVersion=false; those temporary project/harness changes are not included in this PR. Windows, Android, and Mac Catalyst have not been runtime-tested locally. A fail-without-fix comparison was not completed. Local results do not replace standard release-configuration or candidate CI validation.

Issues Fixed

Fixes #16472

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 12:25
@jfversluis
jfversluis deployed to copilot-pat-pool September 30, 2026 12:25 — with GitHub Actions Active
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 39016

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 39016"

@jfversluis
jfversluis changed the base branch from main to net11.0 September 30, 2026 12:26
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions

Copy link
Copy Markdown
Contributor

Skill Validation Results

@jfversluis — new skill validation results are available based on this last commit: 5d52529.
To request a fresh validation after new comments or commits, comment /evaluate-skills.

Overall Passed Static Passed LLM Skipped Skills 25 Agents 6

✅ Skill Validation Results — 5d52529 · [net11.0] Add opt-in Move support to drag and drop · 2026-09-30T12:27:33Z

✅ Static Checks Passed

Skills: 25 | Eval specs linted: 17

Full lint output
── .github/skills/agentic-labeler/tests/eval.vally.yaml
Vally collects pseudonymous usage telemetry (command name, version, outcome, duration, persistent device identifier (when available), and coarse OS/runtime info) to improve the product. No prompts, datasets, file paths, or arguments are collected.
Opt out any time by setting VALLY_TELEMETRY_OPTOUT=1 or DO_NOT_TRACK=1.
Learn more: https://aka.ms/vally/telemetry
✔ .github/skills/agentic-labeler/tests/eval.vally.yaml is valid
── .github/skills/analyze-sessions/tests/eval.vally.yaml
✔ .github/skills/analyze-sessions/tests/eval.vally.yaml is valid
── .github/skills/ci-fix/tests/eval.ownership.vally.yaml
✔ .github/skills/ci-fix/tests/eval.ownership.vally.yaml is valid
── .github/skills/ci-fix/tests/eval.vally.yaml
✔ .github/skills/ci-fix/tests/eval.vally.yaml is valid
── .github/skills/code-review/tests/eval.inline-findings.vally.yaml
✔ .github/skills/code-review/tests/eval.inline-findings.vally.yaml is valid
── .github/skills/code-review/tests/eval.producer-trace.vally.yaml
✔ .github/skills/code-review/tests/eval.producer-trace.vally.yaml is valid
── .github/skills/code-review/tests/eval.trim-aot.vally.yaml
✔ .github/skills/code-review/tests/eval.trim-aot.vally.yaml is valid
── .github/skills/code-review/tests/eval.vally.yaml
✔ .github/skills/code-review/tests/eval.vally.yaml is valid
── .github/skills/code-review/tests/hermeticity.vally.yaml
✔ .github/skills/code-review/tests/hermeticity.vally.yaml is valid
── .github/skills/code-review/tests/soak.capability.vally.yaml
✔ .github/skills/code-review/tests/soak.capability.vally.yaml is valid
── .github/skills/evaluate-pr-tests/tests/eval.vally.yaml
✔ .github/skills/evaluate-pr-tests/tests/eval.vally.yaml is valid
── .github/skills/pr-review/tests/eval.gh-auth.vally.yaml
✔ .github/skills/pr-review/tests/eval.gh-auth.vally.yaml is valid
── .github/skills/review-test-failures/tests/eval.vally.yaml
✔ .github/skills/review-test-failures/tests/eval.vally.yaml is valid
── .github/skills/try-fix/tests/eval.restore.vally.yaml
✔ .github/skills/try-fix/tests/eval.restore.vally.yaml is valid
── .github/skills/try-fix/tests/eval.vally.yaml
✔ .github/skills/try-fix/tests/eval.vally.yaml is valid
── .github/skills/verify-tests-fail-without-fix/tests/eval.protocol.vally.yaml
✔ .github/skills/verify-tests-fail-without-fix/tests/eval.protocol.vally.yaml is valid
── .github/skills/verify-tests-fail-without-fix/tests/eval.vally.yaml
✔ .github/skills/verify-tests-fail-without-fix/tests/eval.vally.yaml is valid

⏭️ LLM Evaluation: Skipped

No changed skills with eval specs found.

🔍 Full results and investigation steps

@jfversluis
jfversluis deployed to copilot-pat-pool September 30, 2026 12:29 — with GitHub Actions Active
@jfversluis
jfversluis deployed to copilot-pat-pool September 30, 2026 12:30 — with GitHub Actions Active
@github-actions github-actions Bot added area-gestures Gesture types platform/ios platform/macos macOS / Mac Catalyst labels Sep 30, 2026
@jfversluis
jfversluis deployed to copilot-pat-pool September 30, 2026 12:31 — with GitHub Actions Active
@kubaflo

This comment has been minimized.

@kubaflo

kubaflo commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

/azp run

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Sep 30, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

@github-actions

Copy link
Copy Markdown
Contributor

Note

🔍 /review started

AzDO build 15518254 is running for ios.

The s/agent-review-in-progress label stays on this PR while the run is active. The final recommendation and outcome labels are posted only after Gate, expert review, and Deep UI tests finish.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Cross-platform build, device, and UI pipelines remain pending for this Apple platform behavior change.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

Adds opt-in drag-and-drop Move support across the shared API, Apple implementation, and platform samples.

Changes:

  • Adds DataPackageOperation.Move with API documentation.
  • Maps accepted operations to native iOS/Mac Catalyst proposals.
  • Adds unit/device coverage and updates Windows/iOS samples.
File Description
src/​Controls/​tests/​DeviceTests/​Elements/​DragAndDrop/​DragAndDropTests.iOS.cs Tests Apple proposal selection and recognizer behavior.
src/​Controls/​tests/​Core.UnitTests/​Gestures/​DropGestureRecognizerTests.cs Tests enum flags and accepted-operation handling.
src/​Controls/​src/​Core/​PublicAPI/​netstandard/​PublicAPI.Unshipped.txt Declares the new API.
src/​Controls/​src/​Core/​PublicAPI/​net/​PublicAPI.Unshipped.txt Declares the new API.
src/​Controls/​src/​Core/​PublicAPI/​net-windows/​PublicAPI.Unshipped.txt Declares the Windows API.
src/​Controls/​src/​Core/​PublicAPI/​net-tizen/​PublicAPI.Unshipped.txt Declares the Tizen API.
src/​Controls/​src/​Core/​PublicAPI/​net-maccatalyst/​PublicAPI.Unshipped.txt Declares the Mac Catalyst API.
src/​Controls/​src/​Core/​PublicAPI/​net-ios/​PublicAPI.Unshipped.txt Declares the iOS API.
src/​Controls/​src/​Core/​PublicAPI/​net-android/​PublicAPI.Unshipped.txt Declares the Android API.
src/​Controls/​src/​Core/​Platform/​iOS/​DragAndDropDelegate.cs Maps aggregated acceptance to Copy, Move, or Cancel.
src/​Controls/​src/​Core/​DragAndDrop/​DragEventArgs.cs Documents platform negotiation behavior.
src/​Controls/​src/​Core/​DragAndDrop/​DataPackageOperation.cs Adds the Move flag.
src/​Controls/​samples/​Controls.Sample/​Pages/​PlatformSpecifics/​Windows/​WindowsDragAndDropCustomization.xaml.cs Configures native Windows Move permissions.
src/​Controls/​samples/​Controls.Sample/​Pages/​PlatformSpecifics/​Windows/​WindowsDragAndDropCustomization.xaml Adds the Move sample control and event hookup.
src/​Controls/​samples/​Controls.Sample/​Pages/​PlatformSpecifics/​iOS/​iOSDragAndDropRequestFullSize.xaml.cs Uses shared Move acceptance on Apple platforms.

e.PlatformArgs!.SetDropProposal(new UIKit.UIDropProposal(UIKit.UIDropOperation.Copy));
else if (moveSwitch.IsToggled)
e.PlatformArgs!.SetDropProposal(new UIKit.UIDropProposal(UIKit.UIDropOperation.Move));
e.AcceptedOperation = DataPackageOperation.Move;
@Pastajello

Copy link
Copy Markdown

Dang, paying my respect. That was fast :D

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@jfversluis — new AI review results are available based on commit 5d52529.

Gate Inconclusive Confidence Low Platform iOS


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ⚠️ INCONCLUSIVE

Platform: IOS · Base: net11.0 · Merge base: 528f519a

🩺 Could not verify — environment/infrastructure error. The gate ran the tests but hit an environment error (an emulator/simulator/Appium/XHarness flake, a device that would not boot, or an empty/invalid result file), so it could not record a real pass/fail. The ⚠️ ENV ERROR marks below are infrastructure, not test failures — this is not a problem with your PR. Comment /review to retry on a fresh agent.

XHarness did not produce the expected fresh result 'testResults.xml' for requested class(es) 'Microsoft.Maui.DeviceTests.DragAndDropTests' (the target tests did not run).

Test Without Fix (expect FAIL) With Fix (expect PASS)
🧪 DropGestureRecognizerTests DropGestureRecognizerTests 🛠️ BUILD ERROR ✅ PASS — 23s
📱 DragAndDropTests (SessionDidUpdateDefaultsToCopy, SessionDidUpdateRoutesAcceptedOperation, SessionDidUpdateWithoutDropRecognizerCancels, SessionDidUpdateSkipsDisabledDropRecognizer, SessionDidUpdateHonorsCustomProposalOnlyWhenAccepted, SessionDidUpdateCombinesRecognizerAcceptance, SessionDidUpdateDoesNotRetainPreviousAcceptance, SessionDidUpdateDoesNotRetainPreviousCustomProposal, SessionDidUpdateReevaluatesChangedRecognizers, SessionDidUpdatePreservesAcceptanceThroughPassiveRecognizer) Category=Gesture 🛠️ BUILD ERROR ⚠️ ENV ERROR
🔴 Without fix — 🧪 DropGestureRecognizerTests: 🛠️ BUILD ERROR · 20s

Error-relevant lines (filtered from the build log):

/Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Gestures/DropGestureRecognizerTests.cs(39,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Gestures/DropGestureRecognizerTests.cs(40,64): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Gestures/DropGestureRecognizerTests.cs(54,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Gestures/DropGestureRecognizerTests.cs(55,64): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj]
🟢 With fix — 🧪 DropGestureRecognizerTests: PASS ✅ · 23s

(no coded error found; showing last 1200 chars)

ompatibleTarget(fieldType: typeof(Microsoft.Maui.Controls.Entry), result: "EntryTest") [< 1 ms]
  Passed TextPackageCorrectlySetsOnCompatibleTarget(fieldType: typeof(Microsoft.Maui.Controls.TimePicker), result: "01:00:00") [< 1 ms]
  Passed TextPackageCorrectlySetsOnCompatibleTarget(fieldType: typeof(Microsoft.Maui.Controls.RadioButton), result: "True") [1 ms]
  Passed TextPackageCorrectlySetsOnCompatibleTarget(fieldType: typeof(Microsoft.Maui.Controls.Label), result: "LabelTest") [< 1 ms]
  Passed TextPackageCorrectlySetsOnCompatibleTarget(fieldType: typeof(Microsoft.Maui.Controls.Editor), result: "EditorTest") [< 1 ms]
  Passed DragOverDefaultsToCopy [< 1 ms]
[xUnit.net 00:00:00.85]   Finished:    Microsoft.Maui.Controls.Core.UnitTests
  Passed DateTextPackageCorrectlySetsOnCompatibleTarget(fieldType: typeof(Microsoft.Maui.Controls.DatePicker), result: "12/12/2020 12:00:00 AM") [2 ms]
  Passed SendDropThrowsSynchronouslyForNullArgs [< 1 ms]
  Passed DropCommandFires [1 ms]
  Passed DataPackageOperationsAreIndependentFlags [1 ms]
  Passed PropertySetters [< 1 ms]
  Passed DragOverCommandFires [< 1 ms]
Test Run Successful.
Total tests: 24
     Passed: 24
 Total time: 1.0841 Seconds
🔴 Without fix — 📱 DragAndDropTests (SessionDidUpdateDefaultsToCopy, SessionDidUpdateRoutesAcceptedOperation, SessionDidUpdateWithoutDropRecognizerCancels, SessionDidUpdateSkipsDisabledDropRecognizer, SessionDidUpdateHonorsCustomProposalOnlyWhenAccepted, SessionDidUpdateCombinesRecognizerAcceptance, SessionDidUpdateDoesNotRetainPreviousAcceptance, SessionDidUpdateDoesNotRetainPreviousCustomProposal, SessionDidUpdateReevaluatesChangedRecognizers, SessionDidUpdatePreservesAcceptanceThroughPassiveRecognizer): 🛠️ BUILD ERROR · 40s

Error-relevant lines (filtered from the build log):

/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(41,64): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(102,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(103,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(104,64): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(139,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(140,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(142,63): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(143,63): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(144,63): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(145,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(226,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(268,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj::TargetFramework=net11.0-ios]
Build FAILED.
🟢 With fix — 📱 DragAndDropTests (SessionDidUpdateDefaultsToCopy, SessionDidUpdateRoutesAcceptedOperation, SessionDidUpdateWithoutDropRecognizerCancels, SessionDidUpdateSkipsDisabledDropRecognizer, SessionDidUpdateHonorsCustomProposalOnlyWhenAccepted, SessionDidUpdateCombinesRecognizerAcceptance, SessionDidUpdateDoesNotRetainPreviousAcceptance, SessionDidUpdateDoesNotRetainPreviousCustomProposal, SessionDidUpdateReevaluatesChangedRecognizers, SessionDidUpdatePreservesAcceptanceThroughPassiveRecognizer): ⚠️ ENV ERROR · 61s

No log file found

⚠️ Failure Details

  • 🛠️ DropGestureRecognizerTests without fix: build failed before tests could run
    • /Users/cloudtest/vss/_work/1/s/src/Controls/tests/Core.UnitTests/Gestures/DropGestureRecognizerTests.cs(39,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users/cl...
  • 🛠️ DragAndDropTests (SessionDidUpdateDefaultsToCopy, SessionDidUpdateRoutesAcceptedOperation, SessionDidUpdateWithoutDropRecognizerCancels, SessionDidUpdateSkipsDisabledDropRecognizer, SessionDidUpdateHonorsCustomProposalOnlyWhenAccepted, SessionDidUpdateCombinesRecognizerAcceptance, SessionDidUpdateDoesNotRetainPreviousAcceptance, SessionDidUpdateDoesNotRetainPreviousCustomProposal, SessionDidUpdateReevaluatesChangedRecognizers, SessionDidUpdatePreservesAcceptanceThroughPassiveRecognizer) without fix: build failed before tests could run
    • /Users/cloudtest/vss/_work/1/s/src/Controls/tests/DeviceTests/Elements/DragAndDrop/DragAndDropTests.iOS.cs(38,36): error CS0117: 'DataPackageOperation' does not contain a definition for 'Move' [/Users...
  • ⚠️ DragAndDropTests (SessionDidUpdateDefaultsToCopy, SessionDidUpdateRoutesAcceptedOperation, SessionDidUpdateWithoutDropRecognizerCancels, SessionDidUpdateSkipsDisabledDropRecognizer, SessionDidUpdateHonorsCustomProposalOnlyWhenAccepted, SessionDidUpdateCombinesRecognizerAcceptance, SessionDidUpdateDoesNotRetainPreviousAcceptance, SessionDidUpdateDoesNotRetainPreviousCustomProposal, SessionDidUpdateReevaluatesChangedRecognizers, SessionDidUpdatePreservesAcceptanceThroughPassiveRecognizer) with fix: XHarness did not produce the expected fresh result 'testResults.xml' for requested class(es) 'Microsoft.Maui.DeviceTests.DragAndDropTests' (the target tests did not run).
📁 Fix files reverted (13 files)
  • src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/Windows/WindowsDragAndDropCustomization.xaml
  • src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/Windows/WindowsDragAndDropCustomization.xaml.cs
  • src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSDragAndDropRequestFullSize.xaml.cs
  • src/Controls/src/Core/DragAndDrop/DataPackageOperation.cs
  • src/Controls/src/Core/DragAndDrop/DragEventArgs.cs
  • src/Controls/src/Core/Platform/iOS/DragAndDropDelegate.cs
  • src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt

📋 Pre-Flight — Context & Validation

Issue: #16472 - Add DataPackageOperation.Move option for Drag & Drop
PR: #39016 - [net11.0] Add opt-in Move support to drag and drop
Base / Head: net11.0 / squashed review commit 8501e991cb08e71d1df14761c14e66bd5059b53d
Platforms Affected: iOS and Mac Catalyst implementation; shared public API on all TFMs; Windows sample negotiation
Files Changed: 13 implementation/API/sample files, 2 test files

Problem and Existing PR Approach

The shared drag-and-drop API exposes only None and Copy, so a drop target cannot request a native move proposal. The PR adds the [Flags] value DataPackageOperation.Move = 2, documents application-owned source-data updates, and records the API for every target framework.

On Apple platforms, the PR changes HandleDragOver from a Boolean validity result to an aggregate DataPackageOperation. SessionDidUpdate preserves an explicit native proposal when any shared operation is accepted; otherwise it prefers Copy, proposes Move only for move-only acceptance when IUIDropSession.AllowsMoveOperation is true, and returns Cancel for unsupported/rejected operations. It deliberately preserves default Copy behavior and combines acceptance across enabled recognizers.

Key Findings

  • src/Controls/src/Core/Platform/iOS/DragAndDropDelegate.cs is the runtime path for both iOS and Mac Catalyst.
  • The PR reuses one DragEventArgs while visiting enabled DropGestureRecognizer instances and ORs each post-callback AcceptedOperation; passive recognizers therefore preserve prior acceptance.
  • Native Move is gated by session.AllowsMoveOperation; Copy | Move prefers Copy.
  • A custom PlatformDragEventArgs.DropProposal takes precedence only when aggregate shared acceptance is non-None.
  • Existing supported None and Copy semantics are intended to remain unchanged. Undefined numeric enum values may now cancel instead of falling back to Copy.
  • Public review feedback identified one low-severity sample mismatch: the iOS sample XAML still labels the scenario “UIDropProposal - Move” although its code now demonstrates shared AcceptedOperation.
  • The prior Gate is INCONCLUSIVE, not a product failure: the focused unit tests passed with the PR, while the iOS device runner did not emit a fresh testResults.xml. Gate verification must not be rerun in STEP 5a.
  • Regression cross-reference found same-file overlap with #34885 but no exact reverted lines.

Code Review Summary

Verdict: SKIPPED
Confidence: N/A
Errors: 0 | Warnings: 0 | Suggestions: 0

The dedicated expert pass is deferred to STEP 5b by the hard execution contract. No pre-flight expert-review agent was launched.

Test Contract for Try-Fix

Platform: ios

Primary regression test:

pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=Gesture"

This covers the added DragAndDropTests cases for default Copy, Move gating, combined recognizer acceptance, custom proposal precedence, disabled/missing recognizers, per-update reset, and recognizer replacement.

Mandatory focused regression test:

dotnet test src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj --filter "FullyQualifiedName~DropGestureRecognizerTests"

Do not run the Gate workflow or any full suite. Each candidate may run one implementation/test pass and at most one focused correction/retest.

Baseline Modification Allow-List

The prior Gate identified these 13 fix files. EstablishBrokenBaseline.ps1 is authoritative; after baseline creation, edit only paths listed in .github/.baseline-state.json under RevertedFiles.

  • src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/Windows/WindowsDragAndDropCustomization.xaml
  • src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/Windows/WindowsDragAndDropCustomization.xaml.cs
  • src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSDragAndDropRequestFullSize.xaml.cs
  • src/Controls/src/Core/DragAndDrop/DataPackageOperation.cs
  • src/Controls/src/Core/DragAndDrop/DragEventArgs.cs
  • src/Controls/src/Core/Platform/iOS/DragAndDropDelegate.cs
  • src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #39016 Aggregate shared accepted-operation flags in the Apple delegate, prefer Copy, and gate native Move on session permission ⚠️ Gate inconclusive 13 implementation/API/sample files Unit tests passed; iOS device execution was blocked by missing fresh XHarness results

🔬 Code Review — Deep Analysis

Expert PR Evaluation

Verdict: NEEDS_DISCUSSION
Confidence: low

The submitted fix coherently adds DataPackageOperation.Move and maps shared acceptance to Apple-native proposals. The implementation aggregates enabled DropGestureRecognizer acceptance, preserves an explicit native DropProposal, prefers Copy for Copy | Move, and proposes Move only when the drop session allows it. The added unit and iOS device-test coverage exercises the principal aggregation, proposal-precedence, state-reset, recognizer lifecycle, and Move-permission paths.

No actionable error or warning was found on an added or modified line, and no unresolved prior error-level review finding was identified. The raw inline findings artifact is therefore an empty JSON array.

The review cannot recommend approval yet because the trusted Gate is inconclusive and current required-check coverage could not be established as passing. This is an evidence limitation, not a test failure or a code defect. One non-blocking refinement remains available: the Apple sample still labels the scenario UIDropProposal - Move even though the updated sample demonstrates shared DragEventArgs.AcceptedOperation acceptance.


🛠️ Try-Fix — Analysis & Comparison

STEP 5a Try-Fix Aggregate

PR: #39016
Platform: iOS
Bound: At most two candidates, each with one implementation/test pass and at most one focused correction/retest.

Candidate 1 — Session-Aware Single Effective Acceptance

Model: gpt-5.3-codex
Result: ⚠️ BLOCKED
Candidate narrative: ../try-fix-1/content.md
Attempt artifacts: attempt-1/

Approach

Resolve one authoritative effective acceptance per update, honoring a custom native proposal first and otherwise mapping that acceptance through IUIDropSession.AllowsMoveOperation, rather than OR-aggregating flags across recognizers as the PR does.

Mechanism-Level Difference

The PR accumulates every recognizer's post-callback flags and derives the native proposal from that combined bitmask. Candidate 1 instead proposed preventing cross-recognizer accumulation from selecting the native operation by resolving a single effective acceptance for the update.

Diff

No candidate diff was produced.

Test Results

  • pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=Gesture" — not run
  • dotnet test src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj --filter "FullyQualifiedName~DropGestureRecognizerTests" — not run

Failure Analysis

EstablishBrokenBaseline.ps1 rejected eight pre-existing tracked modifications outside the PR fix allow-list, and .github/.baseline-state.json was not created. The skill therefore prohibited edits or tests. Inline self-review recorded zero findings against the empty candidate diff. The exact mandatory restore command ran and reported No baseline state found / Restored=False, which is valid for this no-state/no-edit blocked path.

Candidate 2

Model: gpt-5.6-sol
Result: ⚠️ BLOCKED
Candidate narrative: ../try-fix-2/content.md
Attempt artifacts: attempt-2/

Approach

No runtime approach was designed or applied. Candidate 2 followed the required no-edit path because the authoritative baseline could not be established.

Mechanism-Level Difference

Candidate 2 implemented neither the PR's aggregate-flags mechanism nor candidate 1's planned single-effective-acceptance mechanism. Its only executed mechanism was the try-fix skill's fail-closed baseline guard; therefore it provides no runtime alternative or behavioral evidence.

Diff

No candidate diff was produced.

Test Results

  • pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=Gesture" — not run
  • dotnet test src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj --filter "FullyQualifiedName~DropGestureRecognizerTests" — not run

Failure Analysis

EstablishBrokenBaseline.ps1 again rejected the same eight unrelated pre-existing tracked modifications and did not create .github/.baseline-state.json. Editing without the resulting RevertedFiles allow-list would violate the try-fix restoration contract, so the attempt stopped before target-file analysis, design, implementation, or testing. Inline self-review recorded zero findings against the empty candidate diff. The exact mandatory restore command ran and reported No baseline state found / Restored=False, valid for the verified no-state/no-edit blocked path.

Aggregate Outcome

Two child invocations were run sequentially in the required model order. Both are BLOCKED by the same environment state, not failed fixes. No product, API, sample, test, Gate, candidate-1, or pre-existing untracked files were modified by either attempt; both captured empty diffs and skipped tests because no safely restorable baseline existed.

The blocking tracked paths are:

  • .github/scripts/CopilotPipeline.Tests.ps1
  • .github/scripts/Review-PR.Tests.ps1
  • .github/scripts/RunTests.ps1
  • .github/scripts/Set-ScreenResolution.Tests.ps1 (deleted)
  • .github/scripts/shared/Detect-TestsInDiff.ps1
  • .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1
  • eng/scripts/Set-ScreenResolution.ps1
  • eng/scripts/update-cgmanifest.ps1

STEP 5b should consume these as honest partial results. They provide no evidence for or against the PR implementation and must not be treated as test failures or grounds to request changes.


🏁 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Winner

pr-plus-reviewer

The submitted runtime implementation is sound on code review, and pr-plus-reviewer preserves it unchanged while correcting the Apple sample's misleading UIDropProposal - Move label to AcceptedOperation - Move. It also has the strongest fresh platform evidence: all 41 filtered iOS gesture device tests passed. The focused unit-test command was blocked before test execution by unrelated existing RS0030 diagnostics, so that result is an environment/build limitation rather than a regression-test failure.

Because the winning candidate contains a change not present in the submitted PR, the required recommendation is REQUEST CHANGES.

Expert Review

  • Verdict: NEEDS_DISCUSSION
  • Confidence: low
  • Inline findings: none (inline-findings.json is [])
  • Code assessment: the shared Move flag, multi-recognizer acceptance aggregation, Copy preference, explicit native proposal precedence, per-update state handling, and AllowsMoveOperation gate form a coherent implementation.
  • Evidence limitation: the trusted Gate is inconclusive and required-check coverage was not established as passing. This is not a failed fix.
  • Non-blocking refinement: the Apple sample label described a native UIDropProposal even though the Move branch now uses shared DragEventArgs.AcceptedOperation.

Candidate Comparison

Rank Candidate Implementation Validation Assessment
1 pr-plus-reviewer Raw PR fix plus one sample-label correction; no runtime delta iOS Gesture device tests: 41/41 passed. Focused unit command: build-blocked before tests by unrelated existing RS0030 diagnostics Best combination of implementation quality, sample accuracy, and fresh affected-platform evidence
2 pr Adds DataPackageOperation.Move; aggregates accepted operations on Apple platforms; prefers Copy; gates Move on session permission; preserves explicit native proposals Gate inconclusive. Pre-flight records focused unit tests passing, but the prior iOS run lacked a fresh result artifact Runtime approach is sound and expert review found no blocking defect, but the sample label remains stale and platform evidence is weaker
3 try-fix-1 Planned a single authoritative effective acceptance instead of aggregating recognizer flags Blocked before edits or tests; no diff An unimplemented hypothesis with no regression evidence cannot outrank working candidates
4 try-fix-2 No runtime approach was designed or applied Blocked before edits or tests; no diff No implementation or behavioral evidence

No candidate failed a regression test. The pr-plus-reviewer unit command failed to build unrelated unchanged tests and therefore is classified as blocked, not as a candidate regression failure; both try-fix candidates were likewise environment-blocked without executing tests.

Comparative Rationale

The raw PR and pr-plus-reviewer share the same runtime mechanism, so the expert review's positive assessment applies equally to both. Aggregating flags is better supported by the submitted tests and preserves acceptance when passive or multiple enabled recognizers participate; the unimplemented try-fix-1 single-effective-acceptance idea has no code or test result demonstrating that it would preserve those semantics.

pr-plus-reviewer wins narrowly because its one-line label correction makes the sample accurately describe the API exercised by its Move switch, while its fresh iOS run verifies the unchanged runtime behavior. The remaining uncertainty is validation coverage, not a known product defect: the Gate remains inconclusive, Mac Catalyst was not runtime-tested, and the required focused unit command did not reach test execution in the candidate sandbox.

Required Change

Apply pr-plus-reviewer/reviewer.patch, which changes only:

src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSDragAndDropRequestFullSize.xaml

from UIDropProposal - Move to AcceptedOperation - Move.


🔗 Regression Cross-Reference

🔍 Regression Cross-Reference

⚠ Overlaps with prior bug-fix PRs — same files modified, but no exact line revert detected.

File Fix PR Fixed issue(s)
src/Controls/src/Core/DragAndDrop/DataPackageOperation.cs #34885 #5825, #8494, #8716, #10987, #11404, #12008, #18200, #18551, #18657, #18701, #19168, #19209, #20062, #20348, #20834, #20991, #21983, #22038, #22193, #22197, #22769, #23330, #23854, #23902, #23921, #24304, #24831, #25124, #26059, #26397, #26644, #26846, #26961, #26977, #27086, #27367, #27959, #28337, #28351, #28660, #28975, #29390, #29391, #29463, #29493, #29544, #30052, #30065, #30071, #30144, #30399, #30779, #30803, #30970, #31280, #31446, #31475, #31496, #31565, #31825, #31961, #32048, #32050, #32139, #32356, #32419, #32771, #32944, #32984, #32994, #33308, #33501, #33703, #33770, #33773, #34104, #34256, #34257, #34310, #34322, #34363, #34370, #34459, #34518, #34583, #34591, #34666, #34693, #34720, #34730
src/Controls/src/Core/Platform/iOS/DragAndDropDelegate.cs #34885 #5825, #8494, #8716, #10987, #11404, #12008, #18200, #18551, #18657, #18701, #19168, #19209, #20062, #20348, #20834, #20991, #21983, #22038, #22193, #22197, #22769, #23330, #23854, #23902, #23921, #24304, #24831, #25124, #26059, #26397, #26644, #26846, #26961, #26977, #27086, #27367, #27959, #28337, #28351, #28660, #28975, #29390, #29391, #29463, #29493, #29544, #30052, #30065, #30071, #30144, #30399, #30779, #30803, #30970, #31280, #31446, #31475, #31496, #31565, #31825, #31961, #32048, #32050, #32139, #32356, #32419, #32771, #32944, #32984, #32994, #33308, #33501, #33703, #33770, #33773, #34104, #34256, #34257, #34310, #34322, #34363, #34370, #34459, #34518, #34583, #34591, #34666, #34693, #34720, #34730

📱 UI Tests — Button,Label,Layout

Detected UI test categories: Button,Label,Layout

✅ Deep UI tests — 415 passed, 0 failed, 8 skipped across 3 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Button 105/106 (1 skipped) ✓ —
Label 113/115 (2 skipped) ✓ —
Layout 197/202 (5 skipped) ✓ —
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

🧭 Next Steps — reviewer changes required

The reviewer-enhanced candidate identified changes that are not yet in the submitted PR.

Why: The submitted runtime implementation is sound, and pr-plus-reviewer preserves it while correcting the Apple sample's stale Move label. It also has the strongest fresh platform evidence with 41 of 41 filtered iOS gesture device tests passing; the focused unit command was blocked before test execution by unrelated existing build diagnostics.

Address the actionable findings in this review before merging.

@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) and removed s/agent-review-in-progress AI review is currently running for this PR labels Sep 30, 2026
@kubaflo

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Tests Failure Analysis

@jfversluis — test-failure analysis for commit 5d52529.

Scope CI failures Commit 5d52529


🧪 CI Analysis — click to expand
📊 maui-pr

🧪 maui-pr-devicetests
  • 🟡 Needs human investigation — iOS CollectionView Does Not Leak With Default ItemsLayout retained CollectionViewHandler2; no comparable target result identifies whether this is a flake.
  • 🟡 Needs human investigation — MacCatalyst Core Tests was killed with 0 tests recorded after the job exceeded 60 minutes; the new .iOS.cs drag/drop tests also compile for MacCatalyst, so their completion must be checked.

🧪 maui-pr-uitests

🧭 Follow-up — actions and refresh

Next action: Re-run the iOS CollectionView and MacCatalyst Core device work items; confirm the new drag/drop cases complete before attributing either device failure.

Maintainers: comment /review tests to refresh this report.

@kubaflo
kubaflo merged commit b02bff0 into net11.0 Oct 1, 2026
175 of 191 checks passed
@kubaflo
kubaflo deleted the jfversluis-issue-16472-implementation branch October 1, 2026 10:53
@github-actions github-actions Bot added this to the .NET 11.0-rc2 milestone Oct 1, 2026

This branch was successfully deployed

1 active deployment
copilot-pat-pool — 5d52529d Deployed Sep 30, 2026 by jfversluis via conclusion #2148
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-gestures Gesture types platform/ios platform/macos macOS / Mac Catalyst s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants