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