ArcGIS–Getting the Legend Labels out
- by Avner Kashtan
Working with ESRI’s ArcGIS package, especially the WPF API, can be confusing. There’s the REST API, the SOAP APIs, and the WPF classes themselves, which expose some web service calls and information, but not everything. With all that, it can be hard to find specific features between the different options. Some functionality is handed to you on a silver platter, while some is maddeningly hard to implement. Today, for instance, I was working on adding a Legend control to my map-based WPF application, to explain the different symbols that can appear on the map. This is how the legend looks on ESRI’s own map-editing tools: but this is how it looks when I used the Legend control, supplied out of the box by ESRI: Very pretty, but unfortunately missing the option to display the name of the fields that make up the symbology. Luckily, the WPF controls have a lot of templating/extensibility points, to allow you to specify the layout of each field: 1: <esri:Legend>
2: <esri:Legend.MapLayerTemplate>
3: <DataTemplate>
4: <TextBlock Text="{Binding Layer.ID}"/>
5: </DataTemplate>
6: </esri:Legend.MapLayerTemplate>
7: </esri:Legend>
but that only replicates the same built in behavior. I could now add any additional fields I liked, but unfortunately, I couldn’t find them as part of the Layer, GraphicsLayer or FeatureLayer definitions. This is the part where ESRI’s lack of organization is noticeable, since I can see this data easily when accessing the ArcGis Server’s web-interface, but I had no idea how to find it as part of the built-in class. Is it a part of Layer? Of LayerInfo? Of the LayerDefinition class that exists only in the SOAP service?
As it turns out, neither. Since these fields are used by the symbol renderer to determine which symbol to draw, they’re actually a part of the layer’s Renderer. Since I already had a MyFeatureLayer class derived from FeatureLayer that added extra functionality, I could just add this property to it:
1: public string LegendFields
2: {
3: get
4: {
5: if (this.Renderer is UniqueValueRenderer)
6: {
7: return (this.Renderer as UniqueValueRenderer).Field;
8: }
9: else if (this.Renderer is UniqueValueMultipleFieldsRenderer)
10: {
11: var renderer = this.Renderer as UniqueValueMultipleFieldsRenderer;
12: return string.Join(renderer.FieldDelimiter, renderer.Fields);
13: }
14: else return null;
15: }
For my scenario, all of my layers used symbology derived from a single field or, as in the examples above, from several of them. The renderer even kindly supplied me with the comma to separate the fields with. Now it was a simple matter to get the Legend control in line – assuming that it was bound to a collection of MyFeatureLayer:
1: <esri:Legend>
2: <esri:Legend.MapLayerTemplate>
3: <DataTemplate>
4: <StackPanel>
5: <TextBlock Text="{Binding Layer.ID}"/>
6: <TextBlock Text="{Binding Layer.LegendFields}" Margin="10,0,0,0" TextStyle="Italic"/>
7: </StackPanel>
8: </DataTemplate>
9: </esri:Legend.MapLayerTemplate>
10: </esri:Legend>
and get the look I wanted – the list of fields below the layer name, indented.