diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.xaml b/samples/ControlCatalog/Pages/DragAndDropPage.xaml
index 7982ddc1d0..9b4290ad04 100644
--- a/samples/ControlCatalog/Pages/DragAndDropPage.xaml
+++ b/samples/ControlCatalog/Pages/DragAndDropPage.xaml
@@ -43,14 +43,14 @@
MaxWidth="260"
Background="{DynamicResource SystemAccentColorDark1}"
DragDrop.AllowDrop="True">
- Drop some text or files here (Copy)
+ Drop some text, files, bitmap or custom format here (Copy)
- Drop some text or files here (Move)
+ Drop some text or custom format here (Move)
diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs b/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
index fe2b306477..99e319d819 100644
--- a/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
@@ -51,6 +51,9 @@ namespace ControlCatalog.Pages
"Bitmap",
d => d.Add(DataTransferItem.Create(DataFormat.Bitmap, new Bitmap(AssetLoader.Open(new Uri("avares://ControlCatalog/Assets/image1.jpg"))))),
DragDropEffects.Copy);
+
+ AddHandlers(CopyTarget, DragDropEffects.Copy);
+ AddHandlers(MoveTarget, DragDropEffects.Move);
}
private void SetupDnd(string suffix, Action factory, DragDropEffects effects) =>
@@ -94,16 +97,18 @@ namespace ControlCatalog.Pages
}
}
+ dragMe.PointerPressed += DoDrag;
+ }
+
+ private void AddHandlers(Control target, DragDropEffects allowedEffects)
+ {
+ DragDrop.AddDragEnterHandler(target, DragOver);
+ DragDrop.AddDragOverHandler(target, DragOver);
+ DragDrop.AddDropHandler(target, Drop);
+
void DragOver(object? sender, DragEventArgs e)
{
- if (e.Source is Control c && c.Name == "MoveTarget")
- {
- e.DragEffects = e.DragEffects & (DragDropEffects.Move);
- }
- else
- {
- e.DragEffects = e.DragEffects & (DragDropEffects.Copy);
- }
+ e.DragEffects &= allowedEffects;
// Only allow if the dragged data contains text or filenames.
if (!e.DataTransfer.Contains(DataFormat.Text)
@@ -115,14 +120,10 @@ namespace ControlCatalog.Pages
async void Drop(object? sender, DragEventArgs e)
{
- if (e.Source is Control c && c.Name == "MoveTarget")
- {
- e.DragEffects = e.DragEffects & (DragDropEffects.Move);
- }
- else
- {
- e.DragEffects = e.DragEffects & (DragDropEffects.Copy);
- }
+ e.DragEffects &= allowedEffects;
+
+ if (e.DragEffects == DragDropEffects.None)
+ return;
if (e.DataTransfer.Contains(DataFormat.Text))
{
@@ -165,11 +166,6 @@ namespace ControlCatalog.Pages
DropState.Content = "Custom: " + e.DataTransfer.TryGetValue(_customFormat);
}
}
-
- dragMe.PointerPressed += DoDrag;
-
- AddHandler(DragDrop.DropEvent, Drop);
- AddHandler(DragDrop.DragOverEvent, DragOver);
}
}
}
diff --git a/src/Avalonia.Base/Input/AsyncDataTransferExtensions.cs b/src/Avalonia.Base/Input/AsyncDataTransferExtensions.cs
index cf9d320310..a71a1e744d 100644
--- a/src/Avalonia.Base/Input/AsyncDataTransferExtensions.cs
+++ b/src/Avalonia.Base/Input/AsyncDataTransferExtensions.cs
@@ -27,6 +27,9 @@ public static class AsyncDataTransferExtensions
return new AsyncToSyncDataTransfer(asyncDataTransfer);
}
+ internal static IAsyncDataTransfer ToAsynchronous(this IDataTransfer dataTransfer)
+ => dataTransfer as IAsyncDataTransfer ?? new SyncToAsyncDataTransfer(dataTransfer);
+
///
/// Gets whether a supports a specific format.
///
diff --git a/src/Avalonia.Base/Input/SyncToAsyncDataTransfer.cs b/src/Avalonia.Base/Input/SyncToAsyncDataTransfer.cs
new file mode 100644
index 0000000000..c228e8b8db
--- /dev/null
+++ b/src/Avalonia.Base/Input/SyncToAsyncDataTransfer.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+
+namespace Avalonia.Input;
+
+///
+/// Wraps a into a .
+///
+/// The sync object to wrap.
+internal sealed class SyncToAsyncDataTransfer(IDataTransfer dataTransfer)
+ : IDataTransfer, IAsyncDataTransfer
+{
+ private SyncToAsyncDataTransferItem[]? _items;
+
+ public IReadOnlyList Formats
+ => dataTransfer.Formats;
+
+ public IReadOnlyList Items
+ => _items ??= ProvideItems();
+
+ IReadOnlyList IDataTransfer.Items
+ => dataTransfer.Items;
+
+ IReadOnlyList IAsyncDataTransfer.Items
+ => Items;
+
+ private SyncToAsyncDataTransferItem[] ProvideItems()
+ {
+ var asyncItems = dataTransfer.Items;
+ var count = asyncItems.Count;
+ var syncItems = new SyncToAsyncDataTransferItem[count];
+
+ for (var i = 0; i < count; ++i)
+ {
+ var asyncItem = asyncItems[i];
+ syncItems[i] = new SyncToAsyncDataTransferItem(asyncItem);
+ }
+
+ return syncItems;
+ }
+
+ public void Dispose()
+ => dataTransfer.Dispose();
+}
diff --git a/src/Avalonia.Base/Input/SyncToAsyncDataTransferItem.cs b/src/Avalonia.Base/Input/SyncToAsyncDataTransferItem.cs
new file mode 100644
index 0000000000..705ce0bdee
--- /dev/null
+++ b/src/Avalonia.Base/Input/SyncToAsyncDataTransferItem.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Avalonia.Input;
+
+///
+/// Wraps a into a .
+///
+/// The sync item to wrap.
+internal sealed class SyncToAsyncDataTransferItem(IDataTransferItem dataTransferItem)
+ : IDataTransferItem, IAsyncDataTransferItem
+{
+ public IReadOnlyList Formats
+ => dataTransferItem.Formats;
+
+ public object? TryGetRaw(DataFormat format)
+ => dataTransferItem.TryGetRaw(format);
+
+ public Task