Detailed Blog page Skeleton loader
Build a Gradesheet App with .NET MAUI DataGrid

TL;DR: This blog offers a step-by-step guide to building a gradesheet app using .NET MAUI and Syncfusion® DataGrid. It walks through creating a student data model, binding it to a ViewModel, and designing an interactive UI with advanced features like column hiding, sorting, grouping, and filtering, all implemented within the MVVM architecture for a clean and maintainable codebase.

Efficiently managing student grades is essential for educators and institutions. In this blog, we’ll explore how to build a cross-platform gradesheet app using Syncfusion® .NET MAUI DataGrid. You’ll learn how to set up a student data model, bind it to a ViewModel, and create a dynamic UI with features like sorting, filtering, grouping, and column customization, all within the MVVM design pattern.

Let’s explore each step in detail.

Step 1: Defining the grade class

The Grade class serves as the foundation of our application’s data model, representing the student grade information as shown in the following code example.

public class Grade
{
    // Backing fields
    private int _id;
    private string _studentName;
    private string _subjectName;
    private double _assignmentScore;
    private double _quizScore;
    private double _examScore;
    private double _projectScore;
    private string _comments;

    // Properties with backing fields and PropertyChanged notification
    public int ID
    {
        get
        {
            return _id;
        }
        set
        {
            _id = value;
            OnPropertyChanged(nameof(ID));
        }
    }

    public string StudentName
    {
        get
        {
            return _studentName;
        }
        set
        {
            _studentName = value;
            OnPropertyChanged(nameof(StudentName));
        }
    }

    public string SubjectName
    {
        get
        {
            return _subjectName;
        }
        set
        {
            _subjectName = value;
            OnPropertyChanged(nameof(SubjectName));
        }
    }

    public double AssignmentScore
    {
        get
        {
            return _assignmentScore;
        }
        set
        {
            _assignmentScore = value;
            OnPropertyChanged(nameof(AssignmentScore));
        }
    }

    public double QuizScore
    {
        get
        {
            return _quizScore;
        }
        set
        {
            _quizScore = value;
            OnPropertyChanged(nameof(QuizScore));
        }
    }

    public double ExamScore
    {
        get
        {
            return _examScore;
        }
        set
        {
            _examScore = value;
            OnPropertyChanged(nameof(ExamScore));
        }
    }

    public double ProjectScore
    {
        get
        {
            return _projectScore;
        }
        set
        {
            _projectScore = value;
            OnPropertyChanged(nameof(ProjectScore));
        }
    }

    public string Comments
    {
        get
        {
            return _comments;
        }
        set
        {
            _comments = value;
            OnPropertyChanged(nameof(Comments));
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public double CalculateFinalGrade()
    {
        // Example calculation - adjust based on actual needs and weightage
        return (AssignmentScore + QuizScore + ExamScore + ProjectScore) / 4;
    }
}

This class encapsulates all properties that track a student’s academic performance across different metrics, while also storing qualitative feedback in the comments section. The CalculateFinalGrade method computes the overall percentage by averaging scores from all graded categories.

Step 2: Populating student data in the ViewModel

We’ll now create the GradeSheetViewModel to manage the collection of grades for ItemsSource binding property, handle column definition and implement data manipulation as shown in the below code example.

The ItemsSource property is defined as follows:

public class GradeSheetViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Grade> grades;

    public ObservableCollection<Grade> Grades
    {
        get
        {
            return grades;
        }
        set
        {
            grades = value;
            OnPropertyChanged(nameof(Grades));
        }
    }    
}

To populate the grades collection with default student data, refer to the below code example.

private void PopulateCollection()
{
    // Populate Grades collection
    Grades = new ObservableCollection<Grade>();
    for (int i = 0; i < studentNames.Length; i++)
    {
        Grades.Add(new Grade
        {
            ID = studentIDs[i],
            StudentName = studentNames[i],
            SubjectName = subjectNames[i % subjectNames.Length],
            AssignmentScore = assignmentScores[i],
            QuizScore = quizScores[i],
            ExamScore = examScores[i],
            ProjectScore = projectScores[i],
            Comments = comments[i],
        });
    }
}

In the ViewModel, you typically define a ColumnCollection to configure which columns appear in the DataGrid. This collection lets you explicitly specify columns, controlling exactly which data fields are visible to users.

private void InitializeColumns()
{
    columns = new ColumnCollection();
    var studentNameIdUnboundColumn = new DataGridUnboundColumn
    {
        MappingName = "StudentID",
        HeaderText = "Student ID"
    };
    columns.Add(studentNameIdUnboundColumn);
var studentIdColumn = new DataGridTextColumn { MappingName = "ID", HeaderText = "ID" }; columns.Add(studentIdColumn); var studentNameColumn = new DataGridTextColumn { MappingName = "StudentName", HeaderText = "Student Name" }; columns.Add(studentNameColumn); var courseNameColumn = new DataGridTextColumn { MappingName = "SubjectName", HeaderText = "Course Name" }; columns.Add(courseNameColumn); var assignmentScoreColumn = new DataGridNumericColumn { MappingName = "AssignmentScore", HeaderText = "Assignment Score" }; columns.Add(assignmentScoreColumn); var quizScoreColumn = new DataGridNumericColumn { MappingName = "QuizScore", HeaderText = "Quiz Score" }; columns.Add(quizScoreColumn); var examScoreColumn = new DataGridNumericColumn { MappingName = "ExamScore", HeaderText = "Exam Score" }; columns.Add(examScoreColumn); var projectScoreColumn = new DataGridNumericColumn { MappingName = "ProjectScore", HeaderText = "Project Score" }; columns.Add(projectScoreColumn); var gradeColumn = new DataGridUnboundColumn { MappingName = "Grade", HeaderText = "Grade" }; columns.Add(gradeColumn); var commentsColumn = new DataGridTextColumn { MappingName = "Comments", HeaderText = "Comments" }; columns.Add(commentsColumn); }

In the ViewModel, add columns to the ColumnCollection using specialized objects like DataGridTextColumn, and DataGridNumericColumn, chosen based on your data type requirements.

Each column requires two key properties:

