<div class="form">
<div class="drive-panel">
<div class="drive-header dx-treeview-item"><div class="dx-treeview-item-content"><i class="dx-icon dx-icon-activefolder"></i><span>Drive C:</span></div></div>
@(Html.DevExtreme().Sortable()
.Filter(".dx-treeview-item")
.Data("driveC")
.Group("shared")
.AllowReordering(true)
.AllowDropInsideItem(true)
.OnDragChange("onDragChange")
.OnDragEnd("onDragEnd")
.Content(
Html.DevExtreme().TreeView()
.ID("treeviewDriveC")
.ExpandNodesRecursive(false)
.DataStructure(TreeViewDataStructure.Tree)
.KeyExpr("Id")
.ItemsExpr("Items")
.ExpandedExpr("IsExpanded")
.DisplayExpr("Name")
.DataSource(d => d.Mvc().LoadAction("GetHierarchicalDataForDragAndDrop"))
.DataSourceOptions(o => o.Map("mapIcons"))
.Width(250)
.Height(380).ToString()
))
</div>
<div class="drive-panel">
<div class="drive-header dx-treeview-item"><div class="dx-treeview-item-content"><i class="dx-icon dx-icon-activefolder"></i><span>Drive D:</span></div></div>
@(Html.DevExtreme().Sortable()
.Filter(".dx-treeview-item")
.Data("driveD")
.Group("shared")
.AllowReordering(true)
.AllowDropInsideItem(true)
.OnDragChange("onDragChange")
.OnDragEnd("onDragEnd")
.Content(
Html.DevExtreme().TreeView()
.ID("treeviewDriveD")
.ExpandNodesRecursive(false)
.DataStructure(TreeViewDataStructure.Tree)
.KeyExpr("Id")
.ItemsExpr("Items")
.ExpandedExpr("IsExpanded")
.ItemTemplate(@<text>
<div>
<i class="dx-icon dx-icon-<%- IsDirectory ? 'activefolder' : 'file' %>"></i><span><%-Name%></span>
</div>
</text>)
.Width(250)
.Height(380).ToString()
)
)
</div>
</div>
<script>
function onDragChange(e) {
if(e.fromComponent === e.toComponent) {
var $nodes = e.element.find(".dx-treeview-node");
var isDragIntoChild = $nodes.eq(e.fromIndex).find($nodes.eq(e.toIndex)).length > 0;
if(isDragIntoChild) {
e.cancel = true;
}
}
}
function onDragEnd(e) {
if(e.fromComponent === e.toComponent && e.fromIndex === e.toIndex) {
return;
}
var fromTreeView = getTreeView(e.fromData);
var toTreeView = getTreeView(e.toData);
var fromNode = findNode(fromTreeView, e.fromIndex);
var toNode = findNode(toTreeView, calculateToIndex(e));
if(e.dropInsideItem && toNode !== null && !toNode.itemData.IsDirectory) {
return;
}
var fromTopVisibleNode = getTopVisibleNode(fromTreeView);
var toTopVisibleNode = getTopVisibleNode(toTreeView);
var fromItems = fromTreeView.option('items');
var toItems = toTreeView.option('items');
moveNode(fromNode, toNode, fromItems, toItems, e.dropInsideItem);
fromTreeView.option("items", fromItems);
toTreeView.option("items", toItems);
fromTreeView.scrollToItem(fromTopVisibleNode);
toTreeView.scrollToItem(toTopVisibleNode);
}
function getTreeView(driveName) {
return driveName === 'driveC'
? $('#treeviewDriveC').dxTreeView('instance')
: $('#treeviewDriveD').dxTreeView('instance');
}
function calculateToIndex(e) {
if(e.fromComponent != e.toComponent || e.dropInsideItem) {
return e.toIndex;
}
return e.fromIndex >= e.toIndex
? e.toIndex
: e.toIndex + 1;
}
function findNode(treeView, index) {
var nodeElement = treeView.element().find('.dx-treeview-node')[index];
if(nodeElement) {
return findNodeById(treeView.getNodes(), nodeElement.getAttribute('data-item-id'));
}
return null;
}
function findNodeById(nodes, id) {
for(var i = 0; i < nodes.length; i++) {
if(nodes[i].itemData.Id == id) {
return nodes[i];
}
if(nodes[i].children) {
var node = findNodeById(nodes[i].children, id);
if(node != null) {
return node;
}
}
}
return null;
}
function moveNode(fromNode, toNode, fromItems, toItems, isDropInsideItem) {
var fromNodeContainingArray = getNodeContainingArray(fromNode, fromItems);
var fromIndex = findIndex(fromNodeContainingArray, fromNode.itemData.Id);
fromNodeContainingArray.splice(fromIndex, 1);
if(isDropInsideItem) {
toNode.itemData.Items.splice(toNode.itemData.Items.length, 0, fromNode.itemData);
} else {
var toNodeContainingArray = getNodeContainingArray(toNode, toItems);
var toIndex = toNode === null
? toItems.length
: findIndex(toNodeContainingArray, toNode.itemData.Id);
toNodeContainingArray.splice(toIndex, 0, fromNode.itemData);
}
}
function getNodeContainingArray(node, rootArray) {
return node === null || node.parent === null
? rootArray
: node.parent.itemData.Items;
}
function findIndex(array, id) {
var idsArray = array.map(function(elem) { return elem.Id; });
return idsArray.indexOf(id);
}
function getTopVisibleNode(component) {
var treeViewElement = component.element().get(0);
var treeViewTopPosition = treeViewElement.getBoundingClientRect().top;
var nodes = treeViewElement.querySelectorAll(".dx-treeview-node");
for(var i = 0; i < nodes.length; i++) {
var nodeTopPosition = nodes[i].getBoundingClientRect().top;
if(nodeTopPosition >= treeViewTopPosition) {
return nodes[i];
}
}
return null;
}
function mapIcons(item) {
if (item.Icon) {
item.icon = item.Icon;
}
if (item.Items) {
for (var i = 0; i < item.Items.length; i++) {
mapIcons(item.Items[i]);
}
}
return item;
}
</script>
using DevExtreme.AspNet.Data;
using DevExtreme.AspNet.Mvc;
using DevExtreme.NETCore.Demos.Models;
using DevExtreme.NETCore.Demos.Models.SampleData;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace DevExtreme.NETCore.Demos.Controllers {
public class TreeViewController : Controller {
public ActionResult DragAndDropHierarchicalDataStructure() {
return View();
}
public object GetHierarchicalDataForDragAndDrop(DataSourceLoadOptions loadOptions) {
return Content(JsonConvert.SerializeObject(DataSourceLoader.Load(TreeViewHierarchicalDataForDragAndDrop.FileSystemItems, loadOptions)), "application/json");
}
}
}
using System.Collections.Generic;
namespace DevExtreme.NETCore.Demos.Models.TreeView {
public class FileSystemItem {
public string Id { get; set; }
public string ParentId { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
public bool IsDirectory { get; set; }
public bool IsExpanded { get; set; }
public IEnumerable<FileSystemItem> Items { get; set; }
}
}
using System.Collections.Generic;
using DevExtreme.NETCore.Demos.Models.TreeView;
namespace DevExtreme.NETCore.Demos.Models.SampleData {
public static class TreeViewHierarchicalDataForDragAndDrop {
public static readonly IEnumerable<FileSystemItem> FileSystemItems = new[] {
new FileSystemItem {
Id = "1",
Name = "Documents",
Icon = "activefolder",
IsDirectory = true,
IsExpanded = true,
Items = new[] {
new FileSystemItem {
Id = "2",
Name = "Projects",
Icon = "activefolder",
IsDirectory = true,
IsExpanded = true,
Items = new[] {
new FileSystemItem {
Id = "3",
Name = "About.rtf",
Icon = "file",
IsDirectory = false
},
new FileSystemItem {
Id = "4",
Name = "Passwords.rtf",
Icon = "file",
IsDirectory = false
}
}
},
new FileSystemItem {
Id = "5",
Name = "About.xml",
Icon = "file",
IsDirectory = false
},
new FileSystemItem {
Id = "6",
Name = "Managers.rtf",
Icon = "file",
IsDirectory = false
},
new FileSystemItem {
Id = "7",
Name = "ToDo.txt",
Icon = "file",
IsDirectory = false
}
},
},
new FileSystemItem {
Id = "8",
Name = "Images",
Icon = "activefolder",
IsDirectory = true,
IsExpanded = true,
Items = new[] {
new FileSystemItem {
Id = "9",
Name = "logo.png",
Icon = "file",
IsDirectory = false
},
new FileSystemItem {
Id = "10",
Name = "banner.gif",
Icon = "file",
IsDirectory = false
}
}
},
new FileSystemItem {
Id = "11",
Name = "System",
Icon = "activefolder",
IsDirectory = true,
IsExpanded = true,
Items = new[] {
new FileSystemItem {
Id = "12",
Name = "Employees.txt",
Icon = "file",
IsDirectory = false
},
new FileSystemItem {
Id = "13",
Name = "PasswordList.txt",
Icon = "file",
IsDirectory = false
}
}
},
new FileSystemItem {
Id = "14",
Name = "Description.rtf",
Icon = "file",
IsDirectory = false
},
new FileSystemItem {
Id = "15",
Name = "Description.txt",
Icon = "file",
IsDirectory = false
}
};
}
}
.form {
display: flex;
}
.form>div {
display: inline-block;
vertical-align: top;
}
.dx-treeview-item {
box-sizing: border-box;
}
.drive-header {
min-height: auto;
padding: 0px;
cursor: default;
margin-bottom: 10px;
}
.drive-panel {
padding: 20px 30px;
font-size: 115%;
font-weight: bold;
border-right: 1px solid rgba(165, 165, 165, 0.4);
height: 100%;
}
.drive-panel:last-of-type {
border-right: none;
}