Skip to main content Skip to footer

Building a .NET MAUI Medical Dashboard for Blood Sugar Tracking with ComponentOne

Quick Start Guide
What You Will Need

ComponentOne WinUI & MAUI Edition

Visual Studio

Controls Referenced

FlexGrid for MAUI

FlexChart for MAUI

Tutorial Concept Discover how to create a mobile dashboard app that displays medical data from a local data source, such as an Excel file.

Monitoring blood sugar levels is an essential part of diabetes management, and modern mobile apps have made this process more intuitive and accessible than ever. Whether you're developing a companion app for medical devices or building wellness tools for health-focused users, creating a clean and informative dashboard is key.

In this tutorial, you'll learn how to build a cross-platform medical dashboard using .NET MAUI, Microsoft's unified framework for Android, iOS, and Windows applications. We'll use ComponentOne FlexGrid to display readings and FlexChart to visualize trends over time, providing users with a clear picture of how their glucose levels change throughout the day.

.NET MAUI Dashboard

The app will load data from a local Excel file, parse it into a strongly typed collection, and present it through powerful, modern UI controls. By the end of this guide, you'll have a fully functioning .NET MAUI dashboard that runs seamlessly across mobile and desktop platforms. This serves as a foundation for real-world diabetic tracking or other medical data applications.

We will explore:

Ready to check it out? Download ComponentOne Today!

.NET MAUI Project Setup

This step walks through the project setup and component package installation. To build a .NET MAUI application, you'll need:

  • Visual Studio 2022 (version 17.7 or later)
  • The .NET Multi-platform App UI development workload
  • Platform-specific tooling:
    • Android SDK and emulators
    • Xcode 15+ (for iOS development on macOS)
    • Windows App SDK for WinUI apps

Once installed, create a new .NET MAUI App project in Visual Studio named "HealthCareBizMAUI". Next, we will install the UI component packages.

ComponentOne provides enterprise-grade UI components for .NET MAUI, including FlexGrid, FlexChart, and Excel I/O. Install the following NuGet packages from the NuGet Package Manager:

Next, we will load data from an Excel file and convert it into a strongly typed collection that can be bound to our UI controls.

Loading an Excel File in .NET MAUI

For this tutorial, we will simulate a scenario where the data is collected and stored locally, such as from a medical device connected to the user's phone via Bluetooth. XLSX (OpenXML Excel) is a widely used file type, making this part of the tutorial applicable to virtually any industry or scenario.

You can download the sample blood sugar readings XLSX/Excel file, or skip to the end and download the entire sample project. Add the XLSX file to your .NET MAUI project in the Resources folder with its BuildAction property set to "MauiAsset". The file data source looks like this:

.NET MAUI Dashboard Data Source

To work with this data in C#, we need to convert it to a strongly typed collection. The BloodSugarReading model below represents a single blood sugar entry from the Excel file.

public class BloodSugarReading
{
    public DateTime Date { get; set; }
    public DateTime Time { get; set; }
    public double BloodSugarLevel { get; set; }
    public double RunningAverage { get; set; }
    public string? TimeSlice { get; set; }
}

public class BloodSugarSummary
{
    public string? Range { get; set; }
    public int Count { get; set; }
}

Here, we've also defined a BloodSugarSummary object that will be used later on our dashboard.

Next, we need to write the code that will use C1.Excel to read the Excel file and convert it to a collection of BloodSugarReading objects. We load the Excel file asynchronously using the C1.Excel XLBook's LoadAsync method.

using C1.Excel;

public class ExcelLoader
{
    public async Task<List<BloodSugarReading>> LoadAsync()
    {
        var readings = new List<BloodSugarReading>();

        var file = await FileSystem.OpenAppPackageFileAsync("Resources\\bloodsugar.xlsx");

        C1XLBook book = new C1XLBook();
        await book.LoadAsync(file, FileFormat.OpenXml, true);

        var sheet = book.Sheets[0];

        for (int row = 1; row < sheet.Rows.Count; row++)
        {
            var date = sheet[row, 0]?.Value;
            if (date == null) break;

            readings.Add(new BloodSugarReading
            {
                Date = Convert.ToDateTime(sheet[row, 0]?.Value),
                Time = Convert.ToDateTime(sheet[row, 1]?.Value),
                BloodSugarLevel = Convert.ToDouble(sheet[row, 2]?.Value),
                RunningAverage = Convert.ToDouble(sheet[row, 3]?.Value),
                TimeSlice = sheet[row, 4]?.Value?.ToString()
            });
        }

        return readings;
    }
}

