Chart of the Week: Creating a .NET MAUI Dynamic Bar Race Chart for the Top 10 Populations in the World
Tags: dotnetmaui, charts, mobile, desktop
Published: 2024-01-18 10:39:43

---
title: Chart of the Week: Creating a .NET MAUI Dynamic Bar Race Chart for the Top 10 Populations in the World
published: true
date: 2023-12-22 11:02:51 UTC
tags: dotnetmaui, charts, mobile, desktop
canonical_url: https://www.syncfusion.com/blogs/post/dotnet-maui-bar-race-chart.aspx
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0zn7m9yr627zsonl4m93.png
---
Welcome to our  **Chart of the Week**  blog series!

Today, we’ll visualize the top 10 countries in terms of world population from 1960 to 2021 using a bar race chart, also called an animated bar chart.

Bar race charts are effective tools for demonstrating changes in data over time. The Syncfusion [.NET MAUI Cartesian Charts](https://www.syncfusion.com/maui-controls/maui-cartesian-charts ".NET MAUI Cartesian Charts") control can be used to create a dynamic and interactive bar race chart that captivates viewers and conveys valuable information by summarizing a large dataset in a live visual bar graph, with bars racing to the end based on rankings.

Refer to the following image.
<figure>

  <img src="https://www.syncfusion.com/blogs/wp-content/uploads/2023/12/Visualize-population-data-using-.NET-MAUI-Bar-Race-Chart-1.gif" alt="Visualize population data using .NET MAUI Bar Race Chart" style="width:100%">

</figure>

Let’s get started!

## Step 1: Gather data for the world population

Before creating the chart, we need to gather the [world population](https://ourworldindata.org/explorers/population-and-demography "Article: Population & Demography Data Explorer") data. For this demo, we are getting data from 1960 to 2021.

## Step 2: Prepare the data for the bar race chart

Create the  **Model**  class for holding the population data with the help of the  **XValue** ,  **YValue** ,  **Index** ,  **XString** , and other properties.

Refer to the following code example.

```csharp
public Model(int i, string xName, double x, double y)
{
    Index = i;
    XString = xName;
    XValue = x;
    YValue = y;
    ItemColor = brush[i % brush. Count];
}
```

Generate the data collection with the help of the  **ViewModel**  class and  **Data**  property. Then, convert the CSV data to a collection of population data using the  **ReadCSV**  method.

```csharp
Public Ienumerable<Model> ReadCSV()
{
    Assembly executingAssembly = typeof(App).GetTypeInfo().Assembly;
    Stream inputStream = executingAssembly.GetManifestResourceStream(“BarRaceChart.Resources.Raw.populationdata.csv”);

    if (inputStream == null)
    {
        return new List<Model>();
    }

    string? Line;
    List<string> lines = new List<string>();

    using StreamReader reader = new StreamReader(inputStream);
    while ((line = reader.ReadLine()) != null)
    {
        lines.Add(line);
    }
    int index = -1;
    double previousYear = 0;
    string previousString = string.Empty;

    return lines.Select(line =>
    {
        string[] data = line.Split(‘,’);
        string currentString = data[0];
        double currentYear = Convert.ToDouble(data[1]);
        if (currentString != previousString)
        {
            if (currentYear == previousYear)
            {
                index = -1;
            }
            index = index + 1;
            previousString = currentString;
        }

        return new Model(index, data[0], currentYear, Convert.ToDouble(data[2]));
    });
}
```

Data on the populations of all nations is divided, filtered by year, and stored in the **dataCollection** field. Refer to the following code example.

```csharp
Public ViewModel()
{
    var models = new List<Model>(ReadCSV());
    dataCollection = new List<List<Model>>();
    StartYear = models.First().Xvalue;
    EndYear = models.Last().Xvalue;
    int count = 0;
    var previousData = new List<Model>();
    for (double I = StartYear; I <= EndYear; i++)
    {
        Ienumerable<Model> data = models.Where(x => x.Xvalue == i).OrderByDescending(x => x.Yvalue).Take(11);
        dataCollection.Insert(count, UpdateDataIndex(I, previousData, data));
        count++;
        previousData = data.ToList();
    }
            
    Data = dataCollection[0];
    Year = dataCollection[0].First().Xvalue;
}
```

## Step 3: Configure Syncfusion .NET MAUI Cartesian Charts

Next, configure the Syncfusion .NET MAUI Cartesian Charts control using this [documentation](https://help.syncfusion.com/maui/cartesian-charts/getting-started "Getting started with .NET MAUI Charts").

Refer to the following code example.

```xml
<chart:SfCartesianChart IsTransposed="True" >
 <chart:SfCartesianChart.Xaxes>
  <chart:NumericalAxis />        
 </chart:SfCartesianChart.Xaxes>

 <chart:SfCartesianChart.Yaxes>
  <chart:NumericalAxis />       
 </chart:SfCartesianChart.Yaxes>
</chart:SfCartesianChart>
```

## Step 4: Create a bar race chart

Let’s create a **BarRaceSegment** class inherited from the **ColumnSegment** class of .NET MAUI Cartesian Charts, using the segment’s **Draw** method to animate the bar race chart.

```csharp
public class BarRaceSegment : ColumnSegment
{

    protected override void Draw(ICanvas canvas)
    {
    
      // Do animating bar racing customization here
      var series = (Series as BarRaceSeries);

      if (series != null)
      {
        var index = series.ChartSegments.IndexOf(this);

        if (index >= 0 && series.PreviousSegments != null)
        {
            var previousSegment = series.PreviousSegments[index] as BarRaceSegment;
            if (previousSegment != null)
            {
                float previousTop = previousSegment.Top;
                float previousBottom = previousSegment.Bottom;
                float previousLeft = previousSegment.Left;
                float previousRight = previousSegment.Right;

                if (AnimatedValue > 0)
                {
                    float rectTop = GetColumnDynamicAnimationValue(AnimatedValue, previousTop, Top == 0 ? previousTop : Top);
                    float rectBottom = GetColumnDynamicAnimationValue(AnimatedValue, previousBottom, Bottom);
                    float rectLeft = GetColumnDynamicAnimationValue(AnimatedValue, previousLeft, Left);
                    float rectRight = GetColumnDynamicAnimationValue(AnimatedValue, previousRight, Right);

                    if (!float.IsNaN(rectLeft) && !float.IsNaN(rectTop) && !float.IsNaN(rectRight) && !float.IsNaN(rectBottom))
                    {

                        canvas.Alpha = Opacity;
                        CornerRadius cornerRadius = series.CornerRadius;

                        var rect = new Rect() { Left = rectLeft, Top = rectTop, Right = rectRight, Bottom = rectBottom };

                        canvas.SetFillPaint(AnimatedValue >= 0.5 ? Item.ItemColor : previousSegment.Item.itemColor, rect);

                        if (cornerRadius.TopLeft > 0 || cornerRadius.TopRight > 0 || cornerRadius.BottomLeft > 0 || cornerRadius.BottomRight > 0)
                        {
                            canvas.FillRoundedRectangle(rect, cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomLeft, cornerRadius.BottomRight);
                        }
                        else
                        {
                            canvas.FillRectangle(rect);
                        }

                        canvas.DrawString(xString, (float)rect.Left - 5, (float)Math.Round(rect.Center.Y), HorizontalAlignment.Right);

                        if (AnimatedValue == 1)
                            canvas.DrawString(Item.YValue.ToString("#,###,###,###"), (float)rect.Right + 2, (float)rect.Center.Y + 2, HorizontalAlignment.Left);
                        else
                            canvas.DrawString(GetColumnDynamicAnimationValue(AnimatedValue, previousSegment.Item.YValue, Item.YValue).ToString("#,###,###,###"), (float)rect.Right + 2, (float)rect.Center.Y + 2, HorizontalAlignment.Left);

                   }
              }
           }

        }
    }

    private float GetColumnDynamicAnimationValue(float animationValue, double oldValue, double currentValue)
    {
        if (!double.IsNaN(oldValue) && !double.IsNaN(currentValue))
        {
            return (float)((currentValue > oldValue) ?
                oldValue + ((currentValue - oldValue) * animationValue)
                : oldValue - ((oldValue - currentValue) * animationValue));
        }
        else
        {
            return double.IsNaN(oldValue) ? (float)currentValue * animationValue : (float)(oldValue - (oldValue * animationValue));
        }
    }

}
```

Then, create a **BarRaceSeries** class inherited from the **ColumnSeries** class of .NET MAUI Cartesian Charts to visualize the **DemoGraphics** statistic data.

```csharp
public class BarRaceSeries : ColumnSeries
{
    public List<ChartSegment> PreviousSegments { get; set; }

    public List<ChartSegment> ChartSegments { get; set; }

    public BarRaceSeries()
    {
        
    }

    protected override ChartSegment CreateSegment()
    {
        return new BarRaceSegment();
    }

}
```

## Step 5: Bind data to the bar race chart

To visualize the top 10 countries in the world by population statistics for a given year, implement the **BarRaceSeries** instance and bind the data to it.

Refer to the following code example.

```xml
<local:BarRaceSeries CornerRadius="0,100,0,100" EnableAnimation="True" ItemsSource="{Binding Data}" XBindingPath="Ranking" YBindingPath="YValue" >
</local:BarRaceSeries>
```

In the previous code example, we’ve bound the **Data** with the **ItemsSource** property. Then, the **XBindingPath** and **YBindingPath** are bound with the **Ranking** and **YValue** properties, respectively.

## Step 6: Customize the chart appearance

We can enhance the appearance of the chart by changing the axis elements and adding a title.

Refer to the following code example to customize the chart title.

```xml
<chart:SfCartesianChart.Title>
 <HorizontalStackLayout Margin="10,10,0,10">
  <Border Margin="10,0,0,10" Stroke="Transparent" BackgroundColor="DarkBlue" WidthRequest="20" HeightRequest="70"/>
  <VerticalStackLayout HeightRequest="70" Margin="10,0,0,10">
   <Label VerticalOptions="Start" Text="Demographic Statistics | Top 10 Populations in the World" Padding="10,5,5,0" FontSize="20" FontAttributes="Bold" />
   <Label VerticalOptions="Start" Text="Since 1960 to 2021" Padding="10,5,5,0" FontSize="15" FontAttributes="Bold" />
  </VerticalStackLayout>
 </HorizontalStackLayout>
</chart:SfCartesianChart.Title>
```

Then, configure the axis and modify the axis elements, as shown in the following code example.

```xml
<chart:SfCartesianChart.XAxes>
 <chart:NumericalAxis IsInversed="True" IsVisible="False" AutoScrollingDelta="10" AutoScrollingMode="Start" Interval="1" ShowMajorGridLines="False" />
</chart:SfCartesianChart.XAxes>
<chart:SfCartesianChart.YAxes>
 <chart:NumericalAxis PlotOffsetStart="110" LabelCreated="NumericalAxis_LabelCreated" RangePadding="AppendInterval" >
  <chart:NumericalAxis.MajorTickStyle>
   <chart:ChartAxisTickStyle TickSize="0" />
  </chart:NumericalAxis.MajorTickStyle>
  <chart:NumericalAxis.AxisLineStyle>
   <chart:ChartLineStyle StrokeWidth="0" />
  </chart:NumericalAxis.AxisLineStyle>
 </chart:NumericalAxis>
</chart:SfCartesianChart.YAxes>
```

## Step 7:  Configure the play/pause function for animating the bar race chart

Render a pause (play/stop) button and an interactive progress bar using the following code.

**XAML**

```xml
<Border Stroke="Transparent" Margin="20,0,0,10" Grid.Row="1" Padding="5">
 <Grid>
  <Grid.ColumnDefinitions>
   <ColumnDefinition Width="50" />
   <ColumnDefinition />
  </Grid.ColumnDefinitions>

  <Border Grid.Column="0" WidthRequest="45" HeightRequest="45" Stroke="Blue" BackgroundColor="White" >
   <Border.GestureRecognizers>
    <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
   </Border.GestureRecognizers>
   <Border.StrokeShape>
    <RoundRectangle CornerRadius="10" />
   </Border.StrokeShape>
   <Label x:Name="text" Text="{Binding ProgressString}" TextColor="Blue" HorizontalOptions="Center" VerticalOptions="Center" />
  </Border>
  <progress:SfLinearProgressBar Margin="10,20,0,0" Grid.Column="1" Minimum="{Binding StartYear}" Maximum="{Binding EndYear}" Progress="{Binding Year}" TrackHeight="10" TrackCornerRadius="5" ProgressHeight="10" ProgressCornerRadius="5" />
 </Grid>
</Border>
```

**C#**

```csharp
public void Pause()
{
     canStopTimer = true;
     ProgressString = "Play";
}

public async void Play()
{
     ProgressString = "Pause";
     await Task.Delay(500);

     if (Application.Current != null)
        Application.Current.Dispatcher.StartTimer(new TimeSpan(0, 0, 0, 1, 500), UpdateData);

        canStopTimer = false;
}
```

Finally, we can use the **BarRaceSeries'**  **ItemsSource** property to continuously update the chart with population data from the top 10 countries for each year.

```csharp
private bool UpdateData()
{
    if(dataCollection.Count < count + 1)
    {
        ProgressString = "Play";
        count = 0;
        return false;
    }

    if (canStopTimer) return false;

    MainThread.InvokeOnMainThreadAsync(() =>
    {
        Data = dataCollection[count];
        Year = dataCollection[count].First().XValue;
        count++;
    });

    return true;
}
```

After executing the previous code examples, the output will look like the following image.
<figure>

  <img src="https://www.syncfusion.com/blogs/wp-content/uploads/2023/12/Visualizing-the-top-10-populations-in-the-world-using-.NET-MAUI-dynamic-Bar-Race-Chart-1.gif" alt="Visualizing the top 10 populations in the world using .NET MAUI dynamic Bar Race Chart" style="width:100%">

  <figcaption>Visualizing the top 10 populations in the world using .NET MAUI dynamic Bar Race Chart</figcaption>

</figure>

## GitHub reference

For more details, refer to the [complete project on GitHub](https://github.com/SyncfusionExamples/Creating-a-.Net-Maui-Dynamic-Bar-Race-Charts-for-Top-10-World-s-Population "Creating a .NET MAUI Dynamic Bar Race Charts for Top 10 Worlds Population GitHub demo").

## Conclusion

Thanks for reading! In this blog, we’ve created a dynamic bar race chart to visualize the top 10 populations in the world using Syncfusion[.NET MAUI Cartesian Charts](https://www.syncfusion.com/maui-controls/maui-cartesian-charts ".NET MAUI Cartesian Charts"). We encourage you to follow the steps outlined in this blog and share your feedback in the comments section below.

If you require any assistance, please don’t hesitate to contact us via our [support forum](https://www.syncfusion.com/forums "Syncfusion Support Forum"), [support portal](https://support.syncfusion.com/ "Syncfusion Support Portal"), or [feedback portal](https://www.syncfusion.com/feedback "Syncfusion Feedback Portal"). We are always eager to help you!

## Related blogs

- [Syncfusion Essential Studio 2023 Volume 4 Is Here!](https://www.syncfusion.com/blogs/post/essential-studio-2023-volume-4.aspx "Blog: Syncfusion Essential Studio 2023 Volume 4 Is Here!")
- [Introducing the New .NET MAUI Carousel Control](https://www.syncfusion.com/blogs/post/dotnet-maui-carousel-control.aspx "Blog: Introducing the New .NET MAUI Carousel Control")
- [Introducing the New .NET MAUI Sunburst Chart Control](https://www.syncfusion.com/blogs/post/dotnet-maui-sunburst-chart.aspx "Blog: Introducing the New .NET MAUI Sunburst Chart Control")
- [Introducing the New .NET MAUI Switch Control](https://www.syncfusion.com/blogs/post/dotnet-maui-switch-control.aspx "Blog: Introducing the New .NET MAUI Switch Control")