Blazorise Gantt: Editing
Enable built-in task editing or keep persistence and dialogs in your own page.
Set Editable to enable built-in create, add-child, edit, and delete flows. Use command switches and CommandAllowed for role-based or item-based command rules.
Set UseInternalEditing="false" when you want Gantt to keep its command affordances but let your page own the modal and persistence flow through NewItemClicked, AddChildItemClicked, EditItemClicked, and DeleteItemClicked.
Built-in Editing
EnableEditable to use built-in create, add-child, update, and delete workflows. This example also demonstrates command-level control through CommandAllowed.
<Gantt TItem="TaskItem" Data="" Date="" SelectedView="GanttView.Week" DurationField="Duration" Editable CommandAllowed=""> <GanttColumns> <GanttColumn Field="Title" Title="Task" Expandable Width="Width.Px(230)" /> <GanttColumn Field="Start" Width="Width.Px(130)" /> <GanttColumn Field="End" Width="Width.Px(130)" Visible="false" /> <GanttColumn Field="Duration" Width="Width.Px(90)" TextAlignment="TextAlignment.Center" Visible="false" /> <GanttColumn Field="Progress" Width="Width.Px(96)" TextAlignment="TextAlignment.Center" Visible="false" /> <GanttCommandColumn Width="Width.Px(60)" /> </GanttColumns> <GanttToolbar /> <GanttViews> <GanttWeekView TimelineCellWidth="90" RowHeight="44" /> </GanttViews> </Gantt>
@code { private DateOnly selectedDate = DateOnly.FromDateTime( DateTime.Today ); private List<TaskItem> tasks = CreateTasks(); private static List<TaskItem> CreateTasks() { var baseDate = DateTime.Today.AddDays( -2 ); var result = new List<TaskItem> { new() { Id = "1", Title = "Marketing campaign", Start = baseDate, End = baseDate.AddDays( 15 ), Progress = 45d }, new() { Id = "2", ParentId = "1", Title = "Research", Start = baseDate, End = baseDate.AddDays( 4 ), Progress = 100d }, new() { Id = "3", ParentId = "1", Title = "Creative", Start = baseDate.AddDays( 4 ), End = baseDate.AddDays( 9 ), Progress = 70d }, new() { Id = "4", ParentId = "1", Title = "Distribution", Start = baseDate.AddDays( 9 ), End = baseDate.AddDays( 15 ), Progress = 20d }, }; foreach ( var item in result ) { item.Duration = Math.Max( 1, (int)Math.Ceiling( ( item.End - item.Start ).TotalDays ) ); } return result; } private bool AllowCommand( GanttCommandContext<TaskItem> context ) { if ( context.CommandType == GanttCommandType.Delete && context.Item?.ParentId is null ) return false; return true; } public class TaskItem { public string Id { get; set; } public string ParentId { get; set; } public string Title { get; set; } public DateTime Start { get; set; } public DateTime End { get; set; } public int Duration { get; set; } public double Progress { get; set; } } }
External Editing
By setting theUseInternalEditing=false Gantt raises command callbacks with the relevant item and context, but leaves the editing UI and persistence flow to your implementation. This is ideal for keeping Gantt as a pure view component while owning the user experience and data flow of your app.
Last external action: None
<Gantt TItem="TaskItem" Data="" Date="" SelectedView="GanttView.Week" DurationField="Duration" Editable UseInternalEditing="false" NewItemClicked="" AddChildItemClicked="" EditItemClicked="" DeleteItemClicked=""> <GanttColumns> <GanttColumn Field="Title" Title="Task" Expandable Width="Width.Px( 230 )" /> <GanttColumn Field="Start" Width="Width.Px( 130 )" /> <GanttColumn Field="End" Width="Width.Px( 130 )" Visible="false" /> <GanttColumn Field="Duration" Width="Width.Px( 90 )" TextAlignment="TextAlignment.Center" Visible="false" /> <GanttCommandColumn Width="Width.Px( 176 )"> <HeaderTemplate> @if ( context.CanAddTask ) { <Button Size="Size.ExtraSmall" Color="Color.Success" Outline Clicked="@context.AddTask">New</Button> } </HeaderTemplate> <DisplayTemplate> @if ( context.CanAddChild ) { <Button Size="Size.ExtraSmall" Color="Color.Secondary" Outline Margin="Margin.Is1.FromEnd" Clicked="@context.AddChild">Child</Button> } @if ( context.CanEdit ) { <Button Size="Size.ExtraSmall" Color="Color.Primary" Outline Margin="Margin.Is1.FromEnd" Clicked="@context.Edit">Edit</Button> } @if ( context.CanDelete ) { <Button Size="Size.ExtraSmall" Color="Color.Danger" Outline Clicked="@context.Delete">Delete</Button> } </DisplayTemplate> </GanttCommandColumn> </GanttColumns> <GanttViews> <GanttWeekView TimelineCellWidth="90" RowHeight="44" /> </GanttViews> </Gantt> <Paragraph TextColor="TextColor.Muted" Margin="Margin.Is3.FromTop"> Last external action: @lastAction </Paragraph> <Modal @bind-Visible="" Centered> <ModalContent> <ModalHeader> <ModalTitle>@editorModeTitle</ModalTitle> <CloseButton Clicked="" /> </ModalHeader> <ModalBody> <Paragraph TextColor="TextColor.Muted"> This modal is owned by the page. Gantt only raises the command callbacks. </Paragraph> <Field> <FieldLabel>Title</FieldLabel> <FieldBody> <TextInput @bind-Value="" Immediate="false" Placeholder="Task title" /> </FieldBody> </Field> </ModalBody> <ModalFooter> <Button Color="Color.Secondary" Clicked="">Cancel</Button> <Button Color="Color.Primary" Clicked="">Save</Button> </ModalFooter> </ModalContent> </Modal>
@code { private DateOnly selectedDate = DateOnly.FromDateTime( DateTime.Today ); private List<TaskItem> tasks = CreateTasks(); private bool editorVisible; private string editorModeTitle = "Edit task"; private string editorTitle; private string editorParentId; private string editorTargetId; private TaskItem editorDraft; private string lastAction = "None"; private int nextId = 100; private Task OnNewItemClicked( GanttCommandContext<TaskItem> context ) => OpenEditor( context.Item, null, null, "Create task" ); private Task OnAddChildItemClicked( GanttCommandContext<TaskItem> context ) => OpenEditor( context.Item, context.ParentItem, null, $"Create child for {context.ParentItem?.Title}" ); private Task OnEditItemClicked( GanttItemClickedEventArgs<TaskItem> context ) => OpenEditor( context.Item, null, context.Item?.Id, $"Edit {context.Item?.Title}" ); private Task OnDeleteItemClicked( GanttItemClickedEventArgs<TaskItem> context ) { if ( context?.Item?.Id is null ) return Task.CompletedTask; RemoveTaskWithChildren( tasks, context.Item.Id ); lastAction = $"Deleted {context.Item.Title}"; return Task.CompletedTask; } private Task OpenEditor( TaskItem item, TaskItem parentItem, string targetId, string modeTitle ) { editorDraft = item?.Clone() ?? new TaskItem(); editorParentId = parentItem?.Id; editorTargetId = targetId; editorTitle = editorDraft.Title ?? string.Empty; editorModeTitle = modeTitle; editorVisible = true; return Task.CompletedTask; } private Task CloseEditor() { editorVisible = false; editorDraft = null; editorParentId = null; editorTargetId = null; editorTitle = string.Empty; return Task.CompletedTask; } private Task SaveEditor() { if ( editorDraft is null ) return Task.CompletedTask; editorDraft.Title = string.IsNullOrWhiteSpace( editorTitle ) ? "Untitled task" : editorTitle.Trim(); if ( string.IsNullOrEmpty( editorTargetId ) ) { editorDraft.Id ??= $"task-{nextId++}"; editorDraft.ParentId = editorParentId; tasks.Add( editorDraft.Clone() ); lastAction = editorParentId is null ? $"Created {editorDraft.Title}" : $"Created child {editorDraft.Title}"; } else { var existingTask = tasks.FirstOrDefault( x => x.Id == editorTargetId ); if ( existingTask is not null ) { existingTask.Title = editorDraft.Title; lastAction = $"Updated {existingTask.Title}"; } } return CloseEditor(); } private static void RemoveTaskWithChildren( List<TaskItem> list, string itemId ) { var idsToDelete = new HashSet<string>( StringComparer.Ordinal ) { itemId }; var changed = true; while ( changed ) { changed = false; foreach ( var task in list ) { if ( task.ParentId is not null && idsToDelete.Contains( task.ParentId ) && idsToDelete.Add( task.Id ) ) changed = true; } } list.RemoveAll( x => idsToDelete.Contains( x.Id ) ); } private static List<TaskItem> CreateTasks() { var baseDate = DateTime.Today.AddDays( -2 ); return new List<TaskItem> { new() { Id = "1", Title = "Website redesign", Start = baseDate, End = baseDate.AddDays( 14 ), Duration = 14 }, new() { Id = "2", ParentId = "1", Title = "Discovery", Start = baseDate, End = baseDate.AddDays( 4 ), Duration = 4 }, new() { Id = "3", ParentId = "1", Title = "Implementation", Start = baseDate.AddDays( 4 ), End = baseDate.AddDays( 12 ), Duration = 8 }, new() { Id = "4", ParentId = "1", Title = "Launch", Start = baseDate.AddDays( 12 ), End = baseDate.AddDays( 14 ), Duration = 2 }, }; } public class TaskItem { public string Id { get; set; } public string ParentId { get; set; } public string Title { get; set; } public DateTime Start { get; set; } public DateTime End { get; set; } public int Duration { get; set; } public TaskItem Clone() { return new TaskItem { Id = Id, ParentId = ParentId, Title = Title, Start = Start, End = End, Duration = Duration, }; } } }
API
See the documentation below for a complete reference to all of the props and classes available to the components mentioned here.