Refresh the map

Eduardo Gomez Romero 1,175 Reputation points
2025-01-06T15:44:50.4466667+00:00

I need some help in refreshing the map, when the internet is back on

Since the map doesn't have any refresh, I am using the pages stack to reload the page

interface

    public interface IAppService
    {
        Task NavigateToPage(string pageName);

        Task NavigateBack(string quary = "..");

        Task NavigateToPage(string pageName, Dictionary<string, object> objectToPass, bool animate = true);

        Task ShowAlert(string title, string message, string confirm);
    }
}

implementation

    public class AppService: IAppService
    {
        public async Task NavigateBack(string quary = "..")
        {
            await Shell.Current.GoToAsync(quary);
        }

        public async Task NavigateToPage(string pageName)
        {
            await Shell.Current.GoToAsync(pageName);
        }

        public async Task NavigateToPage(string pageName, Dictionary<string, object> objectToPass, bool animate = true)
        {
            await Shell.Current.GoToAsync(pageName, animate, objectToPass);
        }

        public async Task ShowAlert(string title, string message, string confirm = "OK")
        {
            await Shell.Current.DisplayAlert(title, message, confirm);
        }
    }
}


I have a service, that gets turbines from firebase, and it will let me know if I have internet or no

 public class TurbinesService: ITurbineService, ICommandHandler
 {
     public event Action NoInternet = delegate { };

     private const string collectionName = AppConstants.COLLECTIONNAME;
     private readonly IFirestoreService _firestoreService;
     private readonly IBlobService _blobService;
     private readonly IConnectivity _connectivity;
     private static Timer? _timer;
     private FirestoreDb? _firestoreDb;
     private IPinClickHandler? _pinClickHandler;
     private bool isInitializing = false;

     public ICommand PinClickedCommand { get; private set; } = null!;

     public ObservableCollection<TurbinePin> TurbinePins { get; set; } = [];

     public TurbinesService(IFirestoreService firestoreService, IBlobService blobService, IConnectivity connectivity)
     {
         _firestoreService = firestoreService;
         _blobService = blobService;
         _connectivity = connectivity;
         AssingCommand();
     }

     private void AssingCommand()
     {
         PinClickedCommand = new Command<TurbinePin>(async (pin) =>
         {
             if (_pinClickHandler != null)
             {
                 await _pinClickHandler.PinMarkerClicked(pin);
             }
         });
     }

     public async Task InitializeAsync()
     {
         if (isInitializing)
         {
             return; // Prevent multiple initializations
         }

         isInitializing = true;
         TurbinePins.Clear();
         try
         {
             if (_connectivity.NetworkAccess != NetworkAccess.Internet)
             {
                 NoInternet?.Invoke();
                 isInitializing = false;
                 return;
             }
             bool isInitialized = await _firestoreService.InitializeFirestoreAsync();
             if (isInitialized)
             {
                 _firestoreDb = _firestoreService.GetFirestoreDb();
                 if (_firestoreDb != null)
                 {
                     await LoadOrInitializeTurbineAsync();
                     //InitializeTimer();
                 }
             }
         }
         catch (Exception ex)
         {
             Debug.WriteLine($"Initialization failed: {ex.Message}");
         }
         finally
         {
             isInitializing = false;
         }
     }


     private async Task LoadOrInitializeTurbineAsync()
     {
         var turbinesRef = _firestoreDb!.Collection(collectionName);
         var snapshot = await turbinesRef.GetSnapshotAsync();

         if (snapshot.Count == 0)
         {
             await InitializeAsyncDefaultTurbine(turbinesRef);
         }
         else
         {
             var tasks = snapshot.Documents.Select(
                 async document =>
                 {
                     var turbine = document.ConvertTo<Turbine>();
                     turbine.Id = document.Id;
                     await AddTurbineImagesAsync(turbine);
                     return turbine;
                 });

             var turbines = await Task.WhenAll(tasks);
             foreach (var turbine in turbines)
             {
                 AddToCollection(turbine);
             }
         }
     }


And in my appshellviewmodel, I show a popup when there is no internet, and hide it when is available

  public partial class AppShellViewModel: ObservableObject, IPinClickHandler
  {
      private AppShell? _shell;
      private readonly NoInternetPopUp _noInternetPopUp;
      private readonly IServiceProvider _serviceProvider;
      private readonly ITurbineService _turbineService;
      private readonly ICommandHandler _commandHandler;
      private readonly IConnectivity _connectivity;
      private readonly IAppService _appService;
      private bool isInitializing = false;

      public ObservableCollection<TurbinePin> TurbinePins => _turbineService.TurbinePins;
      public ICommand PinClickedCommand => _turbineService.PinClickedCommand;

      public const string FLYOUT_KEY = "flyout_key";
      public const string SWITCH_KEY = "switch_key";

      [ObservableProperty]
      bool isLoadFinished;

      [ObservableProperty]
      bool isCompactMode;

      [ObservableProperty]
      bool isMenuPopUpOpen;

      public AppShellViewModel(ITurbineService turbineService, IAppService appService,
          IServiceProvider serviceProvider, ICommandHandler commandHandler,
          IConnectivity connectivity, NoInternetPopUp noInternetPopUp)
      {
          _serviceProvider = serviceProvider;
          _turbineService = turbineService;
          _appService = appService;
          _commandHandler = commandHandler;
          _connectivity = connectivity;
          _noInternetPopUp = noInternetPopUp;

          _turbineService.NoInternet += TurbineService_NoInternet;

          InitializeCommand();

          _turbineService.SetPinClickHandler(this);

          _connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
      }

      private void TurbineService_NoInternet()
      {
          _noInternetPopUp.Show();
      }

      private void InitializeCommand()
      {
          _commandHandler.SetPinClickedCommand(new Command<TurbinePin>(async (pin) =>
          await PinMarkerClicked(pin)));
      }

      [RelayCommand]
      async Task Appearing(AppShell appShell)
      {
          _shell = appShell;

          try
          {
              if (TurbinePins.Count == 0 && !isInitializing)
              {
                  isInitializing = true;
                  await _turbineService.InitializeAsync();
                  NotifyMapRefresh();
                  isInitializing = false;
              }
          }
          catch (Exception ex)
          {
              Debug.WriteLine($"Initialization failed: {ex.Message}");
              isInitializing = false;
          }
      }

      private void Connectivity_ConnectivityChanged(object? sender, ConnectivityChangedEventArgs e)
      {
          HandleConnectivityChangeAsync();
      }

      private async void HandleConnectivityChangeAsync()
      {
          if (_connectivity.NetworkAccess == NetworkAccess.Internet)
          {
              _noInternetPopUp.IsOpen = false;
              try
              {
                  if (TurbinePins.Count == 0 && !isInitializing)
                  {
                      isInitializing = true;
                      await _turbineService.InitializeAsync();
                      NotifyMapRefresh();
                      isInitializing = false;
                  }
              }
              catch (Exception ex)
              {
                  Debug.WriteLine($"Initialization failed: {ex.Message}");
                  isInitializing = false;
              }
          }
          else
          {
              TurbineService_NoInternet();
          }
      }

As you can see, I am using the mvvm toolkit, to send a message

 public ChargingStationsMapPageViewModel(
     ITurbineService turbineService, IAppService
     appService, IServiceProvider serviceProvider,
     NoInternetPopUp noInternetPopUp,
     ICommandHandler commandHandler, IConnectivity connectivity)
     : base(turbineService, appService, serviceProvider, commandHandler, connectivity, noInternetPopUp)
 {
     MapDialogButtons();

     _appService = appService;

     WeakReferenceMessenger.Default.Register<string>(this, (r
         , message) =>
     {
         if (message == "RefreshMap")
         {
             RefreshMap();
         }
     });

 }


 public ObservableCollection<MapTypeButton> MapTypeButtons { get; set; } = [];

 private void MapDialogButtons()
 {
     MapTypeButtons.Add(new MapTypeButton
     {
         Caption = AppResource.Default,
         ImageName = MaterialFonts.Map,
         Selected = true,
         MapNumber = 1
     });
     MapTypeButtons.Add(new MapTypeButton
     {
         Caption = AppResource.Satelite,
         MapNumber = 2,
         ImageName = MaterialFonts.Satellite,
     });
 }

 [RelayCommand]
 void ItemSelected(Turbine Turbine)
 {
     if (Turbine == null || MapView == null)
     {
         return;
     }
     var mapSpan = MapSpan.FromCenterAndRadius(Turbine.Location,
         Distance.FromKilometers(2));

     MapView!.MoveToRegion(mapSpan);
 }

 [RelayCommand]

 void OpenMapLayerOptions()
 {
     MapDialogPopUp!.IsOpen = true;
 }

 [RelayCommand]
 void ChangeMapType(MapTypeButton mapTypeCustomButton)
 {
     if (mapTypeCustomButton == null)
     {
         return;
     }

     foreach (var mapTypeButton in MapTypeButtons)
     {
         mapTypeButton.Selected = false;
     }

     mapTypeCustomButton.Selected = true;

     switch (mapTypeCustomButton.MapNumber)
     {
         case 1:
             MapView!.MapType = MapType.Street;
             break;
         case 2:
             MapView!.MapType = MapType.Satellite;
             break;
     }

     MapDialogPopUp!.IsOpen = false;
 }

 [RelayCommand]
 async Task RefreshMap()
 {
     isRefreshing = true;

     if (isRefreshing)
     {
         await _appService!.NavigateBack();
         await _appService.NavigateToPage("//ChargingStationsMapPage");
     }

     isRefreshing = false;
 }

   <RefreshView Command="{x:Binding RefreshMapCommand}">
       <maps:Map
       x:Name="ChargingStationMap"
       ItemsSource="{x:Binding TurbinePins}"
       VerticalOptions="FillAndExpand">
           <maps:Map.ItemTemplate>
               <DataTemplate x:DataType="model:TurbinePin">
                   <controls:CustomMapPin
                   Address="{x:Binding Turbine.Address}"
                   Images="{x:Binding Turbine.ImagesURLs}"
                   Label="{x:Binding Turbine.Name}"
                   Location="{x:Binding Turbine.Location}"
                   MarkerClickedCommand="{x:Binding PinClickedCommand}"
                   MarkerClickedCommandParameter="{x:Binding}" />
               </DataTemplate>
           </maps:Map.ItemTemplate>
       </maps:Map>
   </RefreshView>


But is not working, and I suspect is because for some reason, the refreshing, is being called multiple times

Demo

https://reccloud.com/u/k9yz3nb

.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,807 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 46,821 Reputation points Microsoft Vendor
    2025-01-07T06:37:39.5+00:00

    Hello,

    I noticed that your requirement is to use Messager to try to refresh the page by navigating when the network status of the page changes.

    This method does not guarantee page refresh. If your page is a singleton, page elements will not be redrawn after navigating back to this page. To redraw the page, you need to call the InvalidateMeasure method of the page.

    Please refer to the following known issues and API documentation.

    Best Regards,

    Alec Liu.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.