DataTransfer
What a drag is actually carrying: the files, the text, the effect and the drag image - the payload Blazor's DragEventArgs leaves out of reach.
@inject Bit.Butil.DataTransfer dataTransferMDN reference
drop only fires on an element whose dragover handler called
preventDefault, and dropEffect has to be set on every dragover
because the browser resets it between events. Getting either wrong produces a drop target that
silently never fires - so OnDrop wires both up, and there is nothing else to add.
DataTransfer is in every engine. During prerender/SSR the check returns false rather than throwing, so defer it to OnAfterRenderAsync.
@inject Bit.Butil.DataTransfer dataTransfer
var supported = await dataTransfer.IsSupported();Drag files from your file manager onto the box below - or drag text, or a link from another tab. Items are read during the event and handed over whole, because getData outside the drop event answers with an empty string. Files are the other way round: their contents stay readable afterwards, so the handler does not have to do the reading.
private ElementReference _target;
_subscription = await dataTransfer.OnDrop(_target, payload => InvokeAsync(() =>
{
// payload.Files - each with Name, Size, Type, LastModified and an Id to read by
// payload.Text - "text/plain", when the drag carried some
// payload.Uri - "text/uri-list", when a link or an image was dragged in
StateHasChanged();
}), dropEffect: "copy");A dropped file is readable long after the event that delivered it - the file object is held for you, so there is no need to do the work inside the handler. CreateObjectUrl skips .NET entirely for the case where all you want is to show the picture. Release the files when you are done, or they stay in memory for the life of the page.
var bytes = await dataTransfer.ReadFile(file.Id); // upload this
var text = await dataTransfer.ReadFileText(file.Id); // for text files
// or show it without reading it at all:
var url = await dataTransfer.CreateObjectUrl(file.Id); // <img src="@url">
await dataTransfer.ReleaseFile(file.Id);The payload is settled up front rather than produced by a callback, because dragstart has to set its data synchronously and a round trip to .NET is not. A custom MIME type is how a drop target of your own recognises its own drags; text/plain is what other applications read. A drag image has to be rendered and visible when the drag starts - a display:none element produces no image at all, which is the usual reason a custom one does not appear.
_source = await dataTransfer.ConfigureDragSource(_chip,
new Dictionary<string, string>
{
["text/plain"] = "a chip from the demo",
["application/x-butil-demo"] = "{\"id\":42}"
},
effectAllowed: "copyMove",
dragImage: _dragImage);API reference
ValueTask<bool> IsSupported()ValueTask<ButilSubscription?> OnDrop(ElementReference target, Action<DropPayload> onDrop, string dropEffect = "copy")ValueTask<ButilSubscription?> ConfigureDragSource(ElementReference source, Dictionary<string, string> items, string effectAllowed = "all", ElementReference? dragImage = null, int dragImageX = 0, int dragImageY = 0)ValueTask<byte[]?> ReadFile(Guid fileId)ValueTask<string?> ReadFileText(Guid fileId)ValueTask<string?> CreateObjectUrl(Guid fileId)ValueTask ReleaseFile(Guid fileId)ValueTask DisposeAsync()record (DroppedFile[] Files, Dictionary<string, string> Items) { string? Text; string? Uri; }record (Guid Id, string Name, long Size, string Type, long LastModifiedMilliseconds) { DateTimeOffset LastModified; }