
文章目录
0. 前言
随着UI界面的不断发展,不论是桌面UI的拖放复制文件,还是网页UI如DevOps的拖放Task,拖拽(Drag & Drop)操作几乎可以说是每一个UI平台的必备功能,而且拖放也是HTML5标准的组成部分。那么我们今天就来了解一下在微软的.NET Blazor 框架下要如何实现这一功能呢?
1. HTML标准的中的Drag&Drop
在其他框架下开发过前端组件的同学们一定都很熟悉在拖放的过程中有如下一些事件会被触发:
- ondrag:当一个可拖放元素被拖拽时触发
- ondragstart:当用户开始拖拽元素时触发
- ondragend:当用户停止拖拽(放开鼠标或按下ESC)元素时触发
- ondragenter:当元素被拖入有效释放区域时触发
- ondragover:当元素被拖过有效释放区域时触发
- ondragleave:当元素被拖离有效释放区域时触发
- ondrop:当元素被释放在有效区域内时触发
HTML还规定了相关对象和方法
属性:
- dropEffect:获取当前选择的拖放操作类型,或设置新操作类型,值必须为none, copy, link或move
- effectAllowed:提供所有可能操作类型,值为none, copy, copyLink, copyMove, link, linkMove, move, all 或者uninitialized
- files:包含数据转移中所有可用本地文件集合。如果拖放操作与文件无关,值为空集
- item:提供DataTransferItemList对象,包含所有拖放数据的集合
- types:提供string数组,包含dragstart事件中设置的所有格式
方法:
- DataTransfer.clearData():清除指定类型的数据。类型参数时可选的,如果类型参数为空或未设置,所有类型的数据都会被清除。如果指定类型的数据不存在,或不包含任何数据,该方法则不起任何作用。
- DataTransfer.getData():获取指定类型的数据。如果指定类型的数据不存在,或不包含任何数据,则返回空字符串。
- DataTransfer.setData():设置指定类型的数据。如果指定类型的数据不存在,则添加在末尾,因此类型列表的最后一项会是新加入的格式。如果指定类型已包含数据,则新数据会在该位置覆盖旧数据。
- DataTransfer.setDragImage():设置拖拽时使用的自定义图标。
2. Blazor中的Drag&Drop
类似的,Blazor为我们提供了如下API
/// <summary> /// Supplies information about an drag event that is being raised. /// </summary> public class DragEventArgs : MouseEventArgs { /// <summary> /// The data that underlies a drag-and-drop operation, known as the drag data store. /// See <see cref="DataTransfer"/>. /// </summary> public DataTransfer DataTransfer { get; set; } }
/// <summary> /// The <see cref="DataTransfer"/> object is used to hold the data that is being dragged during a drag and drop operation. /// It may hold one or more <see cref="DataTransferItem"/>, each of one or more data types. /// For more information about drag and drop, see HTML Drag and Drop API. /// </summary> public class DataTransfer { /// <summary> /// Gets the type of drag-and-drop operation currently selected or sets the operation to a new type. /// The value must be none, copy, link or move. /// </summary> public string DropEffect { get; set; } /// <summary> /// Provides all of the types of operations that are possible. /// Must be one of none, copy, copyLink, copyMove, link, linkMove, move, all or uninitialized. /// </summary> public string EffectAllowed { get; set; } /// <summary> /// Contains a list of all the local files available on the data transfer. /// If the drag operation doesn't involve dragging files, this property is an empty list. /// </summary> public string[] Files { get; set; } /// <summary> /// Gives a <see cref="DataTransferItem"/> array which is a list of all of the drag data. /// </summary> public UIDataTransferItem[] Items { get; set; } /// <summary> /// An array of <see cref="string"/> giving the formats that were set in the dragstart event. /// </summary> public string[] Types { get; set; }
这里我拿Ant Design Blazor的Tabs组件来举个例子。在开发Tabs组件时,遇到了拖动tabs标题的需求,第一次在网页前端处理Drag&Drop属实有点懵,这要怎么实现,监控mouse相关事件?再一看原版AntDesign的demo,可释放时还要改变鼠标图标?拖拽时元素的虚化怎么实现?(小小的脑袋里装满了大大的问号.gif)。但是查到了上一节提到的HTML标准Drag事件后,结合Blazor为我们提供的DragEventArgs,一个事件处理函数就这么形成了。
尝试1
<!--AntTabs.razor--> @foreach (var pane in Panes) { <div @ondragstart="(e)=>HandleDragStart(e, pane)" @ondrop="(e)=>HandleDrop(e, pane)"> }
// AntTabs.razor.cs // 标题组件集合 internal List<AntTabPane> Panes = new List<AntTabPane>(); // 拖动中的tabs标题 private AntTabPane _draggingPane; //ondragstart事件处理方法 private void HandleDragStart(DragEventArgs args, AntTabPane pane) { // 设置拖动效果为"move" args.DataTransfer.DropEffect = "move"; args.DataTransfer.EffectAllowed = "move"; // 设置拖动标题 _draggingPane = pane; } // ondrop事件处理方法 private void HandleDrop(DragEventArgs args, AntTabPane pane) { // 获取新index int newIndex = Panes.IndexOf(pane); // 移除拖动中的标题 Panes.Remove(_draggingPane); // 将拖动中的标题插入至新位置 Panes.Insert(newIndex, _draggingPane); // 重设拖动标题为空 _draggingPane = null; //...省略若干代码 // 刷新界面 StateHasChanged(); }
效果如下:

