TL;DR: Let’s see how to create a Bullet Chart using the .NET MAUI Linear Gauge. This chart is ideal for visualizing product sales performance for clear and intuitive data representation, making it easier to derive actionable insights. Steps include data collection, model and ViewModel class setup, layout organization, and customization.
Welcome to this week’s edition of the Chart of the Week blog series!
Today, we’ll explore a powerful data visualization technique: the Bullet Chart, built using Syncfusion .NET MAUI Linear Gauge. This chart is ideal for businesses and analysts comparing actual vs. target values, monitoring performance ranges, and tracking progress toward goals. Whether assessing sales revenue, efficiency, or other critical KPIs, integrating a Bullet Chart into your app enables clear and intuitive data representation, making it easier to derive actionable insights.
What is a Bullet Chart?
A Bullet Chart is a data visualization tool that can display key performance indicators in a compact format. It consists of a main bar representing actual performance, a marker for the target value, and qualitative background ranges indicating performance levels. Bullet charts are more space-efficient and informative compared to traditional gauges. They are commonly used in business intelligence dashboards to track metrics like revenue, sales, and efficiency. This makes them ideal for quick and clear performance analysis.
Key reasons to use a Bullet Chart
- Space-efficient: Bullet charts take up less space than traditional gauges or bar charts, making them ideal for dashboards with multiple metrics.
- Clear performance tracking: They effectively show actual performance against a target or benchmark, making it easy to assess progress.
- Enhanced comparisons: The inclusion of a reference marker allows for quick and clear comparisons between actual values and expected goals.
- Simplifies data interpretation: The use of qualitative background ranges (e.g., poor, average, good) helps users quickly understand performance levels.
- Ideal for business dashboards: Bullet charts are widely used in business intelligence (BI) tools to track KPIs like sales, revenue, and efficiency.
Use cases of Bullet Charts
- Sales performance tracking: Compare actual sales revenue against targets to evaluate business growth.
- Financial analysis: Monitor key financial metrics like profit margins, expenses, and budget utilization.
- Employee performance metrics: Assess productivity, efficiency, and goal achievement of employees or teams.
- Customer satisfaction and retention: Measure Net Promoter Score (NPS) or customer feedback against benchmarks.
- Project progress monitoring: Track project completion percentage versus expected timelines and goals.
The following image shows the bullet chart that we intend to create.
Step 1: Collect the data
To start our analysis, we’ll gather essential data needed to visualize product sales and performance effectively. We will use assumed sales performance data for this.
Step 2: Define the GaugeModel class
Now, define a GaugeModel class, which stores data for a product, including category, observed value, target value, and performance ranges (low, mid, high). It also includes an axis visibility property, defaulting to false. The constructor initializes all these values for each instance.
Refer to the following code example.
public class GaugeModel
{
public string Category { get; set; }
public double ObservedValue { get; set; }
public double TargetValue { get; set; }
public double LowRange { get; set; }
public double MidRange { get; set; }
public double HighRange { get; set; }
public bool IsAxisVisible { get; set; } = false;
public GaugeModel(string text, double actual, double target, double low, double mid, double high)
{
Category = text;
ObservedValue = actual;
TargetValue = target;
LowRange = low;
MidRange = mid;
HighRange = high;
}
}
Step 3: Define the ViewModel class
Next, create a ViewModel class with a property named BulletChartData to store product performance data. Populate the collection with sample data and bind it to the UI, ensuring dynamic updates when the data changes.
Refer to the following code example.
internal class ViewModel
{
public ObservableCollection<GaugeModel> BulletChartData { get; set; }
public ViewModel()
{
BulletChartData = new ObservableCollection<GaugeModel>()
{
new Model("Product A", 190, 220, 150, 180, 210, false),
new Model("Product B", 175, 180, 130, 160, 190, false),
new Model("Product C", 195, 200, 130, 170, 210, false),
new Model("Product D", 205, 180, 140, 170, 210, false),
new Model("Product E", 215, 220, 150, 190, 220, false),
new Model("Product F", 185, 200, 140, 170, 210, false),
new Model("Product G", 208, 200, 150, 190, 220, true)
};
}
}
Step 4: Defining the layout
Next, create a structured layout to organize the UI elements:
- Use a Border element with rounded corners to frame the content.
- Inside the Border, define a Grid with two rows: one for the chart title and description and the other for the bullet chart itself.
This structured approach ensures a clean and visually appealing layout.
<Border StrokeShape="RoundRectangle 10" Stroke="Black" Margin="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
</Grid>
</Border>
Step 5: Configure the Syncfusion .NET MAUI Bullet Chart
To design the Bullet Chart, we’ll use the Syncfusion .NET MAUI Linear Gauge control by referring to this documentation. Enhance the chart by defining range indicators, target markers, bar pointers, and tick styles to create a visually intuitive performance comparison.
- The BindableLayout.ItemsSource of VerticalStackLayout is set to BulletChartData, which holds the performance data.
- Each item in the VerticalStackLayout consists of the following:
- .NET MAUI LinearGauge acting as the Bullet Chart.
- Bindings to key performance metrics such as ActualValues, TargetValues, and Range limits.
This setup ensures that the charts update in real-time when data changes occur.
<Grid Grid.Row="1">
<VerticalStackLayout BindableLayout.ItemsSource="{Binding BulletChartData}" x:Name="layout" Spacing="{OnPlatform Default=70, WinUI=40, Android=40}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<gauge:SfLinearGauge>
……
</gauge:SfLinearGauge>
</DataTemplate>
</BindableLayout.ItemTemplate>
</VerticalStackLayout>
</Grid>
Step 6: Customize the .NET MAUI Bullet Chart appearance
To enhance readability and improve data visualization, we’ll customize the following chart elements:
- Title
- Axis
- Tick style
- Range
- Bar Pointer
- Shape marker
- Content pointer
Adding the chart title
Providing context to the visualization helps users understand the insights presented in the Bullet Chart by summarizing its purpose clearly.
- Grid layout: Structures the header section for clear visualization.
- BoxView: Adds a vertical blue bar to visually separate the title.
- StackLayout: Includes the title and description labels to explain the chart’s purpose.
This section enhances clarity by summarizing what the visualization represents.
<Grid Grid.Row="0" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="{OnPlatform Android=68,Default=80,iOS=68}"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{OnPlatform Android=20, Default=40, iOS=15}"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<BoxView Grid.Column="0" Grid.RowSpan="2" BackgroundColor="Blue" Margin="0,-20,0,0" HeightRequest="45" WidthRequest="20" />
<StackLayout Grid.Column="1" Grid.Row="0" Margin="7,7,0,0">
<Label Text="Comparative Analysis of Product Performance" FontSize="{OnPlatform Android=15,Default=18,iOS=15}" FontAttributes="Bold"/>
<Label Text="{OnPlatform Default='Evaluation of Key Performance Metrics and Competitive Benchmarking Across Market Competitors.', Android='Evaluating Key Performance Metrics and Competitive Industry Benchmarks.', iOS='Evaluating Performance Metrics and Industry Benchmarks.'}" FontSize="{OnPlatform Android=10,Default=13,iOS=10}" Margin="0,2,0,0"/>
</StackLayout>
</Grid>
Axis customization
Refer to the following code example to customize the chart axis. Here, we’ll:
- Set the Interval property to define the gauge scaling (e.g., 20).
- Set the Maximum value to align with the highest performance metric (e.g., 220).
- Set the Orientation as Horizontal to render the scale in a left-right direction.
- Configure the MinorTicksPerInterval property to control minor tick visibility.
- Use the ShowLine, ShowLabels, and ShowTicks bindings to manage axis visibility.
<gauge:SfLinearGauge Interval="40" Maximum="240" Orientation="Horizontal" MinorTicksPerInterval="0" ShowLine="False" TickOffset="{OnPlatform Android=30, Default=25, iOS=15}" ShowLabels="{Binding IsAxisVisible}" ShowTicks="{Binding IsAxisVisible}">
……..
</gauge:SfLinearGauge>
Tick style customization
Modify the MajorTickStyle to set the stroke color and enhance the appearance of major ticks.
Refer to the code examples below.
<gauge:SfLinearGauge>
<gauge:SfLinearGauge.MajorTickStyle>
<gauge:LinearTickStyle Stroke="Black"/>
</gauge:SfLinearGauge.MajorTickStyle>
......
</gauge:SfLinearGauge>
Range customization
Let’s define the following three LinearRange elements for different performance levels:
- Low Range (e.g., Poor performance)
- Mid Range (e.g., Average performance)
- High Range (e.g., Excellent performance)
Then, assign distinct colors to each range for better differentiation.
Refer to the following code example.
<gauge:SfLinearGauge>
………….
<gauge:SfLinearGauge.Ranges>
<gauge:LinearRange EndValue="{Binding LowRange}"
StartWidth="{OnPlatform Android=60, Default=50, iOS=30}"
EndWidth="{OnPlatform Android=60, Default=50, iOS=30}"
Position="Cross" Fill="#668ae6"/>
<gauge:LinearRange StartValue="{Binding LowRange}"
EndValue="{Binding MidRange}"
StartWidth="{OnPlatform Android=60, Default=50, iOS=30}"
EndWidth="{OnPlatform Android=60, Default=50, iOS=30}"
Fill="#a6bfe6"
Position="Cross" />
<gauge:LinearRange StartValue="{Binding MidRange}"
EndValue="{Binding HighRange}"
StartWidth="{OnPlatform Android=60, Default=50, iOS=30}"
EndWidth="{OnPlatform Android=60, Default=50, iOS=30}"
Fill="#d9e0ff"
Position="Cross" />
</gauge:SfLinearGauge.Ranges>
…………..
</gauge:SfLinearGauge>
Bar pointer customization
Now, use a BarPointer to represent ObservedValues with a customized Fill and PointerSize.
<gauge:SfLinearGauge>
………
<gauge:SfLinearGauge.BarPointers>
<gauge:BarPointer Value="{Binding ObservedValue}"
PointerSize="{OnPlatform Android=8, Default=8, iOS=6}"
Fill="#003bcc"/>
</gauge:SfLinearGauge.BarPointers>
…………
</gauge:SfLinearGauge>
Shape marker customization
Next, use a LinearShapePointer to indicate TargetValues with a rectangle marker. You can also customize the marker’s shape, width, height, and position for better clarity.
<gauge:SfLinearGauge>
<gauge:SfLinearGauge.MarkerPointers>
<gauge:LinearShapePointer ShapeType="Rectangle"
Value="{Binding TargetValues}" Fill="#003f5c"
ShapeWidth="4" ShapeHeight="30" Position="Cross"/>
</gauge:SfLinearGauge.MarkerPointers>
</gauge:SfLinearGauge>
Content pointer customization
Now, use a LinearContentPointer to indicate Category in content. Then, adjust the Alignment, OffsetX, and OffsetY properties to enhance the readability and positioning of the chart.
<gauge:SfLinearGauge>
……
<gauge:SfLinearGauge.MarkerPointers>
……
<gauge:LinearContentPointer Value="{OnPlatform Android=4, iOS=5, Default=0}"
OffsetX="{OnPlatform Android=-10, iOS=-10, Default=-15}"
OffsetY="{OnPlatform Android=10, iOS=10, Default=10}"
Alignment="{OnPlatform Android=Start, iOS=Start, Default=Start}">
<gauge:LinearContentPointer.Content>
<Label Text="{Binding BindingContext.Category}"/>
</gauge:LinearContentPointer.Content>
</gauge:LinearContentPointer>
</gauge:SfLinearGauge.MarkerPointers>
</gauge:SfLinearGauge>
Tips for effective use of Bullet Chart
- Use clear labels: Ensure product names and performance metrics are easy to read for quick insights.
- Define meaningful ranges: Set low, mid, and high benchmarks that accurately represent performance.
- Apply distinct colors: Use contrasting colors for ranges, actual values, and targets to enhance clarity.
- Ensure consistent scaling: Use uniform scales across multiple charts for accurate comparisons.
Best practices for a Bullet chart
- Use clear and concise labels: Ensure all labels are readable and provide essential context for comparison.
- Choose meaningful ranges: Define appropriate benchmarks (low, mid, high) that reflect performance goals accurately.
- Maintain visual simplicity: Avoid unnecessary elements, keeping the chart clean and easy to interpret.
- Use effective color coding: Differentiate actual values, targets, and ranges with distinct, easily recognizable colors.
- Ensure scalability and consistency: Keep a uniform scale across multiple charts for meaningful data comparisons.
After executing the previous code examples, the output will look like the following image.
GitHub reference
For more details, refer to the visualizing product sales performance with the .NET MAUI Bullet Chart GitHub demo.
Conclusion
Thanks for reading! We’ve explored how to create a Bullet Chart using the Syncfusion .NET MAUI Linear Gauge control. We highly recommend following the steps outlined in this blog. Share your feedback in the comments section below.
If you need any assistance, please do not hesitate to contact us via our support forum, support portal, or feedback portal. We are always eager to help you!
Related Blogs
- Visualize Multi-Dimensional Data Using the .NET MAUI Parallel Coordinate Chart
- Save Chat History to Firebase Realtime Database using the .NET MAUI Chat Control
- Create 3D Column Charts in .NET MAUI to Display America’s Top 10 Sports
- Easily Bind SQLite Data to .NET MAUI ListView and Perform CRUD Actions
Top comments (0)