Search code examples
c#xamarinmvvmxamarin.formsmvvmcross

Bind constructor arguments to Xamarin.Forms' GoogleMaps


I develop simple Xamarin.Forms application with GoogleMaps using MVVMCross. My goal is to center maps' position to user's current location while launching it. Unfortunately I don't know how to bind these values to GoogleMaps' constructor. Right now it's just static value but I want to pass values of latitude and longitude from CurrentLocation object (which has these properties).

View:

<ContentPage.Content>
    <maps:Map MapType="Street" IsShowingUser="True" HasZoomEnabled="True">
        <x:Arguments>
            <maps:MapSpan>
                <x:Arguments>
                    <maps:Position>
                        <x:Arguments>
                            <x:Double>56.368533</x:Double>
                            <x:Double>3.258646</x:Double>
                        </x:Arguments>
                    </maps:Position>
                    <x:Double>0.01</x:Double>
                    <x:Double>0.01</x:Double>
                </x:Arguments>
            </maps:MapSpan>
        </x:Arguments>
    </maps:Map>
</ContentPage.Content>

Here's part of my ViewModel:

public class NearbyBollardsMapViewModel : MvxViewModel
{
    private Location _currentLocation;

    public Location CurrentLocation
    {
        get => _currentLocation;
        set
        {
            _currentLocation = value;
            RaisePropertyChanged(() => CurrentLocation);
        }
    }

    public NearbyBollardsMapViewModel(ILocationService locationService)
    {
        this._locationService = locationService;
        CurrentLocation = _locationService.GetCurrentUsersLocation().Result;
    }
}

Solution

  • You need to create a PCL class that extends from Maps and add a BindableProperty to it.

    1º - Create a class and extend it from Xamarin.Forms.Maps.Map

    (I included the code for the Bindable Property, you can read more Here)

    public class BindableMap : Xamarin.Forms.Maps.Map
    {
        public BindableMap()
        {
    
        }
    
        public MapSpan MapSpan
        {
            get { return (MapSpan)GetValue(MapSpanProperty); }
            set { SetValue(MapSpanProperty, value); }
        }
    
        public static readonly BindableProperty MapSpanProperty = BindableProperty.Create(
                                                         propertyName: "MapSpan",
                                                         returnType: typeof(MapSpan),
                                                         declaringType: typeof(BindableMap),
                                                         defaultValue: null,
                                                         defaultBindingMode: BindingMode.TwoWay,
                                                         validateValue: null,
                                                         propertyChanged: MapSpanPropertyChanged);
    
        private static void MapSpanPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            try
            {
                var thisInstance = bindable as BindableMap;
                var newMapSpan = newValue as MapSpan;
    
                thisInstance?.MoveToRegion(newMapSpan);
            }
            catch (Exception ex)
            {
    
            }
        }
        
    }
    

    2º - Add the property that will be used for binding the MapSpan in the ViewModel

    private MapSpan _mapSpanView;
    public MapSpan MapSpanView
    {
        get { return _mapSpanView; }
        set { SetProperty(ref _mapSpanView, value); }
    }
    
    ...
    
    Position position = new Position((double)Latitude, (double)Longitude);
    MapSpanView = MapSpan.FromCenterAndRadius(
                                prospectPosition,
                                Distance.FromMeters(2500)
                            );
    

    3º - And then Bind it in the xaml

    xmlns:map="clr-namespace:YourNameSpace.Mobile.TheFolderWhereTheClassIs"
    
    ...
    
    <StackLayout x:Name="mapHolder">
        <map:BindableMap
                        x:Name="map"
                        MapSpan="{Binding MapSpanView}"
                        MapType="Street" IsShowingUser="True" HasZoomEnabled="True"/>
    </StackLayout>