Racing Bars

This sample uses C# 13 preview features such as partial properties, it also uses features from the CommunityToolkit.Mvvm package, you can learn more about it here.

sample image

View model

using System;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using LiveChartsCore.Defaults;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.Themes;
using LiveChartsCore.Kernel;
using CommunityToolkit.Mvvm.Input;

namespace ViewModelsSamples.Bars.Race;

// this class is used to store the data for each pilot // mark
// it inherits from ObservableValue, this class implements // mark
// IChartEntity, so it can be used as a data source, it draws the // mark
// Value property as a bar in the chart. // mark
public class PilotInfo : ObservableValue
{
    public PilotInfo(string name, int value, SolidColorPaint paint)
    {
        Name = name;
        Paint = paint;
        Value = value;
    }

    public string Name { get; set; }
    public SolidColorPaint Paint { get; set; }
}

public partial class ViewModel : ObservableObject
{
    private readonly Random _r = new();

    public ViewModel()
    {
        _ = StartRace();
    }

    [ObservableProperty]
    public partial PilotInfo[] Data { get; set; } =
        Fetch();

    public bool IsReading { get; set; } =
        true;

    public Func<ChartPoint, string> LabelsFormatter { get; set; } =
        GetPilotName;

    [RelayCommand]
    public void OnPointMeasured(ChartPoint point)
    {
        // assign the pilot color to the bar

        var pilot = (PilotInfo?)point.Context.DataSource;
        if (point.Context.Visual is null || pilot is null) return;

        point.Context.Visual.Fill = pilot.Paint;
    }

    private static string GetPilotName(ChartPoint point)
    {
        var pilot = (PilotInfo?)point.Context.DataSource;
        return pilot is null ? string.Empty : pilot.Name;
    }

    private async Task StartRace()
    {
        await Task.Delay(1000);

        // to keep this sample simple, we run the next infinite loop
        // in a real application you should stop the loop/task when the view is disposed

        while (IsReading)
        {
            // do a random change to the data
            foreach (var item in Data)
                item.Value += _r.Next(0, 100);

            Data = [.. Data.OrderBy(x => x.Value)];

            await Task.Delay(100);
        }
    }

    private static PilotInfo[] Fetch()
    {
        // generate a different color for each pilot
        var paints = Enumerable.Range(0, 7)
            .Select(i => new SolidColorPaint(ColorPalletes.MaterialDesign500[i].AsSKColor()))
            .ToArray();

        return [
            new("Tsunoda",   500,  paints[0]),
            new("Sainz",     450,  paints[1]),
            new("Riccardo",  520,  paints[2]),
            new("Bottas",    550,  paints[3]),
            new("Perez",     660,  paints[4]),
            new("Verstapen", 920,  paints[5]),
            new("Hamilton",  1000, paints[6])
        ];
    }
}

XAML

<UserControl
    x:Class="WinUISample.Bars.Race.View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:lvc="using:LiveChartsCore.SkiaSharpView.WinUI"
    xmlns:vms="using:ViewModelsSamples.Bars.Race"
    mc:Ignorable="d">

    <UserControl.DataContext>
        <vms:ViewModel/>
    </UserControl.DataContext>

    <lvc:CartesianChart TooltipPosition="Hidden">
        <lvc:CartesianChart.Series>
            <lvc:SeriesCollection>
                <lvc:XamlRowSeries
                    Values="{Binding Data}"
                    DataLabelsFormatter="{Binding LabelsFormatter}"
                    ShowDataLabels="True"
                    DataLabelsPosition="End"
                    DataLabelsTranslate="{lvc:Point Value='-1,0'}"
                    MaxBarWidth="50"
                    Padding="10"
                    PointMeasuredCommand="{Binding PointMeasuredCommand}"/>
            </lvc:SeriesCollection>
        </lvc:CartesianChart.Series>

        <lvc:CartesianChart.XAxes>
            <lvc:AxesCollection>
                <lvc:XamlAxis SeparatorsPaint="{lvc:SolidColorPaint Color='#dcdcdc'}"/>
            </lvc:AxesCollection>
        </lvc:CartesianChart.XAxes>

        <lvc:CartesianChart.YAxes>
            <lvc:AxesCollection>
                <lvc:XamlAxis IsVisible="False"/>
            </lvc:AxesCollection>
        </lvc:CartesianChart.YAxes>
    </lvc:CartesianChart>

</UserControl>

Articles you might also find useful: