VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 网站开发 > ASPnet >
  • 在Blazor中实现拖放(drag and drop)

前言

我在实现一个含有待办列表功能的页面时,发现了一个好看的设计,它将待办分为——“待办”,“正在进行”,和“已完成”三种状态,并且将待办通过拖拽的方式在这三种状态之间进行切换。

image-20211104150546076

这种方式看起来真不错,但是使用Blazor来实现这种拖拽效果看起来似乎并不太容易。

经过一番百度难以找到,最后搜索英文通过Google找到了合适的解决方案。

感谢Chris Sainty的博客,通过学习他的代码,最终实现了自己想要的效果。

解决方案

需要实现的目标:

  • 跟踪用户正在拖拽的项目

  • 控制该项目在哪里可以掉落

  • 反馈给用户哪些列表可以掉落,哪些不能

  • 鼠标松开时更新项目

数据结构:

public class TodoItemDto
{
    public int Id { get; set; }
    public string Title { get; set; }

    public string Description { get; set; }

    public TodoStatusEnum Status { get; set; }

    /// <summary>
    /// 截止日期
    /// </summary>
    public DateTime ClosingDate { get; set; }

    /// <summary>
    /// 标签
    /// </summary>
    public string Tag { get; set; }

    public bool IsImportant { get; set; }

    public int RepeatTimes { get; set; }
}

public enum TodoStatusEnum
{
    Todo,                     // 待办
    InProgress,				  // 正在进行
    Completed                 // 已完成
}

Components

使用三个组件来实现该效果:

  • TodoContainer.razor

    • 使用TodoContainer包含Todo,InProgress,Completed三个待办列表
    • 跟踪容器内被用户拖拽的项目
    • 包含一个事件供状态更新时调用

    TodoContainer.razor

    <div class="todo-view-content-layout">
        @* 将自己作为级联参数传递给子组件(TodoList) *@
        <CascadingValue Value="this">
            @ChildContent
        </CascadingValue>
    </div>
    

    TodoContainer.razor.cs

    [Parameter] public List<TodoItemDto> Todos { get; set; }
    [Parameter] public RenderFragment ChildContent { get; set; }
    
    public TodoItemDto Payload { get; set; }  // 这个属性用来跟踪被用户拖拽的待办项
    
    /// <summary>
    /// 当子组件更新状态时,调用父组件的该方法,改变待办集合中被拖拽项的状态
    /// </summary>
    /// <param name="newStatus"></param>
    public async Task UpdateJobAsync(TodoStatusEnum newStatus)
    {
        var task = Todos.SingleOrDefault(x => x.Id == Payload.Id);
    
        if (task != null)
        {
            task.Status = newStatus;
            task.ClosingDate = DateTime.Now;
            await InvokeAsync(StateHasChanged);
        }
    }
    
  • TodoList.razor

    • 使用TodoList包含 每个状态 的待办列表(一共三个TodoList)
    • 当用户拖拽待办经过列表时呈现不同的UI效果

    TodoList.razor

    <div class="todo-view-content-item-layout @_dropClass">
        <div class="todo-view-content-item">
            @* 展示第一行,标题和任务数等(代码省略) *@
            ...
            @* 展示第二行,添加栏(代码省略)*@
    		...
            @* 第三行展示任务列表 *@
            <div class="todo-view-content-list"
                // 调用html5关于拖拽的原生API,默认(default)阻止其它容器拖拽项目,(preventDefault)则允许容器进行拖拽放置
                 ondragover="event.preventDefault();" 
                // 启用FireFox浏览器对拖放的支持
                 ondragstart="event.dataTransfer.setData('', event.target.id);"
                // 当用户进行放置操作时调用HandleDrop方法
                 @ondrop="HandleDrop"
                // 当用户进入该list范围时调用HandleDragEnter来判断能不能放置
                 @ondragenter="HandleDragEnter"
                // 当用户离开list范围后将dropClass置为默认状态
                 @ondragleave="HandleDragLeave">
    
                @foreach (var todo in Todos)
                {
                    <TodoDisplay TodoItem="todo"/>
                }
            </div>
        </div>
    </div>
    

    TodoList.razor.cs

    [CascadingParameter] TodoContainer Container { get; set; }
    [Parameter] public TodoStatusEnum ListStatus { get; set; }
    [Parameter] public TodoStatusEnum AllowedStatuses { get; set; }
    
    List<TodoItemDto> Todos = new List<TodoItemDto>();
    private string _dropClass = "";  // 用来判断当前list能否放置
    
    protected override void OnParametersSet()
    {
        Todos.Clear();
        Todos.AddRange(Container.Todos.Where(x => x.Status == ListStatus));
    }
    
    private void HandleDragEnter()
    {
        if (ListStatus == Container.Payload.Status) return;
    
        if (AllowedStatuses != Container.Payload.Status)
        {
            _dropClass = "can-drop";
        }
    }
    
    private void HandleDragLeave()
    {
        _dropClass = "";
    }
    
    private async Task HandleDrop()
    {
        _dropClass = "";
    
        if (AllowedStatuses == Container.Payload.Status) return;
    
        await Container.UpdateJobAsync(ListStatus);
    }
    
  • TodoDisplay.razor

    • 这个组件用来展示单个待办
    • 当它被拖拽的时候就通知父组件(TodoContainer)跟踪它

    该组件代码逻辑很简单,但是html的代码量过多就不展示了