We also use the .NET MAUI FileSystem API to ensure we can locate the file within our app's resources across all mobile platforms (Android, iOS, macOS, and WinUI/Windows 11). Make sure the resource file's BuildAction is set to MauiAsset.

Next, we will create the .NET MAUI dashboard UI, incorporating the FlexGrid and FlexChart components.

Creating the .NET MAUI Dashboard UI

This step covers building the UI. First, we highlight several key points in designing the dashboard layout.

We display three tiles at the top that are created simply by using Border elements, and we will update the text values in code using LINQ later.

.NET MAUI Dashboard UI

The FlexGrid and chart controls are put in a VerticalStackLayout, which is also inside a ScrollView. This allows the user to scroll the entire app window and is necessary to support any device size.

We use the FlexLayout control to support mobile device layouts by wrapping the charts horizontally or vertically, depending on the available space. We also set the chart's MaximumWidthRequest to a value, such as 800, which will allow the two charts to display on the same line for wider resolutions.

.NET MAUI Dashboard

We use the FlexChart's SymbolSize property in conjunction with the LineSymbols chart type and centered data labels to create this style of chart, where the labels appear as large dots.

.NET MAUI Chart Control

<c1:FlexChart ChartType="LineSymbols" BindingX="Time" LegendPosition="Bottom"
              MaximumWidthRequest="800">
    <c1:Series Binding="RunningAverage" SeriesName="Running Average" SymbolSize="30">
        <c1:Series.Style>
            <c1:ChartStyle StrokeThickness="3" />
        </c1:Series.Style>
    </c1:Series>
    <c1:Series Binding="BloodSugarLevel" SeriesName="Blood Sugar (mg/dl)" SymbolSize="30">
        <c1:Series.Style>
            <c1:ChartStyle StrokeThickness="3" />
        </c1:Series.Style>
    </c1:Series>
    <c1:FlexChart.DataLabel>
        <c1:DataLabel Content="{}{value}" Position="Center" Overlapping="Hide">
            <c1:DataLabel.Style>
                <c1:ChartStyle Stroke="White"/>
            </c1:DataLabel.Style>
        </c1:DataLabel>
    </c1:FlexChart.DataLabel>
</c1:FlexChart>

For the pie chart, FlexPie, we display percentages in the labels using the chart's built-in analysis features. Using the "p" placeholder value in curly braces will automatically calculate the slice's percentage of the whole pie.

...
<c1:FlexPie.DataLabel>
    <c1:PieDataLabel Content="{}{p:0}%" Position="Center">
        <c1:PieDataLabel.Style>
            <c1:ChartStyle Stroke="White"/>
        </c1:PieDataLabel.Style>
    </c1:PieDataLabel>
</c1:FlexPie.DataLabel>
...

We define a custom color palette resource to be shared between charts that matches our app's branding.

...
<ContentPage.Resources>
  <g:List x:Key="ChartPalette" x:TypeArguments="Brush">
      <SolidColorBrush Color="#C830CC" />
      <SolidColorBrush Color="#4EA6DC" />
      <SolidColorBrush Color="#FF3399" />
  </g:List>
</ContentPage.Resources>
...
<c1:FlexChart Palette="Custom" CustomPalette="{StaticResource ChartPalette}">
...

