diff --git a/src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.Appearance.cs b/src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.Appearance.cs
index db4f84d3a..9f852809c 100644
--- a/src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.Appearance.cs
+++ b/src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.Appearance.cs
@@ -173,7 +173,7 @@ static BasemapGallery()
-
+
diff --git a/src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.cs b/src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.cs
index a840f4bb7..6cd47dc83 100644
--- a/src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.cs
+++ b/src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.cs
@@ -41,9 +41,20 @@ public BasemapGallery()
ListItemTemplate = DefaultListDataTemplate;
GridItemTemplate = DefaultGridDataTemplate;
ControlTemplate = DefaultControlTemplate;
- _ = _controller.LoadFromDefaultPortal();
- }
-
+ Loaded += BasemapGallery_Loaded;
+ }
+
+ private async void BasemapGallery_Loaded(object? sender, EventArgs e)
+ {
+ // Unsubscribe from the Loaded event to ensure this only runs once.
+ Loaded -= BasemapGallery_Loaded;
+
+ if (AvailableBasemaps is null)
+ {
+ await _controller.LoadFromDefaultPortal();
+ }
+ }
+
private void HandleControllerPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
@@ -185,7 +196,7 @@ public GeoModel? GeoModel
public IList? AvailableBasemaps
{
get => GetValue(AvailableBasemapsProperty) as IList;
- set => SetValue(AvailableBasemapsProperty, value);
+ set => SetValue(AvailableBasemapsProperty, value);
}
///
diff --git a/src/Toolkit/Toolkit.UI.Controls/BasemapGallery/BasemapGallery.Windows.cs b/src/Toolkit/Toolkit.UI.Controls/BasemapGallery/BasemapGallery.Windows.cs
index 5dca3ef46..66ecfb5f8 100644
--- a/src/Toolkit/Toolkit.UI.Controls/BasemapGallery/BasemapGallery.Windows.cs
+++ b/src/Toolkit/Toolkit.UI.Controls/BasemapGallery/BasemapGallery.Windows.cs
@@ -45,7 +45,18 @@ public BasemapGallery()
SizeChanged += BasemapGallerySizeChanged;
AvailableBasemaps = new ObservableCollection();
_controller.PropertyChanged += HandleControllerPropertyChanged;
- _ = _controller.LoadFromDefaultPortal();
+ Loaded += BasemapGallery_Loaded;
+ }
+
+ private async void BasemapGallery_Loaded(object? sender, RoutedEventArgs e)
+ {
+ // Unsubscribe from the Loaded event to ensure this only runs once.
+ Loaded -= BasemapGallery_Loaded;
+
+ if (AvailableBasemaps is null)
+ {
+ await _controller.LoadFromDefaultPortal();
+ }
}
private void HandleControllerPropertyChanged(object? sender, PropertyChangedEventArgs e)
diff --git a/src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryController.cs b/src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryController.cs
index c367ece8a..6d47a1e5b 100644
--- a/src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryController.cs
+++ b/src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryController.cs
@@ -29,7 +29,9 @@ namespace Esri.ArcGISRuntime.Toolkit.Maui
namespace Esri.ArcGISRuntime.Toolkit.UI
#endif
{
+#pragma warning disable CA1001 // Types that own disposable fields should be disposable
internal class BasemapGalleryController : INotifyPropertyChanged
+#pragma warning restore CA1001 // Types that own disposable fields should be disposable
{
private ArcGISPortal? _portal;
private bool _ignoreEventsFlag;
@@ -37,6 +39,7 @@ internal class BasemapGalleryController : INotifyPropertyChanged
private GeoModel? _geoModel;
private BasemapGalleryItem? _selectedBasemap;
private bool _isLoading;
+ private CancellationTokenSource? _loadCancellationTokenSource;
public bool IsLoading
{
@@ -72,6 +75,7 @@ public IList? AvailableBasemaps
HandleAvailableBasemapsChanged();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AvailableBasemaps)));
+ _loadCancellationTokenSource?.Cancel();
}
}
}
@@ -227,10 +231,13 @@ private void HandleSelectedBasemapChanged()
public async Task LoadFromDefaultPortal()
{
IsLoading = true;
+ _loadCancellationTokenSource = new CancellationTokenSource();
try
{
- AvailableBasemaps = await PopulateFromDefaultList();
+ AvailableBasemaps = await PopulateFromDefaultList(_loadCancellationTokenSource.Token);
}
+ catch (OperationCanceledException)
+ { }
finally
{
IsLoading = false;
@@ -305,11 +312,11 @@ private static async Task BasemapIsActuallyNotABasemap(Basemap input)
return listOfBasemaps;
}
- private static async Task> PopulateFromDefaultList()
+ private static async Task> PopulateFromDefaultList(CancellationToken cancellationToken = default)
{
- ArcGISPortal defaultPortal = await ArcGISPortal.CreateAsync();
+ ArcGISPortal defaultPortal = await ArcGISPortal.CreateAsync(cancellationToken);
- var results = await defaultPortal.GetDeveloperBasemapsAsync();
+ var results = await defaultPortal.GetDeveloperBasemapsAsync(cancellationToken);
var listOfBasemaps = new List();
diff --git a/src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryItem.cs b/src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryItem.cs
index f95807278..d15258827 100644
--- a/src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryItem.cs
+++ b/src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryItem.cs
@@ -246,9 +246,53 @@ internal bool EqualsBasemap(Basemap? other)
return false;
}
- return other == Basemap || other.Name == Basemap?.Name
- || (other.Item?.ItemId != null && other.Item?.ItemId == Basemap?.Item?.ItemId)
- || (other.Uri != null && other.Uri == Basemap?.Uri);
+ return other == Basemap || (other.Item?.ItemId != null && other.Item?.ItemId == Basemap?.Item?.ItemId)
+ || (other.Uri != null && other.Uri == Basemap?.Uri)
+ || AreBasemapsEqualByLayers(Basemap, other);
+ }
+
+ private static bool AreBasemapsEqualByLayers(Basemap? basemap1, Basemap? basemap2)
+ {
+ if (basemap1 == null || basemap2 == null) return false;
+
+ return LayersEqual(basemap1.BaseLayers, basemap2.BaseLayers)
+ && LayersEqual(basemap1.ReferenceLayers, basemap2.ReferenceLayers);
+ }
+
+ private static bool LayersEqual(LayerCollection layers1, LayerCollection layers2)
+ {
+ return layers1.Count == layers2.Count
+ && layers1.Zip(layers2, LayerEquals).All(equal => equal);
+ }
+
+ private static bool LayerEquals(Layer layer1, Layer layer2)
+ {
+ // This method can be extended to handle more specific layer comparisons if needed
+ if (layer1.GetType() != layer2.GetType()) return false;
+
+ return layer1 switch
+ {
+ AnnotationLayer annotationLayer => annotationLayer.Source == ((AnnotationLayer)layer2).Source,
+ ArcGISMapImageLayer arcGISMapImageLayer => arcGISMapImageLayer.Source == ((ArcGISMapImageLayer)layer2).Source,
+ ArcGISSceneLayer sceneLayer => sceneLayer.Source == ((ArcGISSceneLayer)layer2).Source,
+ ArcGISTiledLayer tiledLayer => tiledLayer.Source == ((ArcGISTiledLayer)layer2).Source,
+ ArcGISVectorTiledLayer vectorTiledLayer => vectorTiledLayer.Source == ((ArcGISVectorTiledLayer)layer2).Source,
+ BingMapsLayer imageServiceLayer => imageServiceLayer.Portal?.Uri == ((BingMapsLayer)layer2).Portal?.Uri,
+ DimensionLayer dimensionLayer => dimensionLayer.Source == ((DimensionLayer)layer2).Source,
+ FeatureCollectionLayer featureCollectionLayer => featureCollectionLayer.FeatureCollection == ((FeatureCollectionLayer)layer2).FeatureCollection,
+ GroupLayer groupLayer => groupLayer.Layers.Count == ((GroupLayer)layer2).Layers.Count && LayersEqual(groupLayer.Layers, ((GroupLayer)layer2).Layers),
+ IntegratedMeshLayer integratedMeshLayer => integratedMeshLayer.Source == ((IntegratedMeshLayer)layer2).Source,
+ KmlLayer kmlLayer => kmlLayer.Dataset?.Source == ((KmlLayer)layer2).Dataset?.Source,
+ Ogc3DTilesLayer ogc3DTilesLayer => ogc3DTilesLayer.Source == ((Ogc3DTilesLayer)layer2).Source,
+ OpenStreetMapLayer openStreetMapLayer => true, // OpenStreetMap layers are considered equal if types match
+ PointCloudLayer pointCloudLayer => pointCloudLayer.Source == ((PointCloudLayer)layer2).Source,
+ WebTiledLayer webTiledLayer => webTiledLayer.TemplateUri == ((WebTiledLayer)layer2).TemplateUri,
+ ServiceImageTiledLayer serviceImageTiledLayer => serviceImageTiledLayer.TileInfo == ((ServiceImageTiledLayer)layer2).TileInfo && serviceImageTiledLayer.FullExtent == ((ServiceImageTiledLayer)layer2).FullExtent,
+ SubtypeFeatureLayer subtypeFeatureLayer => subtypeFeatureLayer.FeatureTable == ((SubtypeFeatureLayer)layer2).FeatureTable,
+ WmsLayer wmsLayer => wmsLayer.Source == ((WmsLayer)layer2).Source,
+ WmtsLayer wmtsLayer => wmtsLayer.Source == ((WmtsLayer)layer2).Source,
+ _ => false,
+ };
}
///