TodoDisplay.razor

<div class="draggable" draggable="true" title="@TodoItem.Title" @ondragstart="@(() => HandleDragStart(TodoItem))">
	// 内容(待办展示)...
</div>

TodoDisplay.razor.cs

[CascadingParameter] TodoContainer Container { get; set; }
[Parameter] public TodoItemDto TodoItem { get; set; }

/// <summary>
/// 通过每次drag开始将Container的Payload赋值以跟踪被拖拽的待办项
/// </summary>
/// <param name="selectedJob"></param>
private void HandleDragStart(TodoItemDto selectedJob)
{
    Container.Payload = selectedJob;
}

使用

从TodoView.razor中进行调用

@page "/todo"
@attribute [Authorize]

<div class="todo-layout">
    <TodoViewTitle></TodoViewTitle>
    <TodoContainer Todos="Todos">
        <TodoList ListStatus="TodoStatusEnum.Todo" AllowedStatuses="TodoStatusEnum.Todo"/>
        <TodoList ListStatus="TodoStatusEnum.InProgress" AllowedStatuses="TodoStatusEnum.InProgress"/>
        <TodoList ListStatus="TodoStatusEnum.Completed" AllowedStatuses="TodoStatusEnum.Completed"/>
    </TodoContainer>
</div>

@code {

    List<TodoItemDto> Todos = new List<TodoItemDto>
    {
        new TodoItemDto()
        {
            Id = 1,
            Title = "开发大创项目右侧状态栏",
            Description = "无",
            IsImportant = true,
            RepeatTimes = 1,
            Tag = "工作",
            ClosingDate = DateTime.Now
        },
        new TodoItemDto()
        {
            Id = 12,
            Title = "每日背单词",
            Description = "无",
            IsImportant = true,
            RepeatTimes = 1,
            Tag = "学习",
            ClosingDate = DateTime.Now
        },
        new TodoItemDto()
        {
            Id = 11,
            Title = "AAAAAAAAAA",
            Description = "无",
            IsImportant = true,
            RepeatTimes = 1,
            Tag = "工作",
            ClosingDate = DateTime.Now
        },
        new TodoItemDto()
        {
            Id = 111,
            Title = "BBBBBBBBBBBB",
            Description = "无",
            IsImportant = true,
            RepeatTimes = 1,
            Tag = "工作",
            ClosingDate = DateTime.Now
        },
        new TodoItemDto()
        {
            Id = 2,
            Title = "CCCCCCCCCCCCCCCC",
            Description = "无",
            IsImportant = true,
            RepeatTimes = 1,
            Tag = "工作",
            ClosingDate = DateTime.Now
        }
    };
}

实现效果如下:

image-20211104210632441

进行拖拽...

image-20211104210703150

拖拽成功!

image-20211104210724878

 

原文:https://www.cnblogs.com/Aatrowen-Blog/p/15510566.html


相关教程