ClassBreakStyle Guide¶
Namespace: ThinkGeo.Core
Applies to: All ThinkGeo products (WPF, WinForms, Blazor, MAUI, GIS Server, etc.)
Overview¶
ClassBreakStyle renders features using different styles depending on where a feature's numeric attribute value falls within a set of defined ranges. Each range — called a ClassBreak — specifies a lower threshold value and the style to apply when a feature's attribute is greater than or equal to that threshold. The style of the last matching break (the highest break that is still ≤ the feature's value) wins.
ClassBreakStyle is the standard approach for choropleth maps (polygon fill intensity driven by census or statistical data), graduated point symbol maps (marker size or color driven by a measured value at each point), and any other layer where you want continuous numeric data to drive discrete visual categories.
Constructors¶
There are two forms in the samples:
Constructor with column name¶
Passes the column name directly at construction time — the most concise form:
var classBreakStyle = new ClassBreakStyle("H_UNITS");
var classBreakStyle = new ClassBreakStyle("PH");
var classBreakStyle = new ClassBreakStyle("CellValue");
The string argument is the exact name of the feature attribute column whose value will be evaluated at render time.
Sources: wpfHowDoI:Samples/VectorDataStyling/RenderBasedOnClassBreaks.xaml.cs, wpfHowDoI:Samples/MapOfflineData/Vector/GenerateESRIGridFile.xaml.cs, blazorHowDoI:Shared/ThematicDemographicStyleBuilder.cs.
Default constructor + ColumnName property¶
Constructs an empty style and sets the column name as a separate step. Used when the column name is determined dynamically or when constructing inside a loop:
var classBreakStyle = new ClassBreakStyle();
classBreakStyle.ColumnName = "H_UNITS";
Sources: wpfHowDoI:Samples/ThinkGeoCloudIntegration/ColorUtilities.xaml.cs, winformHowDoI:Samples/ThinkGeoCloudIntegration/ColorUtilities.cs, mauiHowDoI:Samples/ThinkGeoCloudIntegration/ColorUtilitiesCloudServices.xaml.cs.
Properties¶
ColumnName¶
string — The feature attribute column whose value determines which ClassBreak is applied. Must match the exact column name from the layer's data source (case-sensitive). Set in the constructor or directly on the style object.
ClassBreaks¶
Collection<ClassBreak> — The ordered list of break definitions. Each ClassBreak holds a threshold Value and a style to apply when the feature's column value is ≥ that threshold. Add breaks in ascending order by Value. The collection is mutable and populated via .Add().
BreakValueInclusion¶
BreakValueInclusion enum — Controls whether the break value itself is included in the range above or below the break. The only observed value in the samples is BreakValueInclusion.IncludeValue, set in the Blazor thematic demographic builder when computing cluster breaks from actual data:
ClassBreakStyle classBreakStyle = new ClassBreakStyle(SelectedColumns[0])
{
BreakValueInclusion = BreakValueInclusion.IncludeValue
};
Source: blazorHowDoI:Shared/ThematicDemographicStyleBuilder.cs.
The ClassBreak Object¶
Each entry in ClassBreaks is a ClassBreak instance. Its two-argument constructor takes a double threshold value and a Style:
new ClassBreak(thresholdValue, style)
The style argument can be any concrete style type — AreaStyle, PointStyle, LineStyle, or any other style that inherits from Style.
After construction, the two most-used read-back properties on ClassBreak are:
Value— thedoublethreshold used for legend label formatting (e.g.,$">{classBreak.Value} units")DefaultAreaStyle— the style cast back as anAreaStyle, used when wiring upLegendItem.ImageStyle
Geometry Type Patterns¶
Polygon layers — AreaStyle¶
The dominant use case. Each break assigns a different fill color to polygon features:
var housingUnitsStyle = new ClassBreakStyle("H_UNITS");
var classBreakIntervals = new double[] { 0, 1000, 2000, 3000, 4000, 5000 };
var colors = GeoColor.GetColorsInHueFamily(GeoColors.Red, classBreakIntervals.Length).Reverse().ToList();
for (var i = 0; i < classBreakIntervals.Length; i++)
{
var classBreak = new ClassBreak(
classBreakIntervals[i],
AreaStyle.CreateSimpleAreaStyle(new GeoColor(192, colors[i]), GeoColors.White));
housingUnitsStyle.ClassBreaks.Add(classBreak);
}
layer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(housingUnitsStyle);
layer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
Sources: wpfHowDoI:Samples/VectorDataStyling/RenderBasedOnClassBreaks.xaml.cs, winformHowDoI:Samples/VectorDataStyling/RenderBasedOnClassBreaks.cs, mauiHowDoI:Samples/VectorDataStyling/CreateClassBreakStyle.xaml.cs.
Point layers — PointStyle¶
Each break assigns a different marker color to point features. The first break uses double.MinValue as the sentinel, which matches every feature not covered by any subsequent break — used here to assign a transparent style to values below the lowest explicit threshold:
var classBreakStyle = new ClassBreakStyle("PH");
const byte alpha = 180;
const int symbolSize = 10;
// Sentinel break — catches anything below 6.2 and makes it transparent
classBreakStyle.ClassBreaks.Add(new ClassBreak(double.MinValue,
new PointStyle(PointSymbolType.Circle, symbolSize, new GeoSolidBrush(GeoColors.Transparent))));
// Color-coded breaks by pH value
classBreakStyle.ClassBreaks.Add(new ClassBreak(6.2,
new PointStyle(PointSymbolType.Circle, symbolSize, new GeoSolidBrush(GeoColor.FromArgb(alpha, 255, 0, 0)))));
classBreakStyle.ClassBreaks.Add(new ClassBreak(6.83,
new PointStyle(PointSymbolType.Circle, symbolSize, new GeoSolidBrush(GeoColor.FromArgb(alpha, 255, 128, 0)))));
classBreakStyle.ClassBreaks.Add(new ClassBreak(7.0,
new PointStyle(PointSymbolType.Circle, symbolSize, new GeoSolidBrush(GeoColor.FromArgb(alpha, 245, 210, 10)))));
classBreakStyle.ClassBreaks.Add(new ClassBreak(7.08,
new PointStyle(PointSymbolType.Circle, symbolSize, new GeoSolidBrush(GeoColor.FromArgb(alpha, 225, 255, 0)))));
classBreakStyle.ClassBreaks.Add(new ClassBreak(7.15,
new PointStyle(PointSymbolType.Circle, symbolSize, new GeoSolidBrush(GeoColor.FromArgb(alpha, 224, 251, 132)))));
classBreakStyle.ClassBreaks.Add(new ClassBreak(7.21,
new PointStyle(PointSymbolType.Circle, symbolSize, new GeoSolidBrush(GeoColor.FromArgb(alpha, 128, 255, 128)))));
classBreakStyle.ClassBreaks.Add(new ClassBreak(7.54,
new PointStyle(PointSymbolType.Circle, symbolSize, new GeoSolidBrush(GeoColor.FromArgb(alpha, 0, 255, 0)))));
samplesLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(classBreakStyle);
samplesLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
Sources: wpfHowDoI:Samples/MapOfflineData/Vector/GenerateESRIGridFile.xaml.cs, winformHowDoI:Samples/MapOfflineData/Vector/GenerateESRIGridFile.cs.
Grid / raster cell layers — AreaStyle on GridFeatureLayer¶
ClassBreakStyle is also used on GridFeatureLayer (ESRI grid raster data rendered as cells). The column name is "CellValue" — the grid cell's interpolated value — and AreaStyle fills each cell:
var gridClassBreakStyle = new ClassBreakStyle("CellValue");
const byte alpha = 150;
gridClassBreakStyle.ClassBreaks.Add(new ClassBreak(double.MinValue,
new AreaStyle(new GeoSolidBrush(GeoColors.Transparent))));
gridClassBreakStyle.ClassBreaks.Add(new ClassBreak(6.2,
new AreaStyle(new GeoSolidBrush(GeoColor.FromArgb(alpha, 255, 0, 0)))));
gridClassBreakStyle.ClassBreaks.Add(new ClassBreak(6.83,
new AreaStyle(new GeoSolidBrush(GeoColor.FromArgb(alpha, 255, 128, 0)))));
// ... additional breaks ...
gridFeatureLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(gridClassBreakStyle);
gridFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
Source: wpfHowDoI:Samples/MapOfflineData/Vector/GenerateESRIGridFile.xaml.cs.
Dynamically Computed Breaks¶
When break thresholds are calculated from the actual feature data rather than specified as constants, the pattern is to read all feature values first, compute break points, then build the ClassBreakStyle:
// 1. Read all feature values from the source
featureSource.Open();
var featureCount = featureSource.GetCount();
double[] values = new double[featureCount];
for (int i = 0; i < featureCount; i++)
{
Feature feature = featureSource.GetFeatureById(
(i + 1).ToString(CultureInfo.InvariantCulture), SelectedColumns);
double.TryParse(feature.ColumnValues[SelectedColumns[0]], out values[i]);
}
featureSource.Close();
// 2. Build the ClassBreakStyle using computed thresholds
ClassBreakStyle classBreakStyle = new ClassBreakStyle(SelectedColumns[0])
{
BreakValueInclusion = BreakValueInclusion.IncludeValue
};
double[] classBreakValues = ComputeClusterBreaks(values, classBreaksCount - 1);
for (int i = 0; i < classBreakValues.Length; i++)
{
ClassBreak classBreak = new ClassBreak(
classBreakValues[i],
new AreaStyle(
new GeoPen(GeoColor.FromHtml("#f05133"), 1),
new GeoSolidBrush(new GeoColor(opacity, familyColors[i]))));
classBreakStyle.ClassBreaks.Add(classBreak);
}
Source: blazorHowDoI:Shared/ThematicDemographicStyleBuilder.cs.
Updating a Style at Runtime¶
When regenerating a ClassBreakStyle in response to user input (e.g., a new color family chosen from a cloud color API), clear the layer's existing CustomStyles before adding the new one:
// Get the layer from the overlay already on the map
var housingUnitsOverlay = (LayerOverlay)MapView.Overlays["Frisco Housing Units Overlay"];
var housingUnitsLayer = (ShapeFileFeatureLayer)housingUnitsOverlay.Layers["Frisco Housing Units"];
// Remove previous style
housingUnitsLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Clear();
// Build and add the new style
var classBreakStyle = new ClassBreakStyle();
classBreakStyle.ColumnName = "H_UNITS";
double[] intervals = { 0, 1000, 2000, 3000, 4000, 5000 };
for (var i = 0; i < colors.Count; i++)
{
var areaStyle = new AreaStyle(new GeoSolidBrush(colors[colors.Count - i - 1]));
classBreakStyle.ClassBreaks.Add(new ClassBreak(intervals[i], areaStyle));
}
housingUnitsLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(classBreakStyle);
housingUnitsLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
// Redraw — refresh the overlay, not the whole map, for efficiency
await housingUnitsOverlay.RefreshAsync();
Sources: wpfHowDoI:Samples/ThinkGeoCloudIntegration/ColorUtilities.xaml.cs, winformHowDoI:Samples/ThinkGeoCloudIntegration/ColorUtilities.cs, mauiHowDoI:Samples/ThinkGeoCloudIntegration/ColorUtilitiesCloudServices.xaml.cs.
Wiring Breaks to a LegendAdornmentLayer¶
The standard pattern for creating a synchronized legend is to build both the ClassBreak and its LegendItem in the same loop. classBreak.DefaultAreaStyle gives back the area style that was supplied to the constructor, and classBreak.Value gives the threshold for the label text:
var housingUnitsStyle = new ClassBreakStyle("H_UNITS");
var classBreakIntervals = new double[] { 0, 1000, 2000, 3000, 4000, 5000 };
var colors = GeoColor.GetColorsInHueFamily(GeoColors.Red, classBreakIntervals.Length).Reverse().ToList();
for (var i = 0; i < classBreakIntervals.Length; i++)
{
// Create break and style together
var classBreak = new ClassBreak(
classBreakIntervals[i],
AreaStyle.CreateSimpleAreaStyle(new GeoColor(192, colors[i]), GeoColors.White));
housingUnitsStyle.ClassBreaks.Add(classBreak);
// Mirror it in the legend — use classBreak.DefaultAreaStyle and classBreak.Value
var legendItem = new LegendItem()
{
ImageStyle = classBreak.DefaultAreaStyle,
TextStyle = new TextStyle(
$">{classBreak.Value} units",
new GeoFont("Verdana", 10),
GeoBrushes.Black)
};
legend.LegendItems.Add(legendItem);
}
layer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(housingUnitsStyle);
layer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
When updating the style at runtime, clear the legend's existing items before adding the new ones:
legend.LegendItems.Clear();
foreach (var classBreak in classBreaks)
{
var legendItem = new LegendItem
{
ImageStyle = classBreak.DefaultAreaStyle,
TextStyle = new TextStyle(
$">{classBreak.Value} units",
new GeoFont("Verdana", 10),
GeoBrushes.Black)
};
legend.LegendItems.Add(legendItem);
}
await MapView.AdornmentOverlay.RefreshAsync();
Sources: wpfHowDoI:Samples/VectorDataStyling/RenderBasedOnClassBreaks.xaml.cs, winformHowDoI:Samples/VectorDataStyling/RenderBasedOnClassBreaks.cs, mauiHowDoI:Samples/VectorDataStyling/CreateClassBreakStyle.xaml.cs, mauiHowDoI:Samples/ThinkGeoCloudIntegration/ColorUtilitiesCloudServices.xaml.cs.
Generating a Color Ramp with GeoColor.GetColorsInHueFamily¶
All polygon class-break samples use GeoColor.GetColorsInHueFamily to produce a graduated color sequence, then .Reverse() so that higher values get deeper colors:
var colors = GeoColor.GetColorsInHueFamily(GeoColors.Red, classBreakIntervals.Length)
.Reverse()
.ToList();
The Blazor thematic builder uses GeoColor.GetColorsInQualityFamily instead, which interpolates between a start and end color across a wheel direction:
Collection<GeoColor> familyColors = GeoColor.GetColorsInQualityFamily(
StartColor, EndColor, classBreakCount, ColorWheelDirection);
Sources: wpfHowDoI:Samples/VectorDataStyling/RenderBasedOnClassBreaks.xaml.cs, blazorHowDoI:Shared/ThematicDemographicStyleBuilder.cs.
Common Patterns and Pitfalls¶
Breaks must be added in ascending order. The engine evaluates breaks from lowest to highest and applies the style of the last break whose Value is ≤ the feature's attribute. If breaks are added out of order, features may render with the wrong style.
Use double.MinValue as the sentinel for an explicit "no match" style. If a feature's column value is below the first explicit threshold, no break technically applies. Adding a ClassBreak(double.MinValue, transparentStyle) as the first entry ensures those features are explicitly rendered as transparent (or any other "unclassified" style) rather than falling back to a default.
ClassBreakStyle reads column values as double. The column's raw string value is parsed to double at render time. Non-numeric or empty values will silently fail to match any break, and the feature will render without style. Ensure the column contains valid numeric data before applying ClassBreakStyle.
Always clear CustomStyles before replacing a style at runtime. If you add a second ClassBreakStyle without clearing, both styles render simultaneously, which usually produces unexpected visual layering. Call layer.ZoomLevelSet.ZoomLevel01.CustomStyles.Clear() before adding the new style.
classBreak.DefaultAreaStyle only works when the underlying style is an AreaStyle. When using PointStyle or LineStyle breaks, access the style through classBreak.DefaultPointStyle or classBreak.DefaultLineStyle respectively. Using DefaultAreaStyle on a PointStyle break returns null.
Source References¶
| Sample | Namespace | ClassBreakStyle usage |
|---|---|---|
VectorDataStyling/RenderBasedOnClassBreaks | WPF, WinForms | Polygon choropleth with H_UNITS; GeoColor.GetColorsInHueFamily; legend wiring via classBreak.DefaultAreaStyle and classBreak.Value |
VectorDataStyling/CreateClassBreakStyle | MAUI | Same pattern as above; MapView.MapScale + MapView.CenterPoint for initial extent |
MapOfflineData/Vector/GenerateESRIGridFile | WPF, WinForms | Point layer with PointStyle breaks on "PH" column; grid layer with AreaStyle breaks on "CellValue" column; double.MinValue sentinel |
ThinkGeoCloudIntegration/ColorUtilities | WPF, WinForms, MAUI | Runtime replacement of ClassBreakStyle in response to Cloud Color API; CustomStyles.Clear() + overlay-level RefreshAsync() |
Shared/ThematicDemographicStyleBuilder.cs | Blazor | Dynamically computed cluster breaks from feature data; BreakValueInclusion.IncludeValue; GeoColor.GetColorsInQualityFamily with ColorWheelDirection |