Gantt Tree Inline Editing
Inline editing lets the user click an Edit command button on the row, and all its editable columns open up for changes. They can then click a Save
command button to submit the changes to the data access layer. This fires the OnUpdate
event where your code receives the updated model so you can work with the data (for example, to call the appropriate method of your service).
In a similar fashion, the Cancel
and Delete
command buttons fire events to let you handle the data source operations.
When validation is not satisfied, clicking the Save, Delete or Add buttons will not have effect, but you can still navigate between all fields in the row to complete editing.
You can also cancel the events by setting the IsCancelled
property of the event arguments to true
. This lets you prevent the user from editing certain records, inserting or deleting items, based on your application logic.
To enable Inline editing in the Gantt Tree, set its TreeListEditMode
property to GanttTreeListEditMode.Inline
, then handle the CRUD events as shown in the example below.
<TelerikGantt Data="@Data"
Width="1200px"
Height="600px"
IdField="Id"
ParentIdField="ParentId"
TreeListEditMode="@GanttTreeListEditMode.Inline"
OnUpdate="@UpdateItem"
OnDelete="@DeleteItem"
OnCreate="@CreateItem">
<GanttViews>
<GanttDayView></GanttDayView>
<GanttWeekView></GanttWeekView>
<GanttMonthView></GanttMonthView>
<GanttYearView></GanttYearView>
</GanttViews>
<GanttColumns>
<GanttCommandColumn Width="120px">
<GanttCommandButton Command="Add" Icon="@SvgIcon.Plus"></GanttCommandButton>
<GanttCommandButton Command="Edit" Icon="@SvgIcon.Pencil"></GanttCommandButton>
<GanttCommandButton Command="Save" Icon="@SvgIcon.Save" ShowInEdit="true"></GanttCommandButton>
<GanttCommandButton Command="Cancel" Icon="@SvgIcon.Cancel" ShowInEdit="true"></GanttCommandButton>
<GanttCommandButton Command="Delete" Icon="@SvgIcon.Trash"></GanttCommandButton>
</GanttCommandColumn>
<GanttColumn Field="Id"
Visible="false">
</GanttColumn>
<GanttColumn Field="Title"
Expandable="true"
Width="160px"
Title="Task Title" >
</GanttColumn>
<GanttColumn Field="PercentComplete"
Width="60px">
</GanttColumn>
<GanttColumn Field="Start"
Width="100px"
TextAlign="@ColumnTextAlign.Right">
</GanttColumn>
<GanttColumn Field="End"
DisplayFormat="End: {0:d}"
Width="100px">
</GanttColumn>
</GanttColumns>
</TelerikGantt>
@code {
public DateTime SelectedDate { get; set; } = new DateTime(2019, 11, 11, 6, 0, 0);
class FlatModel
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public double PercentComplete { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
public int LastId { get; set; } = 1;
List<FlatModel> Data { get; set; }
protected override void OnInitialized()
{
Data = new List<FlatModel>();
var random = new Random();
for (int i = 1; i < 6; i++)
{
var newItem = new FlatModel()
{
Id = LastId,
Title = "Employee " + i.ToString(),
Start = new DateTime(2020, 12, 6 + i),
End = new DateTime(2020, 12, 11 + i),
PercentComplete = Math.Round(random.NextDouble(), 2)
};
Data.Add(newItem);
var parentId = LastId;
LastId++;
for (int j = 0; j < 5; j++)
{
Data.Add(new FlatModel()
{
Id = LastId,
ParentId = parentId,
Title = " Employee " + i + " : " + j.ToString(),
Start = new DateTime(2020, 12, 6 + i + j),
End = new DateTime(2020, 12, 7 + i + j),
PercentComplete = Math.Round(random.NextDouble(), 2)
});
LastId++;
}
}
base.OnInitialized();
}
private async Task CreateItem(GanttCreateEventArgs args)
{
var argsItem = args.Item as FlatModel;
argsItem.Id = LastId++;
if (args.ParentItem != null)
{
var parent = (FlatModel)args.ParentItem;
argsItem.ParentId = parent.Id;
}
Data.Insert(0, argsItem);
CalculateParentPercentRecursive(argsItem);
CalculateParentRangeRecursive(argsItem);
}
private async Task UpdateItem(GanttUpdateEventArgs args)
{
var item = args.Item as FlatModel;
var foundItem = Data.FirstOrDefault(i => i.Id.Equals(item.Id));
if (foundItem != null)
{
var startOffset = item.Start - foundItem.Start;
if (startOffset != TimeSpan.Zero)
{
MoveChildrenRecursive(foundItem, startOffset);
}
foundItem.Title = item.Title;
foundItem.Start = item.Start;
foundItem.End = item.End;
foundItem.PercentComplete = item.PercentComplete;
}
CalculateParentPercentRecursive(foundItem);
CalculateParentRangeRecursive(foundItem);
}
private async Task DeleteItem(GanttDeleteEventArgs args)
{
var item = Data.FirstOrDefault(i => i.Id.Equals((args.Item as FlatModel).Id));
RemoveChildRecursive(item);
CalculateParentPercentRecursive(item);
CalculateParentRangeRecursive(item);
}
private void RemoveChildRecursive(FlatModel item)
{
var children = GetChildren(item).ToList();
foreach (var child in children)
{
RemoveChildRecursive(child);
}
Data.Remove(item);
}
private void CalculateParentPercentRecursive(FlatModel item)
{
if (item.ParentId != null)
{
var parent = GetParent(item);
var children = GetChildren(parent);
if (children.Any())
{
parent.PercentComplete = children.Average(i => i.PercentComplete);
CalculateParentPercentRecursive(parent);
}
}
}
private void CalculateParentRangeRecursive(FlatModel item)
{
if (item.ParentId != null)
{
var parent = GetParent(item);
var children = GetChildren(parent);
if (children.Any())
{
parent.Start = children.Min(i => i.Start);
parent.End = children.Max(i => i.End);
CalculateParentRangeRecursive(parent);
}
}
}
private void MoveChildrenRecursive(FlatModel item, TimeSpan offset)
{
var children = GetChildren(item);
foreach (var child in children)
{
child.Start = child.Start.Add(offset);
child.End = child.End.Add(offset);
MoveChildrenRecursive(child, offset);
}
}
private FlatModel GetParent(FlatModel item)
{
return Data.FirstOrDefault(i => i.Id.Equals(item.ParentId));
}
private IEnumerable<FlatModel> GetChildren(FlatModel item)
{
return Data.Where(i => item.Id.Equals(i.ParentId));
}
}
It is up to the data access logic to save the data once it is changed in the data collection, or to revert changes. The example above showcases the events that allow you to do that. In a real application, the code for handling data operations may be entirely different.