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.
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>