TL;DR: Want to build a responsive Employee Onboard Tracker UI using .NET MAUI DataGrid? This guide walks you through using DataGridTemplateColumn to render progress bars, bind checklist data with ObservableCollection, and display nested onboarding tasks, all using MVVM and real-time UI updates. Perfect for developers building onboarding dashboards or task trackers with .NET MAUI DataGrid
Building a responsive and interactive Employee Onboarding Tracker UI is a common challenge for developers working on HR or workflow applications.
In this blog post, we’ll explore how to use .NET MAUI DataGrid to create a dynamic onboarding dashboard. You’ll learn how to bind data using MVVM, visualize progress with a custom progress bar, and display nested checklist items, all within a structured DataGrid.
We begin by creating the Employee model class, which includes properties like name, email, joining date, progress percentage, pending tasks, and a checklist. To support real-time UI updates, the class implements INotifyPropertyChanged. This ensures that any property change is immediately reflected in the UI.
public class Employee : INotifyPropertyChanged
{
private bool isSelected;
private string name;
private string email;
private DateTime dateOfJoining;
private int pendingTasks;
private int onboardingProgressPercentage;
private List checkedlists;
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
OnPropertyChanged(nameof(IsSelected));
}
}
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
public string Email
{
get { return email; }
set
{
email = value;
OnPropertyChanged(nameof(Email));
}
}
public DateTime DateOfJoining
{
get { return dateOfJoining; }
set
{
dateOfJoining = value;
OnPropertyChanged(nameof(DateOfJoining));
}
}
public int OnboardingProgressPercentage
{
get { return onboardingProgressPercentage; }
set
{
onboardingProgressPercentage = value;
OnPropertyChanged(nameof(OnboardingProgressPercentage));
}
}
public int PendingTasks
{
get { return pendingTasks; }
set
{
pendingTasks = value;
OnPropertyChanged(nameof(PendingTasks));
}
}
public List Checklists
{
get { return checkedlists; }
set
{
checkedlists = value;
OnPropertyChanged(nameof(Checklists));
}
}
public Employee()
{
}
} We shall populate the onboarding tracker with sample employee data. This is handled within the EmployeeOnboardingViewModel, which manages both the data and its interaction with the UI.
The Employees property is an ObservableCollection<Employee>, bound to the DataGrid’s ItemsSource. This enables dynamic rendering and ensures that updates to the collection are reflected in real time.
public partial class EmployeeOnboardingViewModel : INotifyPropertyChanged
{
private ObservableCollection<Employee> employees;
public ObservableCollection<Employee> Employees
{
get
{
return employees;
}
set
{
employees = value;
OnPropertyChanged(nameof(Employees));
}
}
} private void GenerateEmployees()
{
// Creating checklist items with random completion status for demo purposes
var tyrionChecklist = CreateChecklistItems(3); // 3 items completed
var sansaChecklist = CreateChecklistItems(4); // 4 items completed
// More checklists...
Employees.Add(new Employee
{
Name = "Tyrion Lannister",
Email = "tyrion@xyz.com",
DateOfJoining = new DateTime(2021, 11, 18),
OnboardingProgressPercentage = 33,
PendingTasks = 6,
Checklists = tyrionChecklist
});
// More employees...
} To maintain a clean and purposeful UI, we define a Columns collection that explicitly controls which fields are displayed in the DataGrid. This approach avoids auto-generating columns, allowing us to selectively present only the most relevant onboarding data.
Each column is carefully tailored to represent specific properties such as selection status, onboarding progress, and checklist items. By doing so, we ensure clarity, consistency, and a user interface that aligns tightly with the onboarding workflow.
public partial class EmployeeOnboardingViewModel : INotifyPropertyChanged
{
// Columns and Data manipulation property definition
private ColumnCollection columns = new ColumnCollection();
public ColumnCollection Columns
{
get
{
return columns;
}
set
{
columns = value;
OnPropertyChanged(nameof(Columns));
}
}
private void InitializeColumns()
{
// Columns added here
}
Instead of auto-generating columns, we explicitly define each one in the ViewModel using:
Each column uses a MappingName to bind to a specific property and a HeaderText for display. The columns below are added to a collection in the ViewModel and bound to the DataGrid control.
This column allows users to select rows individually or via a master checkbox in the header.
// CheckBox Column
var checkBoxColumn = new DataGridCheckBoxColumn()
{
MappingName = "IsSelected",
HeaderText = "",
Width = 50,
HeaderTemplate = new DataTemplate(() =>
{
var headerCheckBox = new Syncfusion.Maui.Buttons.SfCheckBox()
{
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center
};
headerCheckBox.SetBinding(Syncfusion.Maui.Buttons.SfCheckBox.IsCheckedProperty, new Binding() { Path = "IsChecked", Mode = BindingMode.TwoWay, Source = this });
return headerCheckBox;
})
};
Displays a visual progress bar alongside a percentage label, bound to the OnboardingProgressPercentage property.
var progressColumn = new DataGridTemplateColumn()
{
MappingName = "OnboardingProgressPercentage",
HeaderText = "Onboarding Progress",
Width = 180,
CellTemplate = new DataTemplate(() =>
{
// Grid with progress bar and label
})
}; Displays a nested CollectionView within each cell inside the DataGridTemplateColumn to show all checklist items for the employee. Completed tasks are visually marked with a green check icon.
var checklistsColumn = new DataGridTemplateColumn()
{
MappingName = "Checklists",
HeaderText = "Checklists",
Width = 300,
CellTemplate = new DataTemplate(() =>
{
var collectionView = new CollectionView();
collectionView.SetBinding(CollectionView.ItemsSourceProperty, "Checklists");
collectionView.ItemTemplate = new DataTemplate(() =>
{
var grid = new Grid
{
ColumnDefinitions =
{
new ColumnDefinition { Width = GridLength.Star },
new ColumnDefinition { Width = GridLength.Auto }
},
Margin = new Thickness(2)
};
var taskLabel = new Label
{
Margin = new Thickness(5, 2),
HorizontalOptions = LayoutOptions.Start,
};
taskLabel.SetBinding(Label.TextProperty, "Task");
//s taskLabel.SetBinding(Label.BackgroundColorProperty, new Binding("IsCompleted", converter: new BoolToColorConverter()));
var checkLabel = new Label
{
Text = "✓",
TextColor = Colors.Green,
FontAttributes = FontAttributes.Bold,
Margin = new Thickness(5, 0, 5, 0)
};
checkLabel.SetBinding(Label.IsVisibleProperty, "IsCompleted");
grid.Add(taskLabel, 1, 0);
grid.Add(checkLabel, 0, 0);
return grid;
});
return collectionView;
})
}; Bind the UI to the EmployeeOnboardingViewModel using BindingContext. Then, the columns from the view model are bound to the SfDataGrid through the Columns property, enabling dynamic rendering.
<ContentPage.BindingContext>
<local:EmployeeOnboardingViewModel />
</ContentPage.BindingContext>
<syncfusion:SfDataGrid x:Name="dataGrid"
ItemsSource="{Binding Employees}"
Columns="{Binding Columns}"
SelectionMode="Multiple"
AutoGenerateColumnsMode="None"
</syncfusion:SfDataGrid> With the UI now bound to the view model, the next step is to enable interactive features such as column visibility toggling, sorting, grouping, and filtering. These are handled through commands defined in the view model. Let’s define the following commands in the ViewModel:
public partial class EmployeeOnboardingViewModel : INotifyPropertyChanged
{
public ICommand HideColumns { get; set; } = null!;
public ICommand HideAllColumns { get; set; } = null!;
public ICommand ShowAllColumns { get; set; } = null!;
// define other commands
private void InitializeCommands()
{
HideColumns = new Command(ExecuteHideColumns);
ShowAllColumns = new Command(ExecuteShowAllColumns);
HideAllColumns = new Command(ExecuteHideAllColumns);
//initialize other commands
}
} Each command is initialized and bound to UI actions, allowing users to interactively manipulate the DataGrid.
Column visibility is managed through commands like HideColumns, ShowAllColumns, and HideAllColumns, which are bound to UI actions. These commands trigger methods such as ExecuteHideAllColumns and ExecuteShowAllColumns, which loops through the column collection and updates the Visible property accordingly.
private void ExecuteHideColumns()
{
IsOpenForHideColumns = true;
}
private void ExecuteHideAllColumns()
{
foreach (var item in columns)
{
if (item.MappingName == "IsSelected")
continue;
item.Visible = false;
}
}
private void ExecuteShowAllColumns()
{
foreach (var item in columns)
{
if (item.MappingName == "IsSelected")
continue;
item.Visible = true;
}
} In addition to managing column visibility, the onboarding tracker supports advanced interactions within the DataGrid, such as sorting, grouping, and filtering. These features are implemented using command-based logic, ensuring a consistent and maintainable architecture.
The SortColumns command manages sorting logic in the DataGrid. When triggered, it calls ExecuteAddSorting, which clears existing sort descriptions and adds new ones based on user input.
private void ExecuteAddSorting()
{
if (SelectedSortColumn != null)
{
SortColumnDescriptions.Clear();
var sortColumnDescription = new SortColumnDescription()
{
ColumnName = this.SelectedSortColumn.Name ?? string.Empty,
SortDirection = IsOnState ? ListSortDirection.Ascending : ListSortDirection.Descending
};
SortColumnDescriptions.Add(sortColumnDescription);
}
} The GroupColumns command organizes DataGrid content into logical categories. When executed, the ExecuteAddGrouping method clears existing group settings and applies new ones, helping users analyze onboarding data more effectively.
private void ExecuteAddGrouping()
{
if (SelectedGroupColumn != null)
{
GroupColumnDescriptions.Clear();
var groupColumnDescription = new GroupColumnDescription() { ColumnName = this.SelectedGroupColumn.Name ?? string.Empty };
GroupColumnDescriptions.Add(groupColumnDescription);
}
} The FilterRecords method applies string or numeric filters based on selected column and condition. It uses helper methods like MakeStringFilter and MakeNumericFilter to evaluate each record.
public bool FilerRecords(object o)
{
if (SelectedColumn == null && string.IsNullOrEmpty(this.SelectedCondition))
return true;
double res;
bool checkNumeric = double.TryParse(this.FilterText, out res);
var item = o as Employee;
if (item != null && !string.IsNullOrEmpty(this.FilterText) && this.FilterText.Equals(string.Empty))
{
return true;
}
else
{
if (item != null && SelectedColumn != null)
{
if (checkNumeric && !(this.SelectedColumn.Name ?? string.Empty).Equals("All Columns") && !(this.SelectedCondition ?? string.Empty).Equals("Contains"))
{
bool result = this.MakeNumericFilter(item, this.SelectedColumn.Name, this.SelectedCondition);
return result;
}
else if ((this.SelectedColumn.Name ?? string.Empty).Equals("All Columns"))
{
if ((item.Name ?? string.Empty).ToString().ToLower().Contains(this.FilterText!.ToLower()) ||
(item.Email ?? string.Empty).ToString().ToLower().Contains(this.FilterText.ToLower()) ||
item.DateOfJoining.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
item.OnboardingProgressPercentage.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
item.PendingTasks.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
(item.Checklists != null && item.Checklists.ToString().ToLower().Contains(this.FilterText.ToLower())))
{
return true;
}
return false;
}
else
{
bool result = this.MakeStringFilter(item, this.SelectedColumn.Name, this.SelectedCondition!);
return result;
}
}
}
return false;
} After implementing all the above features, data binding, column customization, and command-based interactions, the final UI brings everything together in a responsive and user-friendly layout.
Explore the complete implementation on GitHub to dive deeper into the onboarding tracker UI built with .NET MAUI DataGrid.
We explored how to build an Employee Onboarding Tracker UI using .NET MAUI DataGrid. You can create a structured and interactive onboarding dashboard by leveraging MVVM, custom column types, and nested checklist rendering. The approach supports real-time updates, progress tracking, and flexible data manipulation, which is ideal for task-driven applications.
If you’re a Syncfusion user, you can download the setup from the license and downloads page. Otherwise, you can download a free 30-day trial.
You can also contact us through our support forum, support portal, or feedback portal for queries. We are always happy to assist you!