Skip To Content

Guidelines, best practices, tips, and tricks

In this topic

This topic provides suggestions for improving your development experience and describes some best practices when working with ArcGIS Runtime SDK for .NET.

Ensure required objects have been loaded

A common pitfall when working with asynchronous execution is the potential to refer to objects that are not yet available. When your page initializes, for example, you may have code that tries to manipulate a layer in the Map. If the layer has not been loaded, you're code is likely to encounter an exception. In an ArcGIS Runtime app, you'll often need to know things like:

  • Has a particular layer loaded?
  • Has a particular layer failed to load (and what is the exception)?
  • Have all layers loaded?
  • Is the Map ready?

Within the ArcGIS Runtime API there are events and async tasks which help determine when you can access functionality with predictable results and/or without exceptions. These are described in the following sections in the order in which they typically occur.

Has a particular layer loaded?

Use the MapView.LayerLoaded event to determine when a specific layer has loaded in the map or whether any layers failed to load (LayerLoadedEventArgs has a LoadError property which contains the exception). The following example illustrates handling this event for the map view.

<esri:MapView x:Name="MyMapView" LayerLoaded="MyMapView_LayerLoaded">
    <esri:Map>
        ...
    </esri:Map>
</esri:MapView>
private void MyMapView_LayerLoaded(object sender, LayerLoadedEventArgs e)
{
    if (e.LoadError == null)
        return;
    Debug.WriteLine(string.Format("Error while loading layer : {0} - {1}", 
        e.Layer.ID, e.LoadError.Message));
}
Tip:

If you use the ArcGIS Runtime project or item templates in Visual Studio, this event will be registered in XAML and contain an event handler in the code behind.

Have all layers been loaded?

The awaitable MapView.LayersLoadedAsync task can be used to make sure all layers have loaded before proceding with your code. The following example shows the appropriate pattern for using this method.

public MainPage()
{
    InitializeComponent();

    MyInitializeAfterLayerLoadedAsync();
}

private async void MyInitializeAfterLayerLoadedAsync()
{
    try
    {
        // await LayersLoadedAsync ... not storing return value here, just need to wait for completion
        // if needed, can store the List<LayerLoadedResult> object that's returned for additional info
        await MyMapView.LayersLoadedAsync();

        // can use an overload which takes a collection of specific layers to wait for
        // await MyMapView.LayersLoadedAsync(layersOfInterest);
    }
    catch(Exception ex)
    {
        // ... handle exceptions ... 
    }    

    // can safely continue with layer related functionality ...
}

Is the map ready?

The Map is ready for interaction, such as zooming to an area of interest, only after its spatial reference has been established. Use the MapView.SpatialReferenceChanged event to determine when this occurs, as shown in the following example.

<esri:MapView x:Name="MyMapView"
    LayerLoaded="MyMapView_LayerLoaded"
    SpatialReferenceChanged="MyMapView_SpatialReferenceChanged">
    <esri:Map>
        ...
    </esri:Map>
</esri:MapView>
private bool mapIsReady;
private void MyMapView_SpatialReferenceChanged(object sender, EventArgs e)
{
    // map is initialized with SpatialReference, set a flag to indicate it's ready for interaction
    this.mapIsReady = true;
}

Asynchronous code in a constructor

Asynchronous code that runs using the await keyword must be inside a function that is marked with the async keyword in its signature. Some functions, such as the constructor for your page or window, cannot be altered to be asynchronous. To run asynchronous code from such a function, you must call out to another function. The following example illustrates calling an asynchronous helper function from a constructor to do some initialization work.

// constructor
public MainPage()
{
    InitializeComponent();

    // Call async void method to do initialization or loading of data from constructor
    MyInitializeAsync();
}

// this is top level method that contains all asynchronous operations that are needed when UI loads
// (ok to return void)
private async void MyInitializeAsync()
{
    // Wrap initialization to try-catch block
    try
    {
        // if a lot of work is required, consider splitting functionality into separate tasks
        // and/or void methods (depending if it is async or not) 
        // for example ...
        await DoWorkAsync();
        var myWidget = await DoSomeMoreWorkAsync();
    }
    catch (Exception ex)
    {
        // ... code here to handle possible exceptions ...
    }
}

// this isn't a top level method so should return Task or Task<T>
private async Task DoWorkAsync()
{
    // ...
}

// this isn't a top level method so should return Task or Task<T>
private async Task<Widget> DoSomeMoreWorkAsync()
{
    // ...
}
Caution:

When possible, Task or Task<T> should be returned from your asynchronous functions. It is acceptable to return void from an asynchronous event handler or top-level function.

See the Asynchronous programming topic for more information.

Set an initial map extent

To set the initial extent (area of interest) of the map, use the Map.InitialViewpoint property which takes an object of type Viewpoint.

If setting in XAML use one of the two sub classes:

<esri:MapView x:Name="MyMapView">
  <esri:Map>
    <esri:Map.InitialViewpoint>
      <esri:ViewpointExtent XMin="-117.182686" 
                            YMin="32.695853" 
                            XMax="-117.133872" 
                            YMax="32.71853" 
                            SpatialReferenceID="4326" />
    </esri:Map.InitialViewpoint>
        <!-- ... -->
  </esri:Map>
</esri:MapView>

For setting the initial extent in the code behind, it's recommended to use the base Viewpointclass, which has similar constructor overloads.

// define a viewpoint with coords that define an extent and a spatial reference
mapView.Map.InitialViewpoint = new Viewpoint(new Envelope(-117.18268,32.69585,-117.13387,32.71853, SpatialReferences.Wgs84)); 

