Tag Archive for WPF

ArcGIS–Getting the Legend Labels out

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.

ArcGIS–Getting the Legend Labels out

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.

WPF: Snooping Attached Properties

When developing WPF applications, Snoop is a wonderful tool that can let us see our visual tree at runtime, and find errors in data binding that are otherwise hard to track down in debugging. However, Snoop has an annoying limitation – it can’t show us data-bound Attached Properties. Let’s say we have the following XAML:

<Canvas>
   <Button Canvas.Left="{Binding Location.X}"
           Canvas.Top="{Binding Location.Y"}/>
Canvas>

Snooping this application won’t show Canvas.Left and Canvas.Top among the Button’s properties, since they’re not a part of the Button object. If we have a bug in our binding, we won’t be able to find it.

So what do we do? We can set various TraceLevel attributes to save the binding errors to a log, but a quick workaround during debugging is to bind our data, in addition to the attached properties, to the element’s Tag property, a generic object that can bind to anything, and allow us to Snoop the binding – and find out any problems:

<Canvas>
   <Button Canvas.Left="{Binding Location.X}"
           Canvas.Top="{Binding Location.Y}"
           Tag="{Binding Location}" />
Canvas>

This allowed me to find out, quickly, that my bug was simple – I had forgotten to make my Location property public, causing the binding to fail silently.