LayerOverlay Guide (WPF & WinForms)¶
Namespace: ThinkGeo.UI.Wpf / ThinkGeo.UI.WinForms
Applies to: ThinkGeo Desktop (WPF and WinForms)
Overview¶
LayerOverlay is the fundamental container for displaying map data in ThinkGeo Desktop applications. Every feature layer, raster layer, or image layer that you want to show on the map must be placed inside a LayerOverlay, which is then added to MapView.Overlays.
The overlay acts as a rendering unit — when you call RefreshAsync() on a LayerOverlay, only the layers inside that overlay are redrawn, leaving everything else on the map untouched. This makes targeted partial refreshes both simple and efficient.
A typical map is built from several LayerOverlay instances stacked in draw order: a cloud background tile overlay at the bottom, one or more data overlays in the middle, and dynamic or result overlays on top.
Construction and Adding to the Map¶
LayerOverlay is constructed directly with new. There is no factory method. After construction, set any properties you need, add your layers, and then add the overlay to MapView.Overlays.
// Minimal setup
var layerOverlay = new LayerOverlay();
layerOverlay.Layers.Add("myLayer", myFeatureLayer);
MapView.Overlays.Add("myOverlay", layerOverlay);
Both Layers.Add and Overlays.Add accept an optional string key. Always supply a key when you will need to look up the overlay or layer by name later.
WPF XAML Declaration¶
In WPF, LayerOverlay can be declared directly in XAML as a child of MapView.Overlays, with layers nested inside LayerOverlay.Layers:
<tgWpf:MapView x:Name="MapView" ...>
<tgWpf:MapView.Overlays>
<tgWpf:LayerOverlay TileType="SingleTile">
<tgWpf:LayerOverlay.Layers>
<tgCore:GeoImageLayer ImagePathFilename="Data/background.jpg"
CenterPoint="POINT (0 0)"
Scale="50000" />
</tgWpf:LayerOverlay.Layers>
</tgWpf:LayerOverlay>
<tgWpf:LayerOverlay TileType="SingleTile" Opacity="0">
<tgWpf:LayerOverlay.Layers>
<tgCore:GeoImageLayer ImagePathFilename="Data/detail.jpg"
CenterPoint="POINT (960 3140)"
Scale="5000" />
</tgWpf:LayerOverlay.Layers>
</tgWpf:LayerOverlay>
</tgWpf:MapView.Overlays>
</tgWpf:MapView>
Key Members¶
Properties¶
| Property | Type | Description |
|---|---|---|
Layers | GeoCollection<Layer> | The ordered collection of layers this overlay draws. Layers render bottom-to-top within the overlay. |
TileType | TileType | Controls how the overlay tiles its rendering. See the TileType section below. |
Opacity | float | Overall transparency of the overlay from 0 (fully transparent) to 1 (fully opaque). Default is 1. |
Name | string | An optional name for the overlay. Useful for identification when iterating MapView.Overlays. |
ThrowingExceptionMode | ThrowingExceptionMode | Controls whether drawing exceptions bubble up or are silently swallowed. |
Methods¶
| Method | Description |
|---|---|
RefreshAsync() | Redraws only this overlay. Use for targeted updates without redrawing the entire map. |
TileType¶
TileType is the most important configuration decision you make when creating a LayerOverlay. It controls how the overlay divides and caches its rendering.
TileType.SingleTile¶
The entire overlay is rendered as a single image covering the current map extent. The overlay redraws completely on every pan or zoom.
Use this for: - InMemoryFeatureLayer — data changes frequently; tile caching would show stale tiles. - Dynamic or result layers — spatial query results, highlight layers, edit overlays. - Geometric operation output layers. - Any overlay whose content changes in response to user interaction.
var resultsOverlay = new LayerOverlay();
resultsOverlay.TileType = TileType.SingleTile;
resultsOverlay.Layers.Add("Results", resultsLayer);
MapView.Overlays.Add("Results Overlay", resultsOverlay);
Default (MultipleTile)¶
When TileType is not set, the overlay uses the default tiled rendering mode. Tiles are fetched and cached individually as the user pans and zooms, matching the behaviour of standard web mapping tile services.
Use this for: - ShapeFileFeatureLayer serving as a background data layer. - Large static datasets that do not change during a session. - Layers where rendering from a tile cache is an acceptable trade-off for performance.
// No TileType set — defaults to MultipleTile
var backgroundOverlay = new LayerOverlay();
backgroundOverlay.Layers.Add("Zoning", zoningShapeFileLayer);
MapView.Overlays.Add("Background", backgroundOverlay);
Rule of thumb: If a layer's data can change while the map is displayed, use
TileType.SingleTile. If the data is static for the life of the session, the default tiled mode is fine.
Adding and Accessing Layers¶
Adding Layers¶
Layers.Add has two overloads — keyed and unkeyed:
// Keyed — recommended when you need to look up the layer later
layerOverlay.Layers.Add("Frisco Zoning", zoningLayer);
layerOverlay.Layers.Add("Query Results", resultsLayer);
// Unkeyed — fine for single-layer overlays or when no lookup is needed
layerOverlay.Layers.Add(mvtLayer);
Multiple layers in one overlay render in the order they were added — first-added renders at the bottom, last-added renders on top.
Retrieving Layers¶
Retrieve a layer by its string key with a cast:
var layerOverlay = (LayerOverlay)MapView.Overlays["myOverlay"];
var zoningLayer = (ShapeFileFeatureLayer)layerOverlay.Layers["Frisco Zoning"];
var resultsLayer = (InMemoryFeatureLayer)layerOverlay.Layers["Query Results"];
Index access also works when you know the layer's position:
var firstLayer = (GeoImageLayer)layerOverlay.Layers[0];
MapView.FindFeatureLayer¶
As an alternative to the two-step overlay-then-layer lookup, MapView.FindFeatureLayer searches all overlays and returns the first FeatureLayer with a matching key name:
// Searches all overlays — no need to know which overlay contains the layer
var zoningLayer = (ShapeFileFeatureLayer)MapView.FindFeatureLayer("Frisco Zoning");
This is especially useful in event handlers and helper methods that do not have a direct reference to the overlay.
Partial Refresh¶
Calling MapView.RefreshAsync() redraws every overlay on the map. For most interactive scenarios you only need to redraw the overlay whose data changed. Call RefreshAsync() directly on the overlay instead:
// WinForms — await in an async method
await layerOverlay.RefreshAsync();
// WPF — fire-and-forget from a synchronous event handler
_ = layerOverlay.RefreshAsync();
This is the canonical pattern throughout the HowDoI samples. After modifying layer data — clearing and re-adding features, changing styles, running a geometric operation — call RefreshAsync() on the specific overlay rather than on the whole map.
// Typical modify-then-refresh cycle
resultsLayer.InternalFeatures.Clear();
foreach (var feature in newResults)
resultsLayer.InternalFeatures.Add(feature);
await resultsOverlay.RefreshAsync();
Refreshing Multiple Overlays at Once¶
When you need to refresh several overlays together — for example, when flushing both TrackOverlay and EditOverlay along with your data overlay — pass an array to MapView.RefreshAsync:
await MapView.RefreshAsync(new Overlay[]
{
MapView.TrackOverlay,
MapView.EditOverlay,
layerOverlay
});
Opacity¶
Opacity controls the transparency of the entire overlay — all layers inside it are affected together. The value ranges from 0 (invisible) to 1 (fully opaque).
This is commonly used to fade overlays in and out based on zoom level or application state:
// Hide an overlay without removing it from the map
layerOverlay.Opacity = 0;
// Make it semi-transparent
layerOverlay.Opacity = 0.5f;
// Restore to full visibility
layerOverlay.Opacity = 1;
In WPF, Opacity can also be set as a XAML attribute — overlays that start invisible are declared with Opacity="0" and then animated or set in code-behind.
The ZoomToBlackHole sample shows a real-world use of Opacity driven by scale changes — each overlay fades in and out as the map zooms to show the appropriate level of detail:
// From a CurrentScaleChanged handler — fade overlay based on current scale
private void MapView_CurrentScaleChanged(object sender, CurrentScaleChangedMapViewEventArgs e)
{
foreach (var overlay in MapView.Overlays)
{
if (!(overlay is LayerOverlay layerOverlay)) continue;
if (!(layerOverlay.Layers[0] is GeoImageLayer geoImageLayer)) continue;
if (MapView.CurrentScale < geoImageLayer.LowerScale ||
MapView.CurrentScale > geoImageLayer.UpperScale)
{
layerOverlay.Opacity = 0;
continue;
}
// Fade in/out near the scale boundaries
var upperRatio = 1 - MapView.CurrentScale / geoImageLayer.UpperScale;
var lowerRatio = MapView.CurrentScale / geoImageLayer.LowerScale;
if (upperRatio < 0.4)
layerOverlay.Opacity = upperRatio * 2.5f;
else if (lowerRatio < 2)
layerOverlay.Opacity = lowerRatio / 2f;
else
layerOverlay.Opacity = 1;
}
}
Draw Order and Multiple Overlays¶
MapView.Overlays is an ordered collection. Overlays render in the order they were added — first-added renders at the bottom of the visual stack, last-added renders on top.
The standard layering pattern used across all HowDoI samples is:
- Background —
ThinkGeoCloudVectorMapsOverlay(or similar tile service) at position 0. - Static data — one or more
LayerOverlayinstances holdingShapeFileFeatureLayeror other large static datasets. - Dynamic/result layers —
LayerOverlayinstances withTileType.SingleTilefor query results, highlight layers, drawn features. - Interactive overlays —
MapView.TrackOverlayandMapView.EditOverlayare always on top (they manage their own z-order automatically).
// Typical multi-overlay setup
MapView.MapUnit = GeographyUnit.Meter;
// 1. Cloud background (bottom)
MapView.Overlays.Add(new ThinkGeoCloudVectorMapsOverlay { ... });
// 2. Static data layers
var zoningOverlay = new LayerOverlay();
zoningOverlay.TileType = TileType.SingleTile;
zoningOverlay.Layers.Add("Frisco Zoning", zoningLayer);
MapView.Overlays.Add("Frisco Zoning Overlay", zoningOverlay);
// 3. Query input shape (on top of data, below results)
var queryFeaturesOverlay = new LayerOverlay();
queryFeaturesOverlay.TileType = TileType.SingleTile;
queryFeaturesOverlay.Layers.Add("Query Feature", queryFeatureLayer);
MapView.Overlays.Add("Query Features Overlay", queryFeaturesOverlay);
// 4. Highlighted query results (topmost data layer)
var highlightedFeaturesOverlay = new LayerOverlay();
highlightedFeaturesOverlay.TileType = TileType.SingleTile;
highlightedFeaturesOverlay.Layers.Add("Highlighted Features", highlightedFeaturesLayer);
MapView.Overlays.Add("Highlighted Features Overlay", highlightedFeaturesOverlay);
Inserting at a Specific Position¶
Use Overlays.Insert to place an overlay at a specific index instead of appending it at the end. The ZoomToBlackHole sample uses this to always ensure the black background renders below everything else:
// Always place this overlay at position 0 (bottom of the stack)
MapView.Overlays.Insert(0, backgroundOverlay);
Storing a LayerOverlay as a Field¶
When an overlay needs to be accessed across multiple methods — for example, a background overlay that gets added and removed dynamically — store it as a field rather than retrieving it from MapView.Overlays by key each time:
// Field declaration
private LayerOverlay _backgroundOverlay;
// Initialization
private void CreateBackgroundOverlay()
{
_backgroundOverlay = new LayerOverlay();
_backgroundOverlay.TileType = TileType.SingleTile;
_backgroundOverlay.Layers.Add("Background", backgroundLayer);
MapView.Overlays.Insert(0, _backgroundOverlay);
}
// Later — add it back if it was removed
if (!MapView.Overlays.Contains(_backgroundOverlay))
{
MapView.Overlays.Insert(0, _backgroundOverlay);
_ = MapView.RefreshAsync();
}
MapView.Overlays.Contains accepts an overlay instance directly, so no key lookup is needed when you hold the reference.
Grouping Layers in One Overlay vs. Separate Overlays¶
Same overlay: Put layers in the same LayerOverlay when they logically belong together and always refresh as a unit. A base data layer and a result layer derived from it are natural candidates.
// One overlay refreshed together
var elevationFeaturesOverlay = new LayerOverlay();
elevationFeaturesOverlay.Layers.Add("Elevation Points Layer", elevationPointsLayer);
elevationFeaturesOverlay.Layers.Add("Drawn Shape Layer", drawnShapeLayer);
MapView.Overlays.Add("Elevation Features Overlay", elevationFeaturesOverlay);
// Both layers refresh together in one call
await elevationFeaturesOverlay.RefreshAsync();
Separate overlays: Put layers in different overlays when they refresh independently. This avoids the cost of redrawing a static base layer every time a small dynamic result layer changes.
// Static base data — rarely needs refreshing
var zoningOverlay = new LayerOverlay();
zoningOverlay.TileType = TileType.SingleTile;
zoningOverlay.Layers.Add("Frisco Zoning", zoningLayer);
MapView.Overlays.Add("Frisco Zoning Overlay", zoningOverlay);
// Dynamic results — refreshes frequently
var highlightOverlay = new LayerOverlay();
highlightOverlay.TileType = TileType.SingleTile;
highlightOverlay.Layers.Add("Highlighted Features", highlightedFeaturesLayer);
MapView.Overlays.Add("Highlighted Features Overlay", highlightOverlay);
// Only the highlight layer redraws — zoning data is untouched
await highlightOverlay.RefreshAsync();
ThrowingExceptionMode¶
The ThrowingExceptionMode property controls what happens when a layer inside the overlay throws during drawing. It appears in the ZoomToBlackHole sample for image overlays where rendering errors should be surfaced immediately:
var overlay = new LayerOverlay
{
TileType = TileType.SingleTile,
Opacity = opacity,
ThrowingExceptionMode = ThrowingExceptionMode.ThrowException
};
The default mode silently suppresses drawing exceptions, which is appropriate for production maps where a single layer failure should not crash the application. Use ThrowException during development to catch drawing problems early.
Common Pitfalls¶
1. Using Default TileType with InMemoryFeatureLayer¶
The default tiled rendering mode caches tiles. If you add features to an InMemoryFeatureLayer and the overlay is in default tile mode, the cached tiles will not show your changes until the tile cache expires. Always set TileType = TileType.SingleTile for any overlay containing dynamic data.
2. Calling MapView.RefreshAsync Instead of layerOverlay.RefreshAsync¶
MapView.RefreshAsync() redraws all overlays, including expensive background tile layers. When only one overlay's data has changed, call RefreshAsync() on that overlay directly. The HowDoI samples consistently use per-overlay refresh for every data modification.
3. Adding a Layer Without a Key When Lookup Is Needed¶
If you use Layers.Add(layer) without a key, you can only retrieve that layer by index (Layers[0]). This is fragile — if layers are ever reordered, the index breaks. Use the keyed overload Layers.Add("MyLayerKey", layer) for any layer you will need to retrieve by name.
4. Forgetting to Add the Overlay to the Map¶
Creating a LayerOverlay and adding layers to it does nothing visible until the overlay is added to MapView.Overlays. This is an easy step to miss, especially when refactoring initialization code.
// Easy to forget this line
MapView.Overlays.Add("myOverlay", layerOverlay);
Related Classes and Members¶
| Class / Member | Description |
|---|---|
MapView.Overlays | The ordered collection of all overlays on the map. LayerOverlay instances are added here. |
MapView.FindFeatureLayer(string key) | Searches all overlays and returns the first FeatureLayer with the given key. |
TileType | Enum controlling rendering mode: default (MultipleTile) or TileType.SingleTile. |
ThinkGeoCloudVectorMapsOverlay | The standard background tile overlay. Added to MapView.Overlays directly — not a LayerOverlay. |
MapView.TrackOverlay | Built-in singleton draw overlay. Always renders above all LayerOverlay instances. |
MapView.EditOverlay | Built-in singleton edit overlay. Always renders above all LayerOverlay instances. |
InMemoryFeatureLayer | The standard layer type for dynamic, in-session data. Always pair with TileType.SingleTile. |
ShapeFileFeatureLayer | The standard layer type for static file-based data. Compatible with the default tile mode. |