Drag and Drop
The Drag and Drop functionality for the TreeView allows you to move a node or multitude of nodes between different parents in the same treeview or between different Telerik TreeView instances.
This article is divided in the following sections:
Basics
To enable the Drag and Drop functionality:
-
Set the
Draggable
parameter of the<TelerikTreeView>
totrue
-
Use the Drag events to handle the drag and drop operations and modify the data source as per your business logic.
OnDragStart Event
The OnDragStart
event fires when the user starts dragging a node. It provides details for the dragged item and allows you to cancel the event.
Event Arguments
The OnDragStart
event handler receives as an argument an object of type TreeViewDragStartEventArgs
that contains:
Parameter | Type | Description |
---|---|---|
Item | object | Represents the dragged row. You can cast this object to your model class. |
IsCancelled | bool | Whether the event is to be prevented. |
OnDrag Event
The OnDrag
event fires continuously while the user is dragging a node.
Event Arguments
The OnDrag
event handler receives as an argument an object of type TreeViewDragEventArgs
that contains:
Parameter | Type | Description |
---|---|---|
Item | object | Represents the dragged row. You can cast this object to your model class. |
DestinationItem | object | Represents the row over which the Item is. You can cast this object to your model class. |
DestinationTreeView | object | The reference of the TreeView in which the Item is dropped. |
DestinationIndex | string | The index in the target component where the drop will happen. |
DestinationComponentId | string | The Id of the target component in which the drop will happen. |
DropPosition | enum | Its members allow you to determine the exact position of the dropped item relative to the position of the DestinationItem . |
PageX | double | Represents the X coordinate of the mouse. |
PageY | double | Represents the Y coordinate of the mouse. |
OnDrop Event
The OnDrop
event fires when the user drops a node to a new location. It is triggered only if the new location is a Telerik component. The event allows you to manipulate your data collection based on where the user dropped the element.
Event Arguments
The OnDrop
event provides an object of type TreeViewDropEventArgs
to its event handler which exposes the following fields:
Parameter | Type | Description |
---|---|---|
Item | object | Represents the dragged row. You can cast this object to your model class. |
DestinationItem | object | Represents the row over which the Item is dropped. You can cast this object to your model class. |
Items | object | Represents the dragged row. You can cast this object to your model class. |
DropPosition | enum | Its members allow you to determine the exact position of the dropped item relative to the position of the DestinationItem . |
DestinationGrid | object | The reference of the Grid in which the row is dropped. This is applicable when you drag and drop rows between different grids. |
DestinationIndex | string | The index where the drop will happen in the second component. |
DestinationComponentId | string | The Id of the second component in which the drop will happen. |
OnDragEnd Event
The OnDragEnd
event fires when a drag operation ends. The event is triggered after OnDrop
and unlike it, OnDragEnd
will fire even if the drop location is not a Telerik component. In this case, the non-applicable event arguments will be null
.
Event Arguments
The OnDragEnd
event handler receives as an argument an object of type TreeViewDragEndEventArgs
that contains:
Parameter | Type | Description |
---|---|---|
DestinationItem | object | Represents the row over which the Item is. You can cast this object to your model class. |
DestinationTreeView | object | The reference of the TreeView in which the Item is dropped. |
DestinationIndex | string | The index in the target component where the drop will happen. |
DestinationComponentId | string | The Id of the target component in which the drop will happen. |
DropPosition | enum | Its members allow you to determine the exact position of the dropped item relative to the position of the DestinationItem . |
Examples
- TreeView Drag and Drop Events Example
- Drag and Drop between TreeView, Grid, TreeList and Scheduler
- Flat Data
- Hierarchical Data
- Between Different TreeViews
TreeView Drag and Drop Events
Handle Blazor TreeView Drag Events
<div>
Current Item: @CurrentItem
<br />
Current Location: @Location @DestinationItem
<br />
@Hint
</div>
<TelerikTreeView Data="@Data"
Id="TreeView"
@bind-ExpandedItems="@ExpandedItems"
Draggable="true"
OnDrop="@OnItemDrop"
OnDragStart="@OnDragStart"
DragThrottleInterval="150"
OnDrag="@OnDrag"
OnDragEnd="@OnDragEnd">
<TreeViewBindings>
<TreeViewBinding ParentIdField="Parent"></TreeViewBinding>
</TreeViewBindings>
</TelerikTreeView>
@code {
private string CurrentItem { get; set; }
private string DestinationItem { get; set; }
private string Location { get; set; }
private string Hint { get; set; } = "Documents and its children cannot be moved";
private List<TreeItem> Data { get; set; }
private IEnumerable<object> ExpandedItems { get; set; }
public class TreeItem
{
public int Id { get; set; }
public int? Parent { get; set; }
public string Text { get; set; }
public ISvgIcon Icon { get; set; }
public bool HasChildren { get; set; }
public TreeItem(int id, int? parent, string text, ISvgIcon icon, bool hasChildren)
{
Id = id;
Parent = parent;
Text = text;
Icon = icon;
HasChildren = hasChildren;
}
}
private void OnDragStart(TreeViewDragStartEventArgs args)
{
var item = args.Item as TreeItem;
if (item.Parent == 1 || item.Id == 1)
{
args.IsCancelled = true;
}
else
{
CurrentItem = item.Text;
}
}
private void OnDrag(TreeViewDragEventArgs args)
{
if (args.DestinationItem != null)
{
var destination = args.DestinationItem as TreeItem;
DestinationItem = destination.Text;
}
if (args.DropPosition != null)
{
Location = args.DropPosition.Value.ToString().ToLower();
}
else
{
Location = "over";
}
}
private void OnDragEnd(TreeViewDragEndEventArgs args)
{
var destination = args.DestinationItem as TreeItem;
if (args.DestinationComponentId == "TreeView" && args.DropPosition!=null)
{
Hint = "Item was placed successfully";
}
else
{
Hint = "Invalid location";
}
CurrentItem = "";
Location = "";
DestinationItem = "";
}
protected override void OnInitialized()
{
LoadData();
base.OnInitialized();
}
private void OnItemDrop(TreeViewDropEventArgs args)
{
var item = args.Item as TreeItem;
var destinationItem = args.DestinationItem as TreeItem;
if (destinationItem != null && IsChild(item, destinationItem))
{
return;
}
Data.Remove(item);
if (item.Parent != null && !Data.Any(x => item.Parent == x.Parent))
{
Data.FirstOrDefault(x => x.Id == item.Parent).HasChildren = false;
}
if (args.DropPosition == TreeViewDropPosition.Over)
{
item.Parent = destinationItem.Id;
destinationItem.HasChildren = true;
Data.Add(item);
}
else
{
var index = Data.IndexOf(destinationItem);
item.Parent = destinationItem.Parent;
if (args.DropPosition == TreeViewDropPosition.After)
{
index++;
}
Data.Insert(index, item);
}
// Refresh data
Data = new List<TreeItem>(Data);
}
private bool IsChild(TreeItem item, TreeItem destinationItem)
{
if (destinationItem?.Parent == null || item == null)
{
return false;
}
else if (destinationItem.Parent?.Equals(item.Id) == true)
{
return true;
}
var parentDestinationItem = Data.FirstOrDefault(e => e.Id.Equals(destinationItem.Parent));
return IsChild(item, parentDestinationItem);
}
private void LoadData()
{
Data = new List<TreeItem>()
{
new TreeItem(1, null, "Documents", SvgIcon.Folder, true),
new TreeItem(2, 1, "report.xlsx", SvgIcon.FileExcel, false),
new TreeItem(3, 1, "status.docx", SvgIcon.FileWord, false),
new TreeItem(4, 1, "conferences.xlsx", SvgIcon.FileExcel, false),
new TreeItem(5, 1, "performance.pdf", SvgIcon.FilePdf, false),
new TreeItem(6, null, "Pictures", SvgIcon.Folder, true),
new TreeItem(7, 6, "Camera Roll", SvgIcon.Folder, true),
new TreeItem(8, 7, "team.png", SvgIcon.FileImage, false),
new TreeItem(9, 7, "team-building.png", SvgIcon.FileImage, false),
new TreeItem(10, 7, "friends.png", SvgIcon.FileImage, false),
};
ExpandedItems = Data.ToList();
}
}
Drag and Drop between TreeView, Grid, TreeList and Scheduler
The functionality allows dragging items between TreeView, Grid, TreeList and Scheduler. To achieve it, set the Draggable
/RowDraggable
parameter, and implement it through an event - OnDrop
/OnRowDrop
.
Drag and Drop from Scheduler to Grid, TreeList, TreeView is not yet supported. Only the reversed way.
Drag and Drop between TreeView and Grid
@* Drag and drop in Grid and TreeView. *@
@using System.Collections.Generic;
@using System.Collections.ObjectModel;
@inject PersonService PersonService;
<TelerikGrid Data="@GridData"
Id="Grid1"
Width="450px"
RowDraggable="true"
@ref="@GridRef"
OnRowDrop="@((GridRowDropEventArgs<Person> args) => GridRowDrop(args))">
<GridSettings>
<GridRowDraggableSettings DragClueField="@nameof(Person.Name)"></GridRowDraggableSettings>
</GridSettings>
<GridColumns>
<GridColumn Field=@nameof(Person.EmployeeId) Editable="false" />
<GridColumn Field=@nameof(Person.Name) />
</GridColumns>
</TelerikGrid>
<TelerikTreeView @ref="@TreeRef"
Id="TreeView1"
Data="@TreeData"
OnDrop="@TreeViewDrop"
Draggable="true">
<TreeViewBindings>
<TreeViewBinding TextField="Text" ParentIdField="ParentId" />
</TreeViewBindings>
</TelerikTreeView>
@code {
private List<Person> GridData { get; set; }
private TelerikGrid<Person> GridRef { get; set; }
private TelerikTreeView TreeRef { get; set; }
private TreeViewObservableFlatDataService TreeService { get; set; }
private ObservableCollection<BaseFlatItem> TreeData { get; set; }
protected override async Task OnInitializedAsync()
{
await LoadData();
}
private async Task LoadData()
{
var people = await PersonService.GetPeopleAsync();
GridData = people.Take(10).ToList();
TreeService = new TreeViewObservableFlatDataService("Item");
TreeData = TreeService.GetFlatItems();
}
private void GridRowDrop(GridRowDropEventArgs<Person> args)
{
foreach (var item in args.Items)
{
GridData.Remove(item);
}
if (args.DestinationComponentId == "TreeView1")
{
var destinationItem = (BaseFlatItem)TreeRef.GetItemFromDropIndex(args.DestinationIndex);
args.Items
.Select(item => new BaseFlatItem() { Text = item.Name, Id = Guid.NewGuid() })
.ToList()
.ForEach(item => UpdateTreeView(item, destinationItem, args.DropPosition));
}
else if (args.DestinationComponentId == "Grid1")
{
InsertItemsIntoGrid(args.Items, args.DestinationItem, args.DropPosition);
}
}
private void TreeViewDrop(TreeViewDropEventArgs args)
{
var item = args.Item as BaseFlatItem;
if (args.DestinationComponentId == "TreeView1")
{
UpdateTreeView(item, args.DestinationItem as BaseFlatItem, (GridRowDropPosition)(int)args.DropPosition);
}
else if (args.DestinationComponentId == "Grid1")
{
var sourceItems = TreeService.GetChildItems(item)
.Append(item)
.Select(item => new Person() { Name = item.Text, EmployeeId = GridData.Max(x => x.EmployeeId) + 1 });
TreeService.Remove(item, true);
var destinationItem = GridRef.GetItemFromDropIndex(args.DestinationIndex);
InsertItemsIntoGrid(sourceItems, destinationItem, (GridRowDropPosition)(int)args.DropPosition);
GridRef.Rebind();
}
}
private void InsertItemsIntoGrid(IEnumerable<Person> items, Person destinationItem, GridRowDropPosition dropPosition)
{
var destinationIndex = 0;
if (destinationItem != null)
{
destinationIndex = GridData.IndexOf(destinationItem);
if (dropPosition == GridRowDropPosition.After)
{
destinationIndex += 1;
}
}
GridData.InsertRange(destinationIndex, items);
}
private void UpdateTreeView(BaseFlatItem item, BaseFlatItem destinationItem, GridRowDropPosition dropPosition)
{
if (dropPosition == GridRowDropPosition.Over)
{
TreeService.AddChild(item, destinationItem);
}
else if (dropPosition == GridRowDropPosition.After)
{
TreeService.AddNextSibling(item, destinationItem);
}
else if (dropPosition == GridRowDropPosition.Before)
{
TreeService.AddPrevSibling(item, destinationItem);
}
}
}
See more applicable examples in the Grid Drag and Drop article.
Flat Data
@inject TreeViewFlatDataService TreeViewFlatDataService
<TelerikTreeView Data="@FlatData"
Draggable="@Draggable"
OnDrop="@OnDrop">
<TreeViewBindings>
<TreeViewBinding TextField="Text" ParentIdField="ParentId" />
</TreeViewBindings>
</TelerikTreeView>
@code {
public bool Draggable { get; set; } = true;
public List<BaseFlatItem> FlatData { get; set; }
protected override void OnInitialized()
{
FlatData = TreeViewFlatDataService.GetFlatItems();
}
private void OnDrop(TreeViewDropEventArgs args)
{
var item = args.Item as BaseFlatItem;
var destinationItem = args.DestinationItem as BaseFlatItem;
FlatData = TreeViewFlatDataService.ReorderItems(args.DropPosition, item, destinationItem);
}
}
Hierarchical Data
@inject TreeViewHierarchicalDataService TreeViewHierarchicalDataService
<TelerikTreeView Data="@HierarchicalData"
Draggable="@Draggable"
OnDrop="@OnDrop">
<TreeViewBindings>
<TreeViewBinding ItemsField="Items" HasChildrenField="HasItems"></TreeViewBinding>
</TreeViewBindings>
</TelerikTreeView>
@code {
public bool Draggable { get; set; } = true;
public List<BaseHierarchicalItem> HierarchicalData { get; set; }
protected override void OnInitialized()
{
HierarchicalData = TreeViewHierarchicalDataService.GetHierarchicalItems();
}
private void OnDrop(TreeViewDropEventArgs args)
{
var item = args.Item as BaseHierarchicalItem;
var destinationItem = args.DestinationItem as BaseHierarchicalItem;
HierarchicalData = TreeViewHierarchicalDataService.ReorderHierarchicalItems(args.DropPosition, item, destinationItem);
}
}
Between Different TreeViews
When you drag and drop items from one instance of the TreeView to another, the OnDrop
event fires for the TreeView where the item originally was.
@using System.Collections.ObjectModel
<TelerikTreeView @ref="@FirstTree"
Data="@FirstFlatData"
Draggable="true"
OnDrop="@OnDropFirst">
<TreeViewBindings>
<TreeViewBinding TextField="Text" ParentIdField="ParentId" />
</TreeViewBindings>
</TelerikTreeView>
<TelerikTreeView @ref="@SecondTree"
Data="@SecondFlatData"
OnDrop="@OnDropSecond"
Draggable="true">
<TreeViewBindings>
<TreeViewBinding TextField="Text" ParentIdField="ParentId" />
</TreeViewBindings>
</TelerikTreeView>
@code {
public TelerikTreeView FirstTree { get; set; }
public TelerikTreeView SecondTree { get; set; }
public TreeViewObservableFlatDataService FirstTreeService { get; set; }
public TreeViewObservableFlatDataService SecondTreeService { get; set; }
public ObservableCollection<BaseFlatItem> FirstFlatData { get; set; }
public ObservableCollection<BaseFlatItem> SecondFlatData { get; set; }
protected override Task OnInitializedAsync()
{
FirstTreeService = new TreeViewObservableFlatDataService("Item");
SecondTreeService = new TreeViewObservableFlatDataService("Node");
FirstFlatData = FirstTreeService.GetFlatItems();
SecondFlatData = SecondTreeService.GetFlatItems();
return base.OnInitializedAsync();
}
private void OnDropFirst(TreeViewDropEventArgs args)
{
var item = args.Item as BaseFlatItem;
var destinationItem = args.DestinationItem as BaseFlatItem;
if (args.DestinationTreeView != FirstTree)
{
FirstTreeService.Remove(item);
UpdateSecondTree(item, destinationItem, args.DropPosition);
}
else
{
UpdateFirstTree(item, destinationItem, args.DropPosition);
}
}
private void OnDropSecond(TreeViewDropEventArgs args)
{
var item = args.Item as BaseFlatItem;
var destinationItem = args.DestinationItem as BaseFlatItem;
if (args.DestinationTreeView != SecondTree)
{
SecondTreeService.Remove(item);
UpdateFirstTree(item, destinationItem, args.DropPosition);
}
else
{
UpdateSecondTree(item, destinationItem, args.DropPosition);
}
}
private void UpdateFirstTree(BaseFlatItem item, BaseFlatItem destinationItem, TreeViewDropPosition dropPosition)
{
if (dropPosition == TreeViewDropPosition.Over)
{
SecondTreeService.AddChild(item, destinationItem);
}
else if (dropPosition == TreeViewDropPosition.After)
{
FirstTreeService.AddNextSibling(item, destinationItem);
}
else if (dropPosition == TreeViewDropPosition.Before)
{
FirstTreeService.AddPrevSibling(item, destinationItem);
}
}
private void UpdateSecondTree(BaseFlatItem item, BaseFlatItem destinationItem, TreeViewDropPosition dropPosition)
{
if (dropPosition == TreeViewDropPosition.Over)
{
SecondTreeService.AddChild(item, destinationItem);
}
else if (dropPosition == TreeViewDropPosition.After)
{
SecondTreeService.AddNextSibling(item, destinationItem);
}
else if (dropPosition == TreeViewDropPosition.Before)
{
SecondTreeService.AddPrevSibling(item, destinationItem);
}
}
}