Below is the full XAML markup for the dashboard UI. You can open MainPage.xaml and replace its contents.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:c1="http://schemas.componentone.com/winfx/2006/xaml"
             xmlns:g="clr-namespace:System.Collections.Generic;assembly=mscorlib"
             x:Class="HealtchCareBizMAUI.MainPage">
    <ContentPage.Resources>
        <g:List x:Key="ChartPalette" x:TypeArguments="Brush">
            <SolidColorBrush Color="#C830CC" />
            <SolidColorBrush Color="#4EA6DC" />
            <SolidColorBrush Color="#FF3399" />
        </g:List>
    </ContentPage.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Border Background="#5CE6E6">
            <Image Source="healthclinic_logo.png" HeightRequest="80" Aspect="AspectFit" SemanticProperties.Description="healthclinic biz logo" />
        </Border>

        <ScrollView Grid.Row="1">
            <VerticalStackLayout Padding="10,0" Spacing="25">
                <Label x:Name="labelDate" Text="November 10th, 2025" Style="{StaticResource Headline}" SemanticProperties.HeadingLevel="Level1" />

                <Label Text="Welcome to the HealthClinic Sugar Tracker App!" Style="{StaticResource SubHeadline}"
                       SemanticProperties.HeadingLevel="Level2" SemanticProperties.Description="Welcome to the HealthClinic Sugar Tracker App!" />

                <HorizontalStackLayout HorizontalOptions="CenterAndExpand">
                    <Border Background="#FF3399" HorizontalOptions="FillAndExpand" Margin="5" Padding="10" WidthRequest="150" HeightRequest="100">
                        <VerticalStackLayout>
                            <Label x:Name="labelCurrent" Text="130" Style="{StaticResource Headline}" TextColor="White"/>
                            <Label Text="Current" HorizontalTextAlignment="Center" TextColor="White"/>
                        </VerticalStackLayout>

                    </Border>
                    <Border Background="#A3A3A3" HorizontalOptions="FillAndExpand" Margin="5" Padding="10" WidthRequest="150" HeightRequest="100">
                        <VerticalStackLayout>
                            <Label x:Name="labelDaysHigh" Text="256" Style="{StaticResource Headline}" TextColor="White"/>
                            <Label Text="Day's High" HorizontalTextAlignment="Center" TextColor="White"/>
                        </VerticalStackLayout>
                    </Border>
                    <Border Background="#CACACA" HorizontalOptions="FillAndExpand" Margin="5" Padding="10" WidthRequest="150" HeightRequest="100">
                        <VerticalStackLayout>
                            <Label x:Name="labelDaysLow" Text="86" Style="{StaticResource Headline}" TextColor="White"/>
                            <Label Text="Day's Low" HorizontalTextAlignment="Center" TextColor="White"/>
                        </VerticalStackLayout>
                    </Border>
                </HorizontalStackLayout>

                <FlexLayout Direction="Row" Wrap="Wrap" HorizontalOptions="CenterAndExpand">
                    <c1:FlexChart x:Name="flexChart1" ChartType="LineSymbols" BindingX="Time" LegendPosition="Bottom"
                                  MaximumWidthRequest="800" HeightRequest="300" Palette="Custom" CustomPalette="{StaticResource ChartPalette}">
                        <c1:FlexChart.HeaderStyle>
                            <c1:ChartStyle FontSize="32" />
                        </c1:FlexChart.HeaderStyle>
                        <c1:Series Binding="BloodSugarLevel" SeriesName="Blood Sugar (mg/dl)" SymbolSize="30">
                            <c1:Series.Style>
                                <c1:ChartStyle StrokeThickness="3" />
                            </c1:Series.Style>
                        </c1:Series>
                        <c1:Series Binding="RunningAverage" SeriesName="Running Average" SymbolSize="30">
                            <c1:Series.Style>
                                <c1:ChartStyle StrokeThickness="3" />
                            </c1:Series.Style>
                        </c1:Series>
                        <c1:FlexChart.DataLabel>
                            <c1:DataLabel Content="{}{value}" Position="Center" Overlapping="Hide">
                                <c1:DataLabel.Style>
                                    <c1:ChartStyle Stroke="White"/>
                                </c1:DataLabel.Style>
                            </c1:DataLabel>
                        </c1:FlexChart.DataLabel>
                        <c1:FlexChart.AxisY>
                            <c1:Axis Labels="False" AxisLine="False" MajorGrid="True" MajorTickMarks="None" Min="0" Max="300" />
                        </c1:FlexChart.AxisY>
                        <c1:FlexChart.AxisX>
                            <c1:Axis MajorTickMarks="None" />
                        </c1:FlexChart.AxisX>
                    </c1:FlexChart>

                    <c1:FlexPie x:Name="flexPie1" Binding="Count" BindingName="Range" Header="Todays Snapshot" InnerRadius="0.7" LegendPosition="Top" 
                                WidthRequest="500" HeightRequest="300" Palette="Custom" CustomPalette="{StaticResource ChartPalette}">
                        <c1:FlexPie.HeaderStyle>
                            <c1:ChartStyle FontSize="32" />
                        </c1:FlexPie.HeaderStyle>
                        <c1:FlexPie.DataLabel>
                            <c1:PieDataLabel Content="{}{p:0}%" Position="Center">
                                <c1:PieDataLabel.Style>
                                    <c1:ChartStyle Stroke="White"/>
                                </c1:PieDataLabel.Style>
                            </c1:PieDataLabel>
                        </c1:FlexPie.DataLabel>
                    </c1:FlexPie>
                </FlexLayout>

                <c1:FlexGrid x:Name="flexGrid1" AutoGenerateColumns="False" HorizontalOptions="CenterAndExpand">
                    <c1:FlexGrid.Columns>
                        <c1:GridColumn Binding="Date" Format="d" />
                        <c1:GridColumn Binding="Time" Format="t"/>
                        <c1:GridColumn Binding="BloodSugarLevel"/>
                        <c1:GridColumn Binding="RunningAverage"/>
                        <c1:GridColumn Binding="TimeSlice" />
                    </c1:FlexGrid.Columns>
                </c1:FlexGrid>

            </VerticalStackLayout>
        </ScrollView>
    </Grid>