这里有两个问题:
- 必须要选中标题的内容才能拖动
- 即使拖至有效区域释放,标题顺序并没有改变
- 打断点发现HandleDragStart和HandleDrop并没有被触发
遇事不决,StackOverflow!原来还要给div加上draggable=”true”
尝试2
<!--AntTabs.razor--> @foreach (var pane in Panes) { <div draggable="true" @ondragstart="(e)=>HandleDragStart(e, pane)" @ondrop="(e)=>HandleDrop(e, pane)"> }
效果如下:

好消息:
- 不需要选中标题就能拖动了
- HandleDragStart触发成功
问题:
1.HandleDrop依然没有触发
好吧,继续打开StackOverflow!
尝试3
<!--AntTabs.razor--> @foreach (var pane in Panes) { <div draggable="true" ondragover="event.preventDefault();" @ondragstart="(e)=>HandleDragStart(e, pane)" @ondrop="(e)=>HandleDrop(e, pane)"> }
event.preventDefault()的作用是取消事件的默认动作,当用户在界面上执行拖拽操作时会优先触发ondragover事件,此时ondragover中的默认行为会阻止ondrop事件的触发,因为这里我们需要取消释放目标元素ondragover事件的默认行为
好消息:这次终于实现了Drag&Drop
最后的效果在这里
https://ant-design-blazor.gitee.io/en-US/tabs

3. 总结
Blazor API中的DragEventArgs.DataTranfer.Items,我在HandleDragStart中赋值为draggingPane后,在HandleDrop方法中仍然为null,取不到值,也许时我姿势不对?请各位江湖大佬赐教。
虽然一直从事.NET开发,但其实我之前从来没有任何网页前端开发经验,可见Blazor对于.NET开发人员来说入门是十分容易的,即使是拖放操作这些看上去不是那么简单的UI交互,都能够轻松驾驭。WebAssembly将是前端界的新潮流,大家也可以一起来为Ant-Design-Blazor项目添砖加瓦!
参考文章:
https://docs.microsoft.com/en-us/aspnet/core/blazor/event-handling
https://blazor-university.com/components/component-events/browser-dom-events/
https://chrissainty.com/investigating-drag-and-drop-with-blazor/
github库地址:https://github.com/ant-design-blazor/ant-design-blazor
原文出处:知乎
本文观点不代表Dotnet9立场,转载请联系原作者。