A small, copy-friendly squarified treemap control for .NET MAUI. The control
uses GraphicsView and IDrawable; every tile is calculated algorithmically
and painted on a canvas. It does not build the treemap from Grid, Border,
or child-view tiles.
- Uses the squarify worst-aspect-ratio heuristic to calculate every rectangle.
- Preserves proportional area: the numeric value controls the tile area.
- Recalculates the complete layout whenever the available view size changes.
- Draws spacing, rounded corners, borders, selection, values, and labels.
- Scales typography from each tile's area and shortest side.
- Selects readable light or dark text independently for every tile.
- Supports bindings, observable collections, selection, tap events, and commands.
- Has no package dependency beyond the normal .NET MAUI project references.
- .NET 10 SDK
- The .NET MAUI workload
- The platform tooling for the target you want to run, such as Xcode for iOS and Mac Catalyst or the Android SDK for Android
Install or restore the required workloads:
# Windows or macOS
dotnet workload install maui
# Linux (Android only)
dotnet workload install maui-android
dotnet workload restore SquarifiedTreemap.Sample.csprojgit clone https://github.com/darthza/SquarifiedTreemapMaui.git
cd SquarifiedTreemapMaui
dotnet workload restore SquarifiedTreemap.Sample.csprojBuild Android:
dotnet build SquarifiedTreemap.Sample.csproj \
-f net10.0-androidBuild for an Apple Silicon iOS Simulator:
dotnet build SquarifiedTreemap.Sample.csproj \
-f net10.0-ios \
-p:RuntimeIdentifier=iossimulator-arm64Build Mac Catalyst on Apple Silicon:
dotnet build SquarifiedTreemap.Sample.csproj \
-f net10.0-maccatalyst \
-p:RuntimeIdentifier=maccatalyst-arm64You can also open SquarifiedTreemap.Sample.csproj in your normal .NET MAUI
IDE and select an installed simulator, emulator, or desktop target.
If Apple code signing reports Finder metadata, the checkout is probably in an iCloud or File Provider-backed folder. Move it to a normal local folder before running the Apple target.
Copy these two files into your MAUI project:
Controls/SquarifiedTreemap.cs— the control, item model, drawing, text, selection, and tap handling.Controls/SquarifiedTreemapLayout.cs— the squarified layout algorithm.
Change the SquarifiedTreemap.Controls namespace in both files to your app's
controls namespace. No registration in MauiProgram.cs is required.
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:MyApp.Controls">
<controls:SquarifiedTreemap
ItemsSource="{Binding TreemapItems}"
SelectedItem="{Binding SelectedTreemapItem, Mode=TwoWay}"
ItemTappedCommand="{Binding OpenTreemapItemCommand}"
TreemapPadding="0"
ItemSpacing="4"
CornerRadius="6"
ScaleTextToFit="True"
HorizontalOptions="Fill"
VerticalOptions="Fill"
SemanticProperties.Description="Portfolio allocation treemap" />
</ContentPage>Example data:
using System.Collections.ObjectModel;
using Microsoft.Maui.Graphics;
using MyApp.Controls;
public ObservableCollection<TreemapItem> TreemapItems { get; } = new()
{
new(
"Equities",
40,
color: Color.FromArgb("#4611AE"),
valueText: "40%"),
new(
"Bonds",
25,
color: Color.FromArgb("#00A5DB"),
valueText: "25%"),
new(
"Cash",
18,
color: Color.FromArgb("#C6B6E9"),
valueText: "18%"),
new(
"Options",
11,
color: Color.FromArgb("#C2E942"),
valueText: "11%"),
};Value always determines area. ValueText only changes presentation, such as
adding a percent sign. Leave TextColor unset to let the control choose a
high-contrast color from the tile background. Per-item text colors and base
font sizes remain available when a design requires explicit overrides.
Or create the control directly in C#:
var treemap = new SquarifiedTreemap
{
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Fill,
ItemsSource = new[]
{
new TreemapItem("A", 50),
new TreemapItem("B", 30),
new TreemapItem("C", 20),
},
};
treemap.ItemTapped += (_, eventArgs) =>
Debug.WriteLine(
$"{eventArgs.Item.Label}: {eventArgs.Item.Value}");| Property | Default | Purpose |
|---|---|---|
ItemsSource |
null |
Sequence of TreemapItem values. |
SelectedItem |
null |
Two-way bindable selected item. |
ItemTappedCommand |
null |
Command executed with the tapped item. |
TreemapPadding |
2 |
Padding around the complete treemap. |
ItemSpacing |
2 |
Visual gap between adjacent tiles. |
CornerRadius |
4 |
Tile corner radius. |
ScaleTextToFit |
true |
Scales value and label text from tile geometry. |
TextColor |
null |
Global override; null enables automatic contrast. |
ShowValues / ShowLabels |
true |
Controls the two text lines. |
ValueFormat |
"N0" |
Format used when ValueText is not supplied. |
SelectionStrokeWidth |
3 |
Width of the selected-tile outline. |
ItemTapped and SelectionChanged events are also available. Assign
Palette in C# to replace the built-in colors, and call Refresh() when a
non-observable item sequence changes in place.
- Items are laid out from largest to smallest; equal values retain input order.
- Zero, negative,
NaN, and infinite values are ignored. - The layout normalizes areas to the current canvas and guards against numeric overflow and underflow.
- Spacing is applied after the proportional rectangles are calculated, so the numeric layout remains faithful to the supplied values.
ObservableCollection<TreemapItem>additions and removals trigger a redraw.TreemapItemitself is immutable; replace an item when its value changes.- Values and labels are clipped to their tile. When space is tight, the label is hidden before the value is allowed to become unreadably small.
The implementation follows the classic squarified-treemap approach described by Mark Bruls, Kees Huizing, and Jarke J. van Wijk in Squarified Treemaps. It greedily builds rows while the worst rectangle aspect ratio improves, then places each completed row along the shortest side of the remaining bounds.
The control exposes a semantic description and supports keyboard-independent tap and command handling. Because the tiles are canvas drawings rather than native child controls, individual tiles are not separate screen-reader elements. Pair the treemap with an accessible list when every data item must be individually navigable.
Contributions are welcome. See CONTRIBUTING.md for the expected build and validation steps.
Licensed under the MIT License.