  • MappingName: Specifies which data source property the column binds to.
  • HeaderText: Defines the column’s display title.

Step 3: Designing the UI

The XAML file binds the UI components to the ViewModel, enabling seamless data interaction. To bind the Columns from the ViewModel to the SfDataGrid, use the Columns property. This ensures the DataGrid dynamically updates based on the column definitions provided by the ViewModel.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:syncfusion="clr-namespace:Syncfusion.Maui.DataGrid;assembly=Syncfusion.Maui.DataGrid"
             xmlns:local="clr-namespace:DataGridMAUI"
             x:Class="DataGridMAUI.MainPage"> 

    <ContentPage.BindingContext>
        <local:GradeSheetViewModel />
    </ContentPage.BindingContext>
 
    <syncfusion:SfDataGrid x:Name="dataGrid" Grid.Row="1" ColumnWidthMode="Fill" AutoGenerateColumnsMode="None" Columns="{Binding Columns}" FrozenColumnCount="1" ItemsSource="{Binding Grades}">
        <syncfusion:SfDataGrid.Behaviors>
           <local:FilteringBehavior />
        </syncfusion:SfDataGrid.Behaviors>
    </syncfusion:SfDataGrid>
</ContentPage>
  • The BindingContext is typically set at the page level and must be linked to the ViewModel to access its properties and commands.
  • ItemsSource: This bindable property connects the DataGrid to the data source Grades property in the ViewModel, which is an ObservableCollection<Grade>.
  • Columns: This property binds the ColumnCollection from the ViewModel to the DataGrid, ensuring that all column definitions are rendered correctly in the UI.

Step 4: Handling data manipulation

In this step, we define commands to handle columns visibility, grouping, filtering, sorting and clearing operations.

public class GradeSheetViewModel : INotifyPropertyChanged
{
    // Commands for open popup to handle columns and data manipulation.
    public ICommand HideColumns { get; set; }
    public ICommand HideAllColumns { get; set; }
    public ICommand ShowAllColumns { get; set; }

    public ICommand FilterColumns { get; set; }
    public ICommand ClearFilter { get; set; }

    public ICommand GroupColumns { get; set; }
    public ICommand ClearGroup { get; set; }

    public ICommand SortColumns { get; set; }
    public ICommand ClearSort { get; set; }

    private void InitializeCommands()
    {
        HideColumns = new Command(ExecuteHideColumns);
        ShowAllColumns = new Command(ExecuteShowAllColumns);
        HideAllColumns = new Command(ExecuteHideAllColumns);

        FilterColumns = new Command(ExecuteFilterColumns);
        ClearFilter = new Command(ExecuteClearFilter);
        GroupColumns = new Command(ExecuteGroupColumns);
        ClearGroup = new Command(ExecuteClearGroups);
        SortColumns = new Command(ExecuteSortColumns);
        ClearSort = new Command(ExecuteClearSorts);            
    }        
}

The HideColumns, ShowAllColumns, and HideAllColumns commands manage column visibility in the DataGrid.

  • HideColumns and ShowAllColumns are linked to UI buttons or actions, toggling visibility.
  • The ExecuteHideAllColumns and ExecuteShowAllColumns methods iterate through the column collection to set the Visible property.
private void ExecuteHideColumns()
{
    IsOpenForHideColumns = true; //  which will open the show/hide all columns popup.
}

private void ExecuteHideAllColumns()
{
    foreach (var item in columns)
    {
        item.Visible = false;                
    }
}

private void ExecuteShowAllColumns()
{
    foreach (var item in columns)
    {
        item.Visible = true;                
    }
}

Refer to the following image.

.NET MAUI DataGrid demonstrating show/hide columns
.NET MAUI DataGrid demonstrating show/hide columns

The SortColumns command manages the DataGrid’s sorting behavior through its associated ExecuteAddSorting method, which performs two key operations.

