14 June 2012

Behavior to make Bing Maps view area bindable

The Bing Maps control in Windows Phone 7 is pretty versatile but does not always play along very well with data binding. You can bind ZoomLevel and MapCenter, which is all very well, but if you want to know what area is actually shown within the map – for instance, for making a GIS application that loads only visible data, and not stuff that’s outside the view area anyway, things get a lit complicated.

Not so if you uses this little behavior. It builds upon the SafeBehavior that is already in my wp7nl library on codeplex. It sports two GeoCoordinate depencency propertise, NorthEast and SouthWest

using System.Device.Location;
using System.Windows;
using Microsoft.Phone.Controls.Maps;

namespace Wp7nl.Behaviors
{
  public class ViewportAreaBehavior
  {
    /// <summary>
    /// A behavior to make southwest and northeast bindable
    /// </summary>
    public class ViewportwatcherBehavior : SafeBehavior<Map>
    {
      #region NorthWest
      public const string NorthWestPropertyName = "NorthWest";

      public GeoCoordinate NorthWest
      {
        get { return (GeoCoordinate)GetValue(NorthWestProperty); }
        set { SetValue(NorthWestProperty, value); }
      }

      public static readonly DependencyProperty NorthWestProperty = 
	DependencyProperty.Register(
          NorthWestPropertyName,
          typeof(GeoCoordinate),
          typeof(ViewportwatcherBehavior),
          new PropertyMetadata(null));

      #endregion

      #region SouthEast
      public const string SouthEastPropertyName = "SouthEast";

      public GeoCoordinate SouthEast
      {
        get { return (GeoCoordinate)GetValue(SouthEastProperty); }
        set { SetValue(SouthEastProperty, value); }
      }

      public static readonly DependencyProperty SouthEastProperty = 
	DependencyProperty.Register(
          SouthEastPropertyName,
          typeof(GeoCoordinate),
          typeof(ViewportwatcherBehavior),
          new PropertyMetadata(null));

      #endregion
    }
  }
}

All pretty standard stuff. Next comes the setup/cleanup stuff,

protected override void OnSetup()
{
  AssociatedObject.ViewChangeEnd += AssociatedObjectViewChangeEnd;
  CalcRectangle();
}

protected override void OnCleanup()
{
  AssociatedObject.ViewChangeEnd -= AssociatedObjectViewChangeEnd;
}

in which you can see I intercept the ViewChangeEnd event. I don’t do anything special there: I just call the method CalcRectangle, just as on startup.

void AssociatedObjectViewChangeEnd(object sender, MapEventArgs e)
{
  CalcRectangle();
}

The final missing piece of the puzzle is then of course CalcRectangle itself:

private void CalcRectangle()
{
  NorthWest = AssociatedObject.ViewportPointToLocation(new Point(0, 0));
  SouthEast = AssociatedObject.ViewportPointToLocation(
    new Point(AssociatedObject.ViewportSize.Width, 
              AssociatedObject.ViewportSize.Height));
}

So I basically take the point left top (0,0) and and use ViewportPointToLocation to convert it to a coordinate, and do the same think with the point that left bottom. And that’s all.

Now you can bind to the NorthWest and SouthEast property of the behavior and you get the view area in your view model. Be aware this works one way. It gets the view area to the ViewModel, not the other way around.

Contrary to my custom there’s no solution with the code in it, mostly because it’s already in the wp7nl library since yesterday. You can download the code from the library itself here, if you want.

No comments: