.netmvvmbindmaui

Bind static class in ViewModel class


I am playing around with the new Maps control from dotnet Maui. A simple example is to have the XAML like:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:maps="clr-namespace:Microsoft.Maui.Controls.Maps;assembly=Microsoft.Maui.Controls.Maps"
             x:Class="SaRM.MainPage">

   <maps:Map x:Name="map" MapType="Street"/>

</ContentPage>

and with that I can access the "map" in the code behind cs file:

public partial class MainPage : ContentPage
{    
    public MainPage()
    {
        InitializeComponent();  
    }

    protected override void OnAppearing()
    {
        var location = new Location(36.96, -122.0194);
        var mapSpan = new MapSpan(location, 0.01, 0.01);
        map.MoveToRegion(mapSpan);
    }
}

I would like to have a ViewModel to interact with the XAML but if I create a ViewModel class as:

public class MainViewModel
{

    public Map map;

    public MainViewModel()
    {
        var location = new Location(36.96, -122.0194);
        var mapSpan = new MapSpan(location, 0.01, 0.01);
        map.MoveToRegion(mapSpan);
    }

}

I get an error on "maps":

"Cannot declare variable of static type 'Map'"

That is because Map is a static class. How can I have a ViewModel class that binds to the Map element in XAML in that case?

Update: Here is the Map class definition inside Microsoft.Maui.Maps: enter image description here


Solution

  • UI elements and UI element types should not be directly referenced in your ViewModel,just as FreakyAli said.

    Since Map is a UI element, so we cannot use it in our ViewModel directly.

    You can define Map in YourPage.xaml and add other necessary data on your ViewModel.

    For example:

    you can add Map in YourPage.xaml and add Positions in View Model

             <maps:Map x:Name="map"
                      MapClicked="OnMapClicked"
                      ItemsSource="{Binding Positions}">
               
                <maps:Map.ItemTemplate>
                    <DataTemplate>
                        <maps:Pin Location="{Binding Location}"
                                  Address="{Binding Address}"
                                  Label="{Binding Description}" />
                    </DataTemplate>    
                </maps:Map.ItemTemplate>
            </maps:Map>
    

    And add map.MoveToRegion in YourPage.xaml.cs

    public partial class PinItemsSourcePage : ContentPage
    {
        public PinItemsSourcePage()
        {
            InitializeComponent();
            BindingContext = new PinItemsSourcePageViewModel();
            map.MoveToRegion(MapSpan.FromCenterAndRadius(new Location(39.8283459, -98.5794797), Distance.FromMiles(1500)));
        }
    
        void OnMapClicked(object sender, MapClickedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine($"MapClick: {e.Location.Latitude}, {e.Location.Longitude}");
        }
    }
    

    and define viewModel as follows:

    public class PinItemsSourcePageViewModel
    {
        int _pinCreatedCount = 0;
        readonly ObservableCollection<Position> _positions;
    
        public IEnumerable Positions => _positions;
    
        public ICommand AddLocationCommand { get; }
        public ICommand RemoveLocationCommand { get; }
        public ICommand ClearLocationsCommand { get; }
        public ICommand UpdateLocationsCommand { get; }
        public ICommand ReplaceLocationCommand { get; }
    
        public PinItemsSourcePageViewModel()
        {
            _positions = new ObservableCollection<Position>()
            {
                new Position("New York, USA", "The City That Never Sleeps", new Location(40.67, -73.94)),
                new Position("Los Angeles, USA", "City of Angels", new Location(34.11, -118.41)),
                new Position("San Francisco, USA", "Bay City", new Location(37.77, -122.45))
            };
    
            AddLocationCommand = new Command(AddLocation);
            RemoveLocationCommand = new Command(RemoveLocation);
            ClearLocationsCommand = new Command(() => _positions.Clear());
            UpdateLocationsCommand = new Command(UpdateLocations);
            ReplaceLocationCommand = new Command(ReplaceLocation);
        }
    
        void AddLocation()
        {
            _positions.Add(NewPosition());
        }
    
        void RemoveLocation()
        {
            if (_positions.Any())
            {
                _positions.Remove(_positions.First());
            }
        }
    
        void UpdateLocations()
        {
            if (!_positions.Any())
            {
                return;
            }
    
            double lastLatitude = _positions.Last().Location.Latitude;
            foreach (Position position in Positions)
            {
                position.Location = new Location(lastLatitude, position.Location.Longitude);
            }
        }
    
        void ReplaceLocation()
        {
            if (!_positions.Any())
            {
                return;
            }
    
            _positions[_positions.Count - 1] = NewPosition();
        }
    
        Position NewPosition()
        {
            _pinCreatedCount++;
            return new Position(
                $"Pin {_pinCreatedCount}",
                $"Desc {_pinCreatedCount}",
                RandomPosition.Next(new Location(39.8283459, -98.5794797), 8, 19));
        }
    }
    

    Fore more information, you can check document:Map.

    And above code is from official sample WorkingWithMaps. Please pay attention to PinItemsSourcePage.xaml and PinItemsSourcePageViewModel.cs.