  • Clears all existing sort descriptions
  • Applies new sorting based on user input.

The SortColumnDescriptions collection property will be used from ViewModel to handle this operation.

private void ExecuteAddSorting()
{
    ExecuteClearSorts();
    var sortColumnDescription = new SortColumnDescription()
    {
        ColumnName = this.SelectedSortColumn.Name,
        SortDirection = IsOnState ? ListSortDirection.Ascending : ListSortDirection.Descending
    };
    SortColumnDescriptions.Add(sortColumnDescription);
}

private void ExecuteClearSorts()
{
    if (SortColumnDescriptions == null)
        return;
    SortColumnDescriptions.Clear();            
}

Refer to the following image.

.NET MAUI DataGrid demonstrating sorting in the gradesheet
.NET MAUI DataGrid demonstrating sorting in the gradesheet

The GroupColumns command organizes data into logical categories. The ExecuteAddGrouping method clears previous grouping criteria and applies new ones, enhancing data analysis. GroupColumnDescriptions property used here to handle this grouping operation.

private void ExecuteAddGrouping()
{
    ExecuteClearGroups();
    var groupColumnDescription = new GroupColumnDescription()
    {
        ColumnName = this.SelectedGroupColumn.Name
    };
    GroupColumnDescriptions.Add(groupColumnDescription);
}

private void ExecuteClearGroups()
{
    if (GroupColumnDescriptions == null)
        return;

    GroupColumnDescriptions.Clear();            
}

Refer to the following image.

.NET MAUI DataGrid demonstrating grouping by column in the gradesheet
.NET MAUI DataGrid demonstrating grouping by column in the gradesheet

The FilterColumns command applies conditional filtering. The FilerRecords method checks which records match the criteria, using MakeStringFilter and MakeNumericFilter for detailed logic.

public bool FilerRecords(object o)
{
    double res;
    bool checkNumeric = double.TryParse(this.FilterText, out res);
    var item = o as Grade;
    if (item != null && this.FilterText!.Equals(string.Empty) && !string.IsNullOrEmpty(this.FilterText))
    {
        return true;
    }
    else
    {
        if (item != null && SelectedColumn != null)
        {
            if (checkNumeric && !this.SelectedColumn.Name!.Equals("All Columns") && !this.SelectedCondition!.Equals("Contains"))
            {
                bool result = this.MakeNumericFilter(item, this.SelectedColumn.Name, this.SelectedCondition);
                return result;
            }
            else if (this.SelectedColumn.Name!.Equals("All Columns"))
            {
                if (item.ID!.ToString().ToLower().Contains(this.FilterText!.ToLower()) ||
                    item.StudentName!.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
                    item.SubjectName!.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
                    item.AssignmentScore!.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
                    item.QuizScore!.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
                    item.ExamScore!.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
                    item.ProjectScore!.ToString().ToLower().Contains(this.FilterText.ToLower()) ||
                    item.Comments!.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 executing the code examples, the final output resembles the following image.

.NET MAUI DataGrid demonstrating filtering in the gradesheet
.NET MAUI DataGrid demonstrating filtering in the gradesheet

GitHub reference

For more details, refer to the GitHub demo.

FAQs

Q1. Does the DataGrid support real-time data updates and high performance?
Yes! The DataGrid is optimized for performance and can handle real-time updates efficiently. It uses virtualization to render only visible rows and columns, ensuring smooth scrolling and interaction even with large datasets.

Q2. Can I customize the appearance and behavior of the DataGrid?
Absolutely. The DataGrid offers extensive customization options including:

  • Custom cell templates
  • Conditional styling
  • Row and column customization
  • Theme support and adaptive UI for different screen sizes

This flexibility allows developers to tailor the grid to match their app’s branding and UX requirements.

Q3. Is the Syncfusion® .NET MAUI DataGrid easy to integrate and use?
Yes, it’s designed with developer productivity in mind. With comprehensive documentation, code examples, and a consistent API, you can quickly integrate the DataGrid into your .NET MAUI project. Syncfusion® also provides dedicated support to help you get started and troubleshoot any issues.

Supercharge your cross-platform apps with Syncfusion's robust .NET MAUI controls.

Conclusion

Thanks for reading! The .NET MAUI DataGrid empowers developers to create powerful gradesheet apps for educational institutions. Its robust features such as sorting, filtering, grouping, and column management enable dynamic data visualization and manipulation across iOS, Android, macOS, and Windows. By leveraging the MVVM pattern, this setup ensures scalability and maintainability, making it ideal for schools and universities seeking efficient data management solutions. Try building your own gradesheet app today and explore the full potential of .NET MAUI!

The existing customers can download the new version of Essential Studio on the license and downloads page.  If you are not a Syncfusion® customer, try our 30-day free trial to check out our incredible features. 

Got questions or ideas? Drop them in the comments or contact us through our support forumsupport portal, or feedback portal. We are always happy to assist you!

Test Flight
App Center Badge
Google Play Store Badge
Microsoft Badge
Github Store Badge

Be the first to get updates

Jayaleshwari N

Meet the Author

Jayaleshwari N

Jayaleshwari N works for Syncfusion as a product manager. She has been a .NET developer since 2013, and has experience in the development of custom controls in Xamarin and MAUI platforms.