</ContentPage>

Binding the Data to the UI

The next step is to connect the ExcelLoader class with our UI to populate, or data bind, our dashboard controls.

ExcelLoader _loader = new ExcelLoader();

public MainPage()
{
    InitializeComponent();

    LoadData();
}

private async void LoadData()
{
    var data = await _loader.LoadAsync();

    flexGrid1.ItemsSource = data;

    // get first date from dataset
    var firstDate = data.First().Date;

    // filter chart to display data for first afternoon only
    flexChart1.ItemsSource = data.Where(item => item.Date.Equals(firstDate) && item.TimeSlice == "Afternoon");
    flexChart1.Header = "Afternoon Summary";

    // update tiles using LINQ
    labelCurrent.Text = data.First().BloodSugarLevel.ToString();
    labelDaysHigh.Text = data.Max(b => b.BloodSugarLevel).ToString();
    labelDaysLow.Text = data.Min(b => b.BloodSugarLevel).ToString();

    // populate FlexPie using LINQ
    var bloodSugarSummary = new ObservableCollection<BloodSugarSummary>();

    var belowRange = new BloodSugarSummary();
    belowRange.Range = "Below Range (< 60)";
    belowRange.Count = data.Where(item => item.BloodSugarLevel < 60).Count();
    bloodSugarSummary.Add(belowRange);

    var inRange = new BloodSugarSummary();
    inRange.Range = "In Range";
    inRange.Count = data.Where(item => item.BloodSugarLevel >= 60 && item.BloodSugarLevel <= 149).Count();
    bloodSugarSummary.Add(inRange);

    var aboveRange = new BloodSugarSummary();
    aboveRange.Range = "Above Rane (> 149)";
    aboveRange.Count = data.Where(item => item.BloodSugarLevel > 149).Count();
    bloodSugarSummary.Add(aboveRange);

    flexPie1.ItemsSource = bloodSugarSummary;
}

Observe that we use LINQ queries to filter the data into the FlexChart, FlexPie, and tile labels. This is because we don't necessarily need the entire data set for these charts, which are designed to show a snapshot of a single day or afternoon. We use the BloodSugarSummary class to help build an aggregated data view for the FlexPie control. The entire data set is bound to the FlexGrid, allowing the user to dive deeper and view every detail.

Conclusion

In this tutorial, you built a functional medical dashboard using .NET MAUI and ComponentOne controls. You learned how to:

  • Load an Excel file inside a MAUI app
  • Parse data using ComponentOne's Excel tools
  • Display data in a FlexGrid datagrid
  • Visualize glucose trends using FlexChart
  • Visualize glucose summaries using FlexPie
  • Create a mobile-friendly medical dashboard UI

Download the complete .NET MAUI HealthcareBiz sample app.

We will end the tutorial at this point, but there is more we could do. We can add more chart features, such as drill-down, tooltips, and alarm zones, as well as datagrid features like grouping, aggregation, and filtering. You can learn more about FlexGrid and FlexChart in the ComponentOne .NET MAUI documentation.

Ready to try it out? Download ComponentOne Today!

comments powered by Disqus