diff --git a/MapControl/Shared/MapOverlaysPanel.cs b/MapControl/Shared/MapOverlaysPanel.cs new file mode 100644 index 00000000..7a81d9fe --- /dev/null +++ b/MapControl/Shared/MapOverlaysPanel.cs @@ -0,0 +1,133 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// Copyright © 2024 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Linq; +using Path = System.IO.Path; +#if WPF +using System.Windows; +#elif UWP +using Windows.UI.Xaml; +#elif WINUI +using Microsoft.UI.Xaml; +#endif + +namespace MapControl +{ + /// + /// A MapPanel with a collection of GroundOverlay or GeoImage children. + /// + public partial class MapOverlaysPanel : MapPanel + { + public static readonly DependencyProperty SourcePathsProperty = + DependencyPropertyHelper.Register>(nameof(SourcePaths), null, + (control, oldValue, newValue) => control.SourcePathsPropertyChanged(oldValue, newValue)); + + public IEnumerable SourcePaths + { + get => (IEnumerable)GetValue(SourcePathsProperty); + set => SetValue(SourcePathsProperty, value); + } + + private void SourcePathsPropertyChanged(IEnumerable oldSourcePaths, IEnumerable newSourcePaths) + { + Children.Clear(); + + if (oldSourcePaths is INotifyCollectionChanged oldCollection) + { + oldCollection.CollectionChanged -= SourcePathsCollectionChanged; + } + + if (newSourcePaths != null) + { + if (newSourcePaths is INotifyCollectionChanged newCollection) + { + newCollection.CollectionChanged += SourcePathsCollectionChanged; + } + + AddOverlays(0, newSourcePaths); + } + } + + private void SourcePathsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) + { + switch (args.Action) + { + case NotifyCollectionChangedAction.Add: + AddOverlays(args.NewStartingIndex, args.NewItems.Cast()); + break; + + case NotifyCollectionChangedAction.Remove: + RemoveOverlays(args.OldStartingIndex, args.OldItems.Count); + break; + + case NotifyCollectionChangedAction.Move: + RemoveOverlays(args.OldStartingIndex, args.OldItems.Count); + AddOverlays(args.NewStartingIndex, args.NewItems.Cast()); + break; + + case NotifyCollectionChangedAction.Replace: + ReplaceOverlays(args.NewStartingIndex, args.NewItems.Cast()); + break; + + case NotifyCollectionChangedAction.Reset: + Children.Clear(); + AddOverlays(0, SourcePaths); + break; + } + } + + private void AddOverlays(int index, IEnumerable sourcePaths) + { + foreach (var sourcePath in sourcePaths) + { + Children.Insert(index++, CreateOverlay(sourcePath)); + } + } + + private void ReplaceOverlays(int index, IEnumerable sourcePaths) + { + foreach (var sourcePath in sourcePaths) + { + Children[index++] = CreateOverlay(sourcePath); + } + } + + private void RemoveOverlays(int index, int count) + { + while (--count >= 0) + { + Children.RemoveAt(index); + } + } + + protected virtual FrameworkElement CreateOverlay(string sourcePath) + { + var ext = Path.GetExtension(sourcePath).ToLower(); + FrameworkElement overlay; + + try + { + if (ext == ".kmz" || ext == ".kml") + { + overlay = new GroundOverlay { SourcePath = sourcePath }; + } + else + { + overlay = new GeoImage { SourcePath = sourcePath }; + } + } + catch (Exception ex) + { + Debug.WriteLine($"MapOverlaysControl: {sourcePath}: {ex.Message}"); + overlay = new MapPanel(); + } + + return overlay; + } + } +}