Skip to main content Skip to footer

How to Build a Windows 11 Dashboard with .NET WinUI Charts

Quick Start Guide
What You Will Need

ComponentOne WinUI & MAUI Edition

Visual Studio 2022

Controls Referenced

FlexChart for WinUI

Tutorial Concept Learn how to build a data-bound dashboard for Windows 11 desktop using WinUI 3.0 and ComponentOne FlexChart.

In today's data-driven world, having a clear and accessible way to analyze data is essential for making informed decisions. To address this need, dashboards are preferable solutions as they are designed for both technical and non-technical audiences. Dashboards make complex data easy to understand by delivering valuable insights through charts and graphs.

In this blog, we will develop a Windows dashboard app for a hospital system that shows patient information. We are using the chart and datagrid controls from our ComponentOne WinUI control suite to showcase different aspects of patient data. The final dashboard will look like this:

WinUI Dashboard

Let’s break down the process of developing the dashboard into the following steps:

Ready to Get Started? Download ComponentOne Today!

Setup a WinUI Project and Add Required Dependencies

We’re building this dashboard using WinUI 3, which is used to create modern, Universal Windows applications, otherwise known as Microsoft Store apps. It uses XAML UI components, which are similar to WPF.

Let’s start by creating a WinUI project in Visual Studio 2022 and following the steps below:

1. Open Visual Studio, and select File->New->Project.

2. Search “WinUI template” in the search box and select “Blank App, packaged (WinUI 3 in Desktop)”.

WinUI Template

3. Enter the Project Name and click the “Create” button.

4. Now that we have successfully set up our project, it is time to add all the required dependencies. We are adding FlexGrid and FlexChart dependencies to show the grid and charts in our dashboard. Follow the below steps to install NuGet Packages:

a. In the Solution Explorer, right-click on the project and select "Manage NuGet packages."

Solution Explorer

b. Search “C1.WinUI.Grid” in the search bar and install the latest version of the C1.WinUI.Grid NuGet package.

Nuget Package

Similarly, you can install the “C1.WinUI.Chart” NuGet package to use C1 WinUI Charts in your project.

Now, we have set up a WinUI project and added the required Dependencies.

Create the Dashboard Blueprint

In this step, we are creating a dashboard UI with a FlexGrid that showcases all the patients' records in an organized table, as well as three different types of FlexChart controls that show the following metrics: 

  • Line Chart: Shows patient appointments over the years
  • Pie Chart: Shows the distribution of medical conditions of patients
  • Column Chart: Shows insurance providers registered by patients

Let’s follow the below steps to create a dashboard UI:

1. Add XMLNS attributes in the Window tag of MainWindow.xaml file:

xmlns:c1grid="using:C1.WinUI.Grid"
xmlns:c1chart="using:C1.WinUI.Chart"

2. Add C1FlexGrid and C1FlexChart controls in Xaml by replacing the <StackPanel> block with the following Xaml code:

<Grid Background="Black">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="3*"/>
        <ColumnDefinition Width="1.5*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="60"/>
        <RowDefinition Height="4*"/>
        <RowDefinition Height="3*"/>
    </Grid.RowDefinitions>
    <TextBlock Text="Hospital Records" FontSize="25" Foreground="White" FontWeight="Bold" VerticalAlignment="Center" Margin="10,0,0,0"/>
    <TextBlock x:Name="patientTxt" FontSize="18" Foreground="White" FontWeight="Bold" VerticalAlignment="Center" Margin="10,0,0,0" Grid.Column="1"/>

    <!--Hospital Grid Data-->
    <c1grid:FlexGrid x:Name="patientRecordsGrid" Margin="0,0,5,5" Background="#4d4d4d" Foreground="White" 
                     Grid.Row="1" HeadersVisibility="All" SelectionMode="RowRange" VerticalScrollBarVisibility="Visible"
                     HorizontalScrollBarVisibility="Visible" BorderBrush="White" BorderThickness="2" ColumnHeaderFontWeight="Bold" ColumnHeaderFontSize="18"/>

    <!--Patient's Admission Chart View over the years-->
    <c1chart:FlexChart x:Name="AdmissionChart" ChartType="LineSymbols" Foreground="White" BorderBrush="White" BorderThickness="2" Grid.Row="2" Margin="0,5,5,0" PlotMargin="80,40,120,60"></c1chart:FlexChart>

    <!--Medical Conditions Pie Chart-->
    <c1chart:FlexPie x:Name="MedicalConditionPieChart" Foreground="White" Footer="Medical Conditions" BorderBrush="White" BorderThickness="2" Grid.Row="1" Grid.Column="1" Margin="5,0,0,5"></c1chart:FlexPie>

    <!--Chart view for Insurance Providers registered by Patient-->
    <c1chart:FlexChart x:Name="InsuranceProviderChart" ChartType="Column" Foreground="White" BorderBrush="White" BorderThickness="2" Grid.Row="2" Grid.Column="1" Margin="5,5,0,0"></c1chart:FlexChart>

