Blazor Drag and Drop

Updated on October 2, 2020 to use the latext Blazor syntax (.NET 5)

This blog post shows how to implement drag and drop in server-side Blazor applications. We will use the HTML 5 Drag and Drop API which is built-in in every modern browser.

Hey, we have announced Blazor support in Radzen! Check how you can create a complete Blazor CRUD application in minutes.

Sample project

Start from the default Blazor project template. Pick BlazorDragAndDrop as the project name.

DragAndDropService

This is a service which the draggable components will use to communicate with the drop targets.

Add a new file called DragAndDropService.cs in the Data directory:

using System;

namespace BlazorDragAndDrop.Data
{
    public class DragAndDropService
    {
        public object Data { get; set; }
        public string Zone { get; set; }

        public void StartDrag(object data, string zone)
        {
            this.Data = data;
            this.Zone = zone;
        }

        public bool Accepts(string zone)
        {
            return this.Zone == zone;
        }
    }
}

Then register the DragAndDropService in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton<WeatherForecastService>();
    services.AddSingleton<DragAndDropService>();
}

Draggable component

The next step is to create the component which the users will drag.

Add a new file Draggable.razor in the Shared directory:

@typeparam T
@using BlazorDragAndDrop.Data
@inject DragAndDropService DragAndDropService
<div draggable="true" @ondragstart="@OnDragStart">
    @ChildContent
</div>
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public string Zone { get; set; }

    [Parameter]
    public T Data { get; set;}

    void OnDragStart()
    {
        // Notify the DragAndDropService what the current Data and Zone are.
        DragAndDropService.StartDrag(Data, Zone);
    }
}

The Draggable component has three properties.

  1. ChildContent is the user defined draggable content. We simply render it via @ChildContent.
  2. Zone is restricts (optinally) where this draggable can be dropped. A draggable can be dropped in a drop target only if their zones have the same value.
  3. Data is the user specified data associated with the draggable. Can be anything.

DropTarget component

The DropTarget accepts Draggable components and triggers an event when the user drops one.

Add a new file DropTarget.cshtml in the Shared directory:

@using BlazorDragAndDrop.Data
@inject DragAndDropService DragAndDropService
@typeparam T
<div ondragover="event.preventDefault()" @ondrop="@OnDrop">
    @ChildContent
</div>
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public string Zone { get; set; }

    [Parameter]
    public Action<T> Drop { get; set; }

    void OnDrop()
    {
        if (Drop != null && DragAndDropService.Accepts(Zone))
        {
            Drop((T)DragAndDropService.Data);
        }
    }
}

The component has three properties.

  1. ChildContent is the user defined drop target content.
  2. Zone is specifies (optinally) what draggable components are accepted. A draggable can be dropped in a drop target only if their zones have the same value.
  3. Drop is the event handler that the DropTarget will trigger when the user drops an accepted draggable. The Data assocated with the dropped component is provided as argument.

Usage

Here is how to use the Draggable and DropTarget components in the Index.razor page:

@page "/"

<style>
    .draggable {
      border: 1px solid #ccc;
      border-radius: 5px;
      margin: 1rem;
      padding: 1rem;
      display: inline-block;
      cursor: move;
    }

    .drop-target {
      border: 1px dashed #ebebeb;
      margin: 1rem;
      padding: 1rem;
      display: inline-block;
    }
</style>

<Draggable Data="@draggableDataA">
    <div class="draggable">
        Draggable A
    </div>
</Draggable>

<Draggable Data="@draggableDataB">
    <div class="draggable">
        Draggable B
    </div>
</Draggable>

<DropTarget T="String" Drop="@OnDrop">
    <div class="drop-target">
        Accepts Draggable A or B
    </div>
</DropTarget>

@if (dropMessage != null) {
    @dropMessage
}

<DropTarget T="String" Zone="DropZone">
    <div class="drop-target">
        Can't drop here
    </div>
</DropTarget>

@code {
    string draggableDataA = "Draggable Data A";
    string draggableDataB = "Draggable Data B";

    string dropMessage = null;

    void OnDrop(string data)
    {
        dropMessage = $"Dropped: {data}";

        // Important: Invoke StateHasChanged() to update the page
        StateHasChanged();
    }
}

The complete sample project is available in the Radzen Github repository.

Cheers!

by Atanas Korchev

Designing custom forms with Radzen

Radzen has supported forms since its very first release. The faithful Form component also got some improvements such as multi-column support and popup lookups. Still in some cases it wasn’t enough as Radzen users needed full control over the form layout in ...
Read more