Hey Forms Community.
For the past several months I've been neck deep in maps, and I've nearly re-written Xamarin.Forms.Maps entirely. I'm trying to gauge the demand for truly bindable maps, and if I should submit this stuff back to Xamarin.
Features:
- BindableMarker (all properties are bindable)
public Xamarin.Forms.Color Color;
... yes that's right, full RGB marker support for markerspublic event MarkerSelected;
public event MarkerDeselected;
- BindablePolygon (all properties are bindable)
public bool IsVisible;
public IEnumberable<IList<Position>> Holes;
public IEnumerable<Position> Area;
public Xamarin.Forms.Color FillColor;
public Xamarin.Forms.Color StrokeColor;
- NativeMarkerImage
public Xamarin.Forms.Color Color;
- a View with a custom renderer allowing you to extract the native map pin into your XAML code.
- BindableMap
- Lots and lots. The highlights here are the fact that markers are truly bindable without leveraging the native map's
clear();
methods. This allows us to keep callouts open and keep unchanging markers on the view at all times. - Has a
CalloutTemplate
that allows us to write our callouts in Xaml (see example below) - Fixes
VisibleRegion
to allow us to set the visible region from the ViewModel (two way binding). public event EventHandler<MapBoundsChangedEventArgs> BoundsChanged;
public event EventHandler MapLoaded;
public event EventHandler MarkersChanging;
public event EventHandler PolygonsChanging;
public event EventHandler MarkersChanged;
public event EventHandler PolygonsChanged;
public event EventHandler MapClicked;
- LatLongBounds - new class that should be part of
MapSpan
- returns the NorthEast and the SoutWest corners of the visible map
public bool Contains(IEnumerable<Position>);
- might expose
CalculateCentroid();
- Lots and lots. The highlights here are the fact that markers are truly bindable without leveraging the native map's
side note: I've completely done away the the MessagingCenter
as I'm really not a fan. I have hard event subscriptions between the PCL and the native, and they're not exposed to you... just to keep it clean.
note: the video above is on iOS, but it works equally as well on Android... even the native Android markers and a fully bindable callout... and yes, even buttons on the callout work for Android. - what a PITA that was.
Here's the xaml for a map
<rdr:ExtendedMap x:Name="StreetlightsMap"
HorizontalOptions="FillAndExpand"
MapClicked="StreetlightsMap_OnMapClicked"
MapLoaded="StreetlightsMap_OnMapLoaded"
VerticalOptions="FillAndExpand"
MarkerSource="{Binding Streetlights}"
MapBounds="{Binding MapBounds}"
VisibleRegion="{Binding VisibleRegion}"
ServiceArea="{Binding ServiceArea}"
HideOutOfViewElements="False"
AllowRotation="False"
MarkerAlignmentForCallout="Bottom"
MapType="{Binding MapType}"
RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1 }"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1 }">
<rdr:ExtendedMap.Behaviors>
<behaviors:MapBoundsChangedCommandBehavior Command="{Binding GetStreetlightsCommand}"
CommandParameter="{Binding .}" />
</rdr:ExtendedMap.Behaviors>
<rdr:ExtendedMap.CalloutTemplate>
<DataTemplate>
<controls:StreetlightMarkerCallout />
</DataTemplate>
</rdr:ExtendedMap.CalloutTemplate>
<!-- Sets the initial map location.-->
<x:Arguments>
<maps:MapSpan>
<x:Arguments>
<!-- These coordinates land near Red Deer at a fairly high altitude (birds eye view). -->
<maps:Position>
<x:Arguments>
<!-- Latitude-->
<x:Double>52.325</x:Double>
<!-- Longitude-->
<x:Double>-113.9</x:Double>
</x:Arguments>
</maps:Position>
<!-- Elevation lat/long-->
<x:Double>.8</x:Double>
<x:Double>.8</x:Double>
</x:Arguments>
</maps:MapSpan>
</x:Arguments>
</rdr:ExtendedMap>
and here's the xaml for a callout
<?xml version="1.0" encoding="utf-8"?>
<customMap:BindableMapCallout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:loc="clr-namespace:MyApp.Mobile.Localization;assembly=MyApp.Mobile"
xmlns:customMap="clr-namespace:MyApp.Mobile.CustomMap;assembly=MyApp.Mobile"
x:Class="MyApp.Mobile.Controls.StreetlightMarkerCallout"
Title="{loc:Translate StreetlightCallout_Title}">
<StackLayout Padding="0">
<!-- Address-->
<Label Text="{loc:Translate StreetlightCallout_Address}"
Style="{StaticResource Common-BoldText}" />
<!-- this stupidity exists because the iOS callout doesn't auto-resize... yet. -->
<Label Text="{Binding Address}" HeightRequest="45" WidthRequest="200">
<Label.IsVisible>
<OnPlatform x:TypeArguments="x:Boolean" Android="False" iOS="True" />
</Label.IsVisible>
</Label>
<Label Text="{Binding Address}">
<Label.IsVisible>
<OnPlatform x:TypeArguments="x:Boolean" Android="True" iOS="False" />
</Label.IsVisible>
</Label>
<Label Text="Repair Requested Date:"
Style="{StaticResource Common-BoldText}"
IsVisible="{Binding IsOut}" />
<Label Text="{Binding RepairRequestedDate}"
IsVisible="{Binding IsOut}" />
<!--Information-->
<Label Text="{loc:Translate StreetlightCallout_Info}"
Style="{StaticResource Common-BoldText}" />
<!-- this stupidity exists because the iOS callout doesn't auto-resize... yet. -->
<Label Text="{Binding Info}" HeightRequest="65" WidthRequest="220">
<Label.IsVisible>
<OnPlatform x:TypeArguments="x:Boolean" Android="False" iOS="True" />
</Label.IsVisible>
</Label>
<Label Text="{Binding Info}">
<Label.IsVisible>
<OnPlatform x:TypeArguments="x:Boolean" Android="True" iOS="False" />
</Label.IsVisible>
</Label>
<!-- Report Button-->
<Button Text="{loc:Translate StreetlightCallout_RepairButtonText}"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
Command="{Binding ReportStreetlightCommand}"
BorderRadius="0"
BorderWidth="0"
CommandParameter="{Binding .}"
IsVisible="{Binding SubmitButtonVisible}" />
</StackLayout>
</customMap:BindableMapCallout>