Databind the Dashboard

In the previous step, we created a blueprint for the dashboard. To see the actual functional application, let’s bind the data by following the steps below:

1. Add Binding Properties

Firstly, create a ViewModel where we can add binding properties. The ViewModel acts as the data source for the UI where we can control the view.

You will also need to create the following required classes with their respective properties: Appointment.cs, GenderData.cs, MedicalCondition.cs, and InsuranceProvider.cs.
Refer to the sample attached at the end of the blog for these classes.

Follow the steps below to add and initialize the binding properties:

a. Add a class named HospitalViewModel.cs by right-clicking on Project->Add->Class.

Add Class

b. Add required ObservableCollection properties in HospitalViewModel.cs class so we can link these with the Charts and Grid. Here, we add the following properties to fetch the patients' specific records.

//Collection of Patient records for FlexGrid
public ObservableCollection<Appointment> HospitalData;

//Collection of Patient's Admission Gender-wise for Line Chart
public ObservableCollection<GenderData> GenderWiseAdmissionData;

//Collection of Medical Conditions Distribution for Pie Chart
public ObservableCollection<MedicalCondition> MedicalConditionData;

//Collection of Insurance Provider Distribution for Column Chart
public ObservableCollection<InsuranceProvider> InsuranceProviderData;  

c. Create a constructor for the HospitalViewModel class to initialize binding properties by deserializing the JSON. The JSON consists of “Patient’s records.”

public HospitalViewModel()
{
    //Load data from HospitalDataset.json to HospitalData collection
    var json = File.ReadAllText(Windows.ApplicationModel.Package.Current.InstalledPath + "/HospitalDataset.json");
    HospitalData = JsonConvert.DeserializeObject<ObservableCollection<Appointment>>(json);
    //Initialize collections for Charts
    GenderWiseAdmissionData = new ObservableCollection<GenderData>();
    MedicalConditionData = new ObservableCollection<MedicalCondition>();
    InsuranceProviderData = new ObservableCollection<InsuranceProvider>();
}

Note: The HospitalDataSet.json file is included in the sample attached at the end of this blog.

2. Bind Properties in UI

Now, all the required properties are created in the HospitalViewModel.cs class, and it's time to bind these properties to the UI to reflect the data under the ViewModel’s collection properties.

Follow the steps below to bind all required ViewModel properties to the UI in the MainWindow.Xaml file:

a. Initialize the HospitalViewModel instance under the MainWindow class in the MainWindow.Xaml.cs file so we can bind any accessible properties of HospitalViewModel to the MainWindow.Xaml.

public HospitalViewModel vm { get; } = new(); 

b. Bind the HospitalData property to the ItemsSource of the FlexGrid in the MainWindow.xaml file so that the FlexGrid reflects all the records of the HospitalData collection. Update the FlexGrid code in MainWindow.xaml as below:

<c1grid:FlexGrid x:Name="patientRecordsGrid" ItemsSource="{x:Bind vm.HospitalData}"
                 Margin="0,0,5,5" Background="#4d4d4d" Foreground="White" Grid.Row="1"
                 HeadersVisibility="All" SelectionMode="RowRange" VerticalScrollBarVisibility="Visible" 
                 HorizontalScrollBarVisibility="Visible" BorderBrush="White" BorderThickness="2" 
                 ColumnHeaderFontWeight="Bold" ColumnHeaderFontSize="18"/>

c. Bind the GenderWiseAdmissionData property to the ItemsSource of the AdmissionChart (Line Chart). Then, add 'Series' under the chart for genders and set BindingX="Year" to visualize the Gender-wise data over the years. You can also style the axis of FlexChart accordingly.

Update the AdmissionChart code in MainWindow.xaml file as below:

