Saturday, September 3, 2011

MAKE ARCGIS SILVERLIGHT CONTROLS MVVM ENABLED - PART VI

Introduction


Before tackling the more advanced ArcGis Silverlight controls as the Editor, I will first look how we can enhance the ArcGis Silverlight controls so that they fit into the MVVM pattern.

Sometimes You will encounter ArcGis controls or classes with properties that cannot be set from within your ViewModel. When you try it, you will probably end up with a null pointer exception. The reason of this is that in order to have properties bound to you ViewModel, these properties must be inherit from the dependency property object. This is not always the case for the properties of an ArcGis Silverlight class, probably due technical reasons. The next article explains how we can enhance the class so that we can set non dependency properties from within the ViewModel pattern.

The dependency issue


If you have a property to be set, but you cannot do it from the ViewModel, you can add a new property to the class that inherit from the dependency object. You can use this property to set the value for a property that is not inherit from the dependency object. Let’s take an example of how we can do this by using the MeasureAction class from the ArcGis Sivlerlight API.

When you look at the examples from ESRI, you can use the measure action as

 <Button Content="Measure"  >
     <i:Interaction.Triggers>
       <i:EventTrigger EventName="Click">
         <esri:MeasureAction                                 
           AreaUnit="SquareMiles"
           DisplayTotals="{Binding Totals}"
           DistanceUnit="{Binding Distance}"
           MapUnits="Meters"
           MeasureMode="Polygon"                                  
           FillSymbol="{StaticResource DefaultFillSymbol}"
           TargetName="MyMap"/>
        </i:EventTrigger>
     </i:Interaction.Triggers>
 </Button>

 When we try to replace the property TargetName by the following expression :

             TargetName = “{Binding MyMap}”

You will get a null pointer exception, and that is because the property ‘TargetName’ is different implemented as the other properties. You can see this in the ESRI documentation shown next.




































You can see that properties as AreaUnit, MapUnits inherit from the dependency objet. When you look at the TargetName, this is inherited from an other object, in the case of the MeasureAction it is a Microsoft defined property. ESRI could solved it by implementing the map control by an own property inherit from the dependency object as this is the case for the Legend control. But for some technical reason this was not done.

To solve the problem,  simply add a new property ‘MapMeasure’ to the MeasureAction  that will contain the map control.

The map dependency property


Create a new class in the project with the MeasureAction that implement the dependency project. The code below show how this can  be done.

using System.Windows;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Actions;

namespace MyToolbar.Helper
{
  public class MapContent
  {
    public static readonly DependencyProperty MapMeasureProperty =
  DependencyProperty.RegisterAttached("MapMeasure"typeof(Map),
  typeof(MapContent), new PropertyMetadata(OnMapMeasureChanged));

    public static Map GetMapMeasure(DependencyObject depObject)
    {
      return (Map)depObject.GetValue(MapMeasureProperty);
    }

    public static void SetMapMeasure(DependencyObject depObject, Map value)
    {
      depObject.SetValue(MapMeasureProperty, value);
    }

    private static void OnMapMeasureChanged(DependencyObject depObject,
      DependencyPropertyChangedEventArgs e)
    {
      MeasureAction measureAction = depObject as MeasureAction;
      measureAction.TargetObject = GetMapMeasure(measureAction);
    }
  }
}

This is a typical implementation of a dependency property. The only code that you has to add is the code that is in the method On<property>Changed. In the case of the MeasureAction, the TargetObject is initialized with the map control. You can now in all modules activate the measure tool.

In the real world the XAML code will now look like  

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Click"
            <esri:MeasureAction                                 
                                    AreaUnit="SquareMeters"
                                    DisplayTotals="True"
                                    DistanceUnit="Meters"
                                    MapUnits="Meters"
                                    MeasureMode="Polygon"                                  
                                    FillSymbol="{StaticResource DefaultFillSymbol}"
                                    mapMeasure:MapContent.MapMeasure="{Binding MapControl}"
                        />
            </i:EventTrigger>
</i:Interaction.Triggers>

As you can see, now we have made the MeasureAction ViewModel enabled.

When at a given time the ArcGis Silverlight API add a new property ‘Map’ to the MeasureAction, you only has to change the property in the XAML, no code change is required. This is another example of the power of using the MVVM pattern.