Skip to content

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:

  1. BackgroundThinkGeoCloudVectorMapsOverlay (or similar tile service) at position 0.
  2. Static data — one or more LayerOverlay instances holding ShapeFileFeatureLayer or other large static datasets.
  3. Dynamic/result layersLayerOverlay instances with TileType.SingleTile for query results, highlight layers, drawn features.
  4. Interactive overlaysMapView.TrackOverlay and MapView.EditOverlay are 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);

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.