<c1chart:FlexChart x:Name="AdmissionChart" ItemsSource="{x:Bind vm.GenderWiseAdmissionData}" BindingX="Year" ChartType="LineSymbols" Foreground="White" BorderBrush="White" BorderThickness="2" Grid.Row="2" Margin="0,5,5,0">
    <!--Add Series-->
    <c1chart:FlexChart.Series>
        <c1chart:Series Binding="Male" SeriesName="Male">
            <c1chart:Series.DataLabel>
                <c1chart:DataLabel Content=" Male({Year}) : {Male:0}" Position="Bottom" Offset="20" />
            </c1chart:Series.DataLabel>
        </c1chart:Series>
        <c1chart:Series Binding="Female" SeriesName="Female">
            <c1chart:Series.DataLabel>
                <c1chart:DataLabel Content=" Female({Year}) : {Female:0}" Position="Top" Offset="10"  />
            </c1chart:Series.DataLabel>
        </c1chart:Series>
    </c1chart:FlexChart.Series>
    <!--Style Axis-->
    <c1chart:FlexChart.AxisY>
        <c1chart:Axis  Title="No. of Appointments">
            <c1chart:Axis.TitleStyle>
                <c1chart:ChartStyle Stroke="White" FontSize="15" FontWeight="Bold"/>
            </c1chart:Axis.TitleStyle>
        </c1chart:Axis>
    </c1chart:FlexChart.AxisY>
    <c1chart:FlexChart.AxisX>
        <c1chart:Axis Title="Years">
            <c1chart:Axis.TitleStyle>
                <c1chart:ChartStyle Stroke="White" FontSize="15" FontWeight="Bold"/>
            </c1chart:Axis.TitleStyle>
        </c1chart:Axis>
    </c1chart:FlexChart.AxisX>
</c1chart:FlexChart>

In the same way, you can add 'Series' and bind with respective properties for the other two charts. Refer to the Xaml file for other charts in the attached sample at the end of the blog.

Now, we have bound all HospitalViewModel’s properties in the UI, which looks like this:

HospitalViewModel Properties

Sync the Modified Data with Charts

At this point, we can see records in the FlexGrid, and all required properties are bound to the UI. However, not all the charts visualize the data, and they are not synced with FlexGrid’s ItemsSource (Hospitaldata). Let’s sync the data with Charts.

  1. Create an UpdateChartData() method in HospitalViewModel.cs class and update every collection for chart data as per their use case.
public void UpdateChartData()
{
    //Clear all existing chart's records
    GenderWiseAdmissionData.Clear();
    MedicalConditionData.Clear();
    InsuranceProviderData.Clear();

    //Update GenderWiseAdmissionData Collection
    var yearWiseData = HospitalData.DistinctBy(x => x.Year).OrderBy(x => x.Year);
    foreach (var data in yearWiseData)
    {
        var male = HospitalData.Where(x => x.Year == data.Year).Count(x => x.Gender == "Male");
        var female = HospitalData.Where(x => x.Year == data.Year).Count(x => x.Gender == "Female");
        GenderWiseAdmissionData.Add(new GenderData() { Year = data.Year, Male = male, Female = female });
    }

    //Update MedicalConditionData Collection
    var medicalCondition = HospitalData.DistinctBy(x => x.MedicalCondition).OrderBy(x => x.MedicalCondition);
    foreach (var data in medicalCondition)
    {
        var admissionCount = HospitalData.Where(x => x.MedicalCondition == data.MedicalCondition).Count();
        MedicalConditionData.Add(new MedicalCondition() { Name = data.MedicalCondition, AdmissionCount = admissionCount });
    }

    //Update InsuranceProviderData Collection
    var insuranceProviders = HospitalData.DistinctBy(x => x.InsuranceProvider).OrderBy(x => x.InsuranceProvider);
    foreach (var data in insuranceProviders)
    {
        var admissionCount = HospitalData.Where(x => x.InsuranceProvider == data.InsuranceProvider).Count();
        InsuranceProviderData.Add(new InsuranceProvider() { Name = data.InsuranceProvider, Customers = admissionCount });
    }
}

2. Call the UpdateChartData() method in HospitalViewModel’s constructor to initialize the chart with the data once the instance of HospitalViewModel is created.

public HospitalViewModel()
{
    ……
    //Update Data on first Load
    UpdateChartData();
    ……
}

3. Handle the CollectionChanged event of the HospitalData collection and update the chart’s data once the CollectionChanged event fires. This will update the chart’s data when we modify FlexGrid records.

HospitalData.CollectionChanged += (s, e) => 
{
    //Update data once Hospital Data updates
    UpdateChartData();
};

Now, the chart’s data will be shown with the grid data as below:

Charts Grid Data