// define a viewpoint with a center point and scale denominator (1:50,000 in this example)
mapView.Map.InitialViewpoint = new Viewpoint(new MapPoint(-117.15,32.705,SpatialReferences.Wgs84), 50000);

Set the MapView spatial reference

The spatial reference of the map view is read-only and is determined by the spatial reference of the map it contains. This can either be set explicitly to force a specific spatial reference or will otherwise be taken from the first layer with a valid SpatialReference in the Map.Layers collection.

Note:

Setting the Map.SpatialReference property in XAML is not supported except when binding the property to an existing SpatialReference instance.

Specify spatial references

If you need a WGS1984 or Web Mercator Auxiliary Sphere spatial reference, you can use the SpatialReferences static convenience class which currently has two options: SpatialReferences.Wgs84 and SpatialReferences.WebMercator. These options will internally use the SpatialReference.Create(int wkid) method to return a spatial reference instance.

When specifying a value other than Web Mercator or WGS 1984 for a geometry's SpatialReference property, you should use the SpatialReference.Create method which will ensure instance re-use.

var myEnvelope = new Envelope(325466,673277,325617,673231); 
myEnvelope.SpatialReference = SpatialReference.Create(27700); // British National Grid

Note:

Spatial reference WKIDs can be found in the Geographic coordinate systems and Projected coordinate systems topics.

You should avoid creating new SpatialReference instances, for example: var sr = new SpatialReference(4326);.

Create geometry

There are a couple options for creating geometry. Use the method that is best suited for your requirements and for the geometry type with which you are working.

Note:

Geometry is immutable, which means geometry instances cannot be modified once created.

Create points

For map points, you should always use the MapPoint constructor to create new instances. The constructor requires x and y coordinates, and has several overrides that allow you to also specify a spatial reference, a z coordinate, or an m coordinate.

// create a new MapPoint
var myMapPoint = new MapPoint(-2.345, 45.67, new SpatialReferences.Wgs84);
For consistency with other geometry types, the API includes a MapPointBuilder that you can use to create a point. This provides the only way to create an empty MapPoint (undefined values for the x and y coordinates).
var myMapPoint = new MapPointBuilder().ToGeometry() // an empty MapPoint with NaN coordinate values

Create polylines and polygons

The constructors for Polyline and Polygon have several overrides that allow you to create a new geometry from a collection of points or segments. If the points or segments passed into the constructor have a spatial reference, then the new geometry will pick that up. Otherwise, you should specify a spatial reference in the constructor overload of Polygon or Polyline.

// create a polygon from a collection of vertices (MapPoints) and specify a spatial reference
var myPolygon = new Esri.ArcGISRuntime.Geometry.Polygon(mapPoints, SpatialReferences.Wgs84);

// create a polyline from a collection of segments that have a spatial reference defined (and will be used by the new polyline)
var myPolyline = new Esri.ArcGISRuntime.Geometry.Polyline(segments)

For large numbers of points, the PointCollection class provides efficient storage of coordinates and avoids the creation of many MapPoint instances. This provides an efficient way to build up a polygon or polyline from a large number of points, perhaps read from an external file or database, for example.

// create an empty point collection
var myPoints = new PointCollection(SpatialReferences.WebMercator);

// set the capacity if you know the maximum number of points to add
myPoints.Capacity = maxNumberOfPoints; 

// repeat adding vertex coordinates directly into the PointCollection (no MapPoint instances)
while(moPoints)
{
    // ... read x/y coordinate values ...

    myPoints.AddPoint(x,y); 

    // ... see if there are more ...
}

// pass the point collection to the Polygon constructor
var myPolygon = new Polygon(myPoints);
Note:

You can create a multipart polygon using a list of point collections (List<PointCollection>), where each PointCollection contains the vertices for a part (ring, in other words).

Update geometry

You can use the PolylineBuilder and PolygonBuilder classes to create and modify a new shape. The builder is best suited for editing workflows where the user may be adding, inserting or removing parts of a geometry interactively. Because Geometry is immutable, the builder provides a way to make changes to a working geometry. Use the ToGeometry method on PolylineBuilder or PolygonBuilder to get the polygon or polyline from the builder.

When updating or modifying existing geometries, use PolylineBuilder and PolygonBuilderclasses to modify the geometry. The easiest way is to pass an existing geometry to the constructor for the builder.

var polylineBuilder = new PolylineBuilder(polyline); // Create builder based on existing one
polylineBuilder.Parts[0].RemoveAt(0); 
polylineBuilder.AddPoint(8, 4); // adding a point to last part

polyline = polylineBuilder.ToGeometry(); // get updated geometry

XML namespace references

Be consistent with naming of XML namespace references in your XAML code. If you use the same names for these references in all your projects, you'll be able to copy and paste XAML between apps without having to modify the prefixes used to refer to namespaces. The ArcGIS Runtime for .NET samples use the following namespace designations.

xmlns:esri="using:Esri.ArcGISRuntime.Controls"
xmlns:layers="using:Esri.ArcGISRuntime.Layers"
xmlns:data="using:Esri.ArcGISRuntime.Data"
xmlns:symb="using:Esri.ArcGISRuntime.Symbology"
xmlns:geom="using:Esri.ArcGISRuntime.Geometry"
Tip:

If you're consistent with names for common objects in your UI (MapView, Map, layers, and so on), it will also make it easier to copy and paste .NET code between projects.

Load data on a Windows Phone (or emulator)

You may need to load data onto your Windows Phone for use with your ArcGIS Runtime SDK for .NET app. The following links describe tools that can be used to load a folder from your local hard drive onto your Windows Phone or emulator.

Related topics