Your search did not match any results.

List - Item Drag & Drop

This demo shows how to enable item drag and drop in the List component. You can reorder items or drag and drop them between two separate lists. Use the handles on the right side of items to initiate drag and drop.

Backend API
<div class="widget-container"> <div> @Html.Partial("_ItemDragging", "PlannedTasks") </div> <div> @Html.Partial("_ItemDragging", "DoingTasks") </div> </div> <script> function onDragStart(e) { var items = e.component.option("items"); e.itemData = items[e.fromIndex]; } function onAdd(e) { var store = e.component.getDataSource().store(), values = e.itemData; values.sort = e.toIndex; store.insert(values).then(() => { var changes = [{ type: "insert", data: values }]; store.push(changes); }); } function onRemove(e) { var store = e.component.getDataSource().store(), key = e.itemData.ID; store.remove(key).then(() => { var changes = [{ type: "remove", key: key }]; store.push(changes); }); } function onReorder(e) { var store = e.component.getDataSource().store(), key = e.itemData.ID, data = { Sort: e.toIndex }; store.update(key, data).then(() => { var changes = [{ type: "update", key: key, data: data }]; store.push(changes); }); } </script>
@model string @(Html.DevExtreme().List() .ID(@Model) .Height(380) .DataSource(d => d.WebApi() .RouteName("ListItemDraggingEditing") .Key("ID") .LoadAction(string.Format("Get{0}", @Model)) .InsertAction(string.Format("Insert{0}", @Model)) .UpdateAction(string.Format("Update{0}", @Model)) .DeleteAction(string.Format("Delete{0}", @Model)) ) .DataSourceOptions(dso => dso.Paginate(false)) .DataSourceOptions(o => o.ReshapeOnPush(true).Sort("Sort")) .DisplayExpr("Text") .ItemDragging(id => id .AllowReordering(true) .Group("tasks") .OnDragStart("onDragStart") .OnAdd("onAdd") .OnRemove("onRemove") .OnReorder("onReorder") ) )
using DevExtreme.MVC.Demos.Models.SampleData; using System; using System.Collections.Generic; using System.Linq; using System.Web.Mvc; namespace DevExtreme.MVC.Demos.Controllers { public class ListController : Controller { public ActionResult ItemDragging() { return View(); } } }
using DevExtreme.AspNet.Data; using DevExtreme.AspNet.Mvc; using Newtonsoft.Json; using System; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Web.Http; using DevExtreme.MVC.Demos.Models; namespace DevExtreme.MVC.Demos.Controllers.ApiControllers { [Route("api/ListItemDraggingEditing/{action}", Name = "ListItemDraggingEditing")] public class ListItemDraggingEditingController : ApiController { InMemoryListPlannedDataContext _plannedTasksData = new InMemoryListPlannedDataContext(); InMemoryListDoingDataContext _doingTasksData = new InMemoryListDoingDataContext(); [HttpGet] public HttpResponseMessage GetPlannedTasks(DataSourceLoadOptions loadOptions) { return Request.CreateResponse(DataSourceLoader.Load(_plannedTasksData.ListItems, loadOptions)); } [HttpGet] public HttpResponseMessage GetDoingTasks(DataSourceLoadOptions loadOptions) { return Request.CreateResponse(DataSourceLoader.Load(_doingTasksData.ListItems, loadOptions)); } [HttpPost] public HttpResponseMessage InsertPlannedTasks(FormDataCollection form) { var values = form.Get("values"); var listItem = new ListTaskItem(); JsonConvert.PopulateObject(values, listItem); Validate(listItem); if(!ModelState.IsValid) return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState.GetFullErrorMessage()); return InsertTask(listItem, _plannedTasksData); } [HttpPost] public HttpResponseMessage InsertDoingTasks(FormDataCollection form) { var values = form.Get("values"); var listItem = new ListTaskItem(); JsonConvert.PopulateObject(values, listItem); Validate(listItem); if(!ModelState.IsValid) return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState.GetFullErrorMessage()); return InsertTask(listItem, _doingTasksData); } HttpResponseMessage InsertTask(ListTaskItem listItem, InMemoryListTasksDataContext<ListTaskItem> tasksData) { var sortedTasks = tasksData.ListItems.OrderBy(t => t.Sort); for(var i = listItem.Sort; i < tasksData.ListItems.Count; i++) { sortedTasks.ElementAt(i).Sort++; } tasksData.ListItems.Add(listItem); tasksData.SaveChanges(); return Request.CreateResponse(HttpStatusCode.Created, listItem); } [HttpPut] public HttpResponseMessage UpdatePlannedTasks(FormDataCollection form) { var key = Convert.ToInt32(form.Get("key")); var values = form.Get("values"); var listItem = _plannedTasksData.ListItems.First(a => a.ID == key); var oldOrderIndex = listItem.Sort; JsonConvert.PopulateObject(values, listItem); Validate(listItem); if(!ModelState.IsValid) return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState.GetFullErrorMessage()); return UpdateTask(listItem, oldOrderIndex, _plannedTasksData); } [HttpPut] public HttpResponseMessage UpdateDoingTasks(FormDataCollection form) { var key = Convert.ToInt32(form.Get("key")); var values = form.Get("values"); var listItem = _doingTasksData.ListItems.First(a => a.ID == key); var oldOrderIndex = listItem.Sort; JsonConvert.PopulateObject(values, listItem); Validate(listItem); if(!ModelState.IsValid) return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState.GetFullErrorMessage()); return UpdateTask(listItem, oldOrderIndex, _doingTasksData); } HttpResponseMessage UpdateTask(ListTaskItem listItem, int oldOrderIndex, InMemoryListTasksDataContext<ListTaskItem> tasksData) { tasksData.ListItems.Remove(listItem); var sortedTasks = tasksData.ListItems.OrderBy(t => t.Sort); AdjustSort(listItem, oldOrderIndex, sortedTasks); tasksData.ListItems.Add(listItem); tasksData.SaveChanges(); return Request.CreateResponse(HttpStatusCode.OK, listItem); } void AdjustSort(ListTaskItem listItem, int oldOrderIndex, IOrderedEnumerable<ListTaskItem> sortedTasks) { if(oldOrderIndex != listItem.Sort) { if(oldOrderIndex < listItem.Sort) { for(var i = oldOrderIndex; i < listItem.Sort; i++) { if(sortedTasks.ElementAt(i).Sort > 0) { sortedTasks.ElementAt(i).Sort--; } } } else if(oldOrderIndex > listItem.Sort) { for(var i = listItem.Sort; i < oldOrderIndex; i++) { if(sortedTasks.ElementAt(i).Sort < sortedTasks.Count()) { sortedTasks.ElementAt(i).Sort++; } } } } } [HttpDelete] public void DeletePlannedTasks(FormDataCollection form) { var key = Convert.ToInt32(form.Get("key")); DeleteTask(key, _plannedTasksData); } [HttpDelete] public void DeleteDoingTasks(FormDataCollection form) { var key = Convert.ToInt32(form.Get("key")); DeleteTask(key, _doingTasksData); } private void DeleteTask(int key, InMemoryListTasksDataContext<ListTaskItem> tasksData) { var listItem = tasksData.ListItems.First(a => a.ID == key); tasksData.ListItems.Remove(listItem); var sortedTasks = tasksData.ListItems.OrderBy(t => t.Sort); for(var i = listItem.Sort; i < tasksData.ListItems.Count; i++) { sortedTasks.ElementAt(i).Sort--; } tasksData.SaveChanges(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace DevExtreme.MVC.Demos.Models { public class InMemoryListDoingDataContext : InMemoryListTasksDataContext<ListTaskItem> { public override ICollection<ListTaskItem> ListItems => ItemsInternal; protected override IEnumerable<ListTaskItem> Source => SampleData.SampleData.ListDoingTasks.ToList<ListTaskItem>(); protected override int GetKey(ListTaskItem item) => item.ID; protected override void SetKey(ListTaskItem item, int key) => item.ID = key; } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace DevExtreme.MVC.Demos.Models { public class InMemoryListPlannedDataContext : InMemoryListTasksDataContext<ListTaskItem> { public override ICollection<ListTaskItem> ListItems => ItemsInternal; protected override IEnumerable<ListTaskItem> Source => SampleData.SampleData.ListPlannedTasks.ToList<ListTaskItem>(); protected override int GetKey(ListTaskItem item) => item.ID; protected override void SetKey(ListTaskItem item, int key) => item.ID = key; } }
using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Web; namespace DevExtreme.MVC.Demos.Models { public abstract class InMemoryListTasksDataContext<T> { protected ICollection<T> ItemsInternal { get { var session = HttpContext.Current.Session; var key = GetType().FullName; if(session[key] == null) session[key] = DeepClone(Source); return (ICollection<T>)session[key]; } } public abstract ICollection<T> ListItems { get; } protected abstract IEnumerable<T> Source { get; } public void SaveChanges() { foreach(var item in ItemsInternal.Where(i => GetKey(i) == 0)) SetKey(item, ItemsInternal.Max(GetKey) + 1); } protected abstract int GetKey(T item); protected abstract void SetKey(T item, int key); static ICollection<T> DeepClone(IEnumerable<T> source) { return JsonSerializer.Deserialize<List<T>>(JsonSerializer.Serialize(source)); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace DevExtreme.MVC.Demos.Models { public class ListTaskItem { public int ID { get; set; } public string Text { get; set; } public int Sort { get; set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace DevExtreme.MVC.Demos.Models.SampleData { public partial class SampleData { public static IEnumerable<ListTaskItem> ListDoingTasks = new[] { new ListTaskItem { ID = 1, Text = "Prepare 2019 Financial", Sort = 0 }, new ListTaskItem { ID = 2, Text = "Prepare 2019 Marketing Plan", Sort = 1 }, new ListTaskItem { ID = 3, Text = "Update Personnel Files", Sort = 2 }, new ListTaskItem { ID = 4, Text = "Review Health Insurance Options Under the Affordable Care Act", Sort = 3 } }; public static IEnumerable<ListTaskItem> ListPlannedTasks = new[] { new ListTaskItem { ID = 5, Text = "New Brochures", Sort = 0 }, new ListTaskItem { ID = 6, Text = "2019 Brochure Designs", Sort = 1 }, new ListTaskItem { ID = 7, Text = "Brochure Design Review", Sort = 2 }, new ListTaskItem { ID = 8, Text = "Website Re-Design Plan", Sort = 3 }, new ListTaskItem { ID = 9, Text = "Rollout of New Website and Marketing Brochures", Sort = 4 }, new ListTaskItem { ID = 10, Text = "Create 2018 Sales Report", Sort = 5 }, new ListTaskItem { ID = 11, Text = "Direct vs Online Sales Comparison Report", Sort = 6 }, new ListTaskItem { ID = 12, Text = "Review 2018 Sales Report and Approve 2019 Plans", Sort = 7 }, new ListTaskItem { ID = 13, Text = "Submit Signed NDA", Sort = 8 }, new ListTaskItem { ID = 14, Text = "Update Revenue Projections", Sort = 9 }, new ListTaskItem { ID = 15, Text = "Review Revenue Projections", Sort = 10 }, new ListTaskItem { ID = 16, Text = "Comment on Revenue Projections", Sort = 11 }, new ListTaskItem { ID = 17, Text = "Scan Health Insurance Forms", Sort = 12 }, new ListTaskItem { ID = 18, Text = "Sign Health Insurance Forms", Sort = 13 }, new ListTaskItem { ID = 19, Text = "Follow up with West Coast Stores", Sort = 14 }, new ListTaskItem { ID = 20, Text = "Follow up with East Coast Stores", Sort = 15 }, new ListTaskItem { ID = 21, Text = "Submit Refund Report for 2019 Recall", Sort = 16 }, new ListTaskItem { ID = 22, Text = "Give Final Approval for Refunds", Sort = 17 }, new ListTaskItem { ID = 23, Text = "Prepare Product Recall Report", Sort = 18 }, new ListTaskItem { ID = 24, Text = "Review Product Recall Report by Engineering Team", Sort = 19 }, new ListTaskItem { ID = 25, Text = "Review Training Course for any Omissions", Sort = 20 }, new ListTaskItem { ID = 26, Text = "Review Overtime Report", Sort = 21 }, new ListTaskItem { ID = 27, Text = "Submit Overtime Request Forms", Sort = 22 }, new ListTaskItem { ID = 28, Text = "Overtime Approval Guidelines", Sort = 23 }, new ListTaskItem { ID = 29, Text = "Create Report on Customer Feedback", Sort = 24 }, new ListTaskItem { ID = 30, Text = "Review Customer Feedback Report", Sort = 25 } }; } }
.widget-container { display: flex; } .widget-container > * { height: 400px; width: 50%; padding: 10px; } .dx-scrollview-content { min-height: 380px; }

The following steps describe how to configure this functionality:

  1. Enable item drag and drop
    Set the itemDragging.allowReordering to true.

  2. Add the lists to the same group
    Set the group property of both components to the same value to allow a user to drag and drop items between them.

  3. Reorder list items in code
    Use the onDragStart event handler to store the dragged item's data. When a user drops the item, the onRemove and onAdd functions allow you to remove the item from its initial position and add it to the new location.