View and Modify Data of a Particular Patient

Now, the dashboard is almost complete. We can visualize data on FlexGrid and FlexCharts. It’s time to make it more interactive.

Currently, all chart visualization is happening based on the data of all patients. What if we can visualize a particular patient’s information on different parameters? This will help us to analyze a particular patient's data more accurately.

For this, we need to add a little more code by following the steps below:

1. Create a VisualizePatientRecords(string patientId) method in HospitalViewModel.cs class and update every chart’s collection as per their PatientId.

public void VisualizePatientRecords(string patientId)
{
    //Clear all existing chart's records
    GenderWiseAdmissionData.Clear();
    MedicalConditionData.Clear();
    InsuranceProviderData.Clear();

    //Patient's GenderWiseAdmissionData Collection
    var yearWiseData = HospitalData.DistinctBy(x => x.Year).OrderBy(x => x.Year);
    foreach (var data in yearWiseData)
    {
        int? male = HospitalData.First(x => x.PatientId == patientId).Gender == "Male" ? HospitalData.Where(x => x.Year == data.Year && x.PatientId == patientId).Count(x => x.Gender == "Male") : null;
        int? female = HospitalData.First(x => x.PatientId == patientId).Gender == "Female" ? HospitalData.Where(x => x.Year == data.Year && x.PatientId == patientId).Count(x => x.Gender == "Female") : null;
        GenderWiseAdmissionData.Add(new GenderData() { Year = data.Year, Male = male, Female = female });
    }

    //Patient's MedicalConditionData Collection
    var medicalCondition = HospitalData.DistinctBy(x => x.MedicalCondition).OrderBy(x => x.MedicalCondition);
    foreach (var data in medicalCondition)
    {
        var admissionCount = HospitalData.Where(x => x.MedicalCondition == data.MedicalCondition && x.PatientId == patientId).Count();
        if (admissionCount > 0)
            MedicalConditionData.Add(new MedicalCondition() { Name = data.MedicalCondition, AdmissionCount = admissionCount });
    }

    //Patient's InsuranceProviderData Collection
    var insuranceProviders = HospitalData.DistinctBy(x => x.InsuranceProvider).OrderBy(x => x.InsuranceProvider);
    foreach (var data in insuranceProviders)
    {
        var claimsCount = HospitalData.Where(x => x.InsuranceProvider == data.InsuranceProvider && x.PatientId == patientId).Count();
        if (claimsCount > 0)
            InsuranceProviderData.Add(new InsuranceProvider() { Name = data.InsuranceProvider, Customers = claimsCount });
    }
}

2. Handle the SelectionChanged event of FlexGrid to call the VisualizePatientRecords() method.  It will display data of the specific patient in the charts when any row is selected in the grid.

patientRecordsGrid.SelectionChanged += (s, e) =>
{
    if (patientRecordsGrid.Selection != null)
    {
        //Show a particular patient record
        if (patientRecordsGrid.Selection.RowsCount == 1)
        {
            var patient = patientRecordsGrid.SelectedItems[0] as Appointment;
            vm.VisualizePatientRecords(patient.PatientId);
            patientTxt.Text = patient.Name + "(" + patient.PatientId + ")";
        }
        //Show all records
        else
        {
            if (isSelectAll == false)
            {
                patientRecordsGrid.Select(e.CellRange.Row, patientRecordsGrid.ViewRange.Column);
            }
            isSelectAll = true;
            vm.UpdateChartData();
            patientTxt.Text = string.Empty;
        }
    }
    //Show all records
    else
    {
        vm.UpdateChartData();
        patientTxt.Text = string.Empty;
    }
};

3. Handle the CellEditEnded event of FlexGrid and call the VisualizePatientRecords() method to update the specific patient's data. This updates the chart immediately when we edit any record in the grid.

patientRecordsGrid.CellEditEnded += (s, e) =>{
    var patient = patientRecordsGrid.SelectedItems[0] as Appointment;
    vm.VisualizePatientRecords(patient.PatientId);
    patientTxt.Text = patient.Name + "(" + patient.PatientId + ")";
};

This is how our dashboard works to showcase particular patients' records:

WinUI Dashboard animation

Conclusion

We hope this blog helps you create an interactive WinUI dashboard using C1 controls. Download the full sample to customize it further, fit your requirements, and showcase different metrics in your WinUI dashboard.

Ready to try it out? Download ComponentOne Today!

To learn more about these advanced WinUI controls, check out the documentation and sample demos in the links below:

comments powered by Disqus