XAML-Map-Control/MapControl/Shared/MapBase.MapLayer.cs

258 lines
8.4 KiB
C#
Raw Normal View History

2025-08-12 23:02:11 +02:00
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
#if WPF
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
#elif UWP
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
#elif WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
#endif
namespace MapControl
{
public interface IMapLayer : IMapElement
{
Brush MapBackground { get; }
Brush MapForeground { get; }
}
public partial class MapBase
{
2025-08-12 10:57:44 +02:00
private bool hasMapLayerBackground;
private bool hasMapLayerForeground;
public static readonly DependencyProperty MapLayerProperty =
2025-08-12 10:57:44 +02:00
DependencyPropertyHelper.Register<MapBase, object>(nameof(MapLayer), null,
(map, oldValue, newValue) => map.MapLayerPropertyChanged(oldValue, newValue));
2025-08-13 07:56:49 +02:00
public static readonly DependencyProperty MapLayersSourceProperty =
DependencyPropertyHelper.Register<MapBase, IEnumerable>(nameof(MapLayersSource), null,
(map, oldValue, newValue) => map.MapLayersSourcePropertyChanged(oldValue, newValue));
/// <summary>
/// Gets or sets the base map layer, which is added as first element to the Children collection.
2025-08-12 23:02:11 +02:00
/// If the passed object is not a FrameworkElement, MapBase tries to locate a DataTemplate
/// resource for the object's type and generate a FrameworkElement from that DataTemplate.
/// If the FrameworkElement implements IMapLayer (like e.g. MapTileLayer or MapImageLayer),
/// its (non-null) MapBackground and MapForeground property values are used for the MapBase
/// Background and Foreground.
/// </summary>
2025-08-12 10:57:44 +02:00
public object MapLayer
{
2025-08-12 10:57:44 +02:00
get => GetValue(MapLayerProperty);
set => SetValue(MapLayerProperty, value);
}
2025-08-13 07:56:49 +02:00
/// <summary>
/// Holds a collection of map layers, either FrameworkElements or plain objects with
/// an associated DataTemplate resource from which a FrameworkElement can be created.
/// FrameworkElemens are added to the Children collection, starting at index 0.
/// The first element of this collection is assigned to the MapLayer property.
/// Subsequent changes of the MapLayer or Children properties are not reflected
/// by the MapLayersSource collection.
/// </summary>
public IEnumerable MapLayersSource
{
2025-08-13 07:56:49 +02:00
get => (IEnumerable)GetValue(MapLayersSourceProperty);
set => SetValue(MapLayersSourceProperty, value);
}
2025-08-12 10:57:44 +02:00
private void MapLayerPropertyChanged(object oldLayer, object newLayer)
{
if (oldLayer != null)
{
2025-08-12 23:02:11 +02:00
if (Children.Count > 0 &&
(Children[0] == oldLayer as FrameworkElement ||
((FrameworkElement)Children[0]).DataContext == oldLayer))
{
2025-08-12 11:33:39 +02:00
Children.RemoveAt(0);
}
2025-08-12 10:57:44 +02:00
if (hasMapLayerBackground)
{
2025-08-12 10:57:44 +02:00
ClearValue(BackgroundProperty);
}
if (hasMapLayerForeground)
{
ClearValue(ForegroundProperty);
}
}
2025-08-12 10:57:44 +02:00
hasMapLayerBackground = false;
hasMapLayerForeground = false;
if (newLayer != null)
{
2025-08-12 23:02:11 +02:00
if (Children.Count == 0 ||
(Children[0] != newLayer as FrameworkElement &&
((FrameworkElement)Children[0]).DataContext != newLayer))
{
2025-08-12 11:33:39 +02:00
Children.Insert(0, GetMapLayer(newLayer));
}
2025-08-12 23:02:11 +02:00
if (Children[0] is IMapLayer mapLayer)
{
if (mapLayer.MapBackground != null)
{
Background = mapLayer.MapBackground;
2025-08-12 10:57:44 +02:00
hasMapLayerBackground = true;
}
2025-08-12 10:57:44 +02:00
if (mapLayer.MapForeground != null)
{
Foreground = mapLayer.MapForeground;
2025-08-12 10:57:44 +02:00
hasMapLayerForeground = true;
}
}
}
}
2025-08-13 07:56:49 +02:00
private void MapLayersSourcePropertyChanged(IEnumerable oldLayers, IEnumerable newLayers)
{
2025-08-13 07:56:49 +02:00
if (oldLayers != null)
{
2025-08-13 07:56:49 +02:00
if (oldLayers is INotifyCollectionChanged incc)
{
2025-08-13 07:56:49 +02:00
incc.CollectionChanged -= MapLayersSourceCollectionChanged;
}
2025-08-13 07:56:49 +02:00
RemoveMapLayers(oldLayers, 0);
}
2025-08-13 07:56:49 +02:00
if (newLayers != null)
{
2025-08-13 07:56:49 +02:00
if (newLayers is INotifyCollectionChanged incc)
{
2025-08-13 07:56:49 +02:00
incc.CollectionChanged += MapLayersSourceCollectionChanged;
}
2025-08-13 07:56:49 +02:00
AddMapLayers(newLayers, 0);
}
}
2025-08-13 07:56:49 +02:00
private void MapLayersSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddMapLayers(e.NewItems, e.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
RemoveMapLayers(e.OldItems, e.OldStartingIndex);
break;
case NotifyCollectionChangedAction.Replace:
RemoveMapLayers(e.OldItems, e.OldStartingIndex);
AddMapLayers(e.NewItems, e.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Reset:
break;
default:
break;
}
}
2025-08-13 07:56:49 +02:00
private void AddMapLayers(IEnumerable layers, int index)
{
2025-08-13 07:56:49 +02:00
var mapLayers = layers.Cast<object>().Select(GetMapLayer).ToList();
2025-08-12 10:57:44 +02:00
if (mapLayers.Count > 0)
{
#if WPF
2025-08-12 10:57:44 +02:00
// Execute at DispatcherPriority.DataBind to ensure that all bindings are evaluated.
Dispatcher.Invoke(() => AddMapLayers(mapLayers, index), DispatcherPriority.DataBind);
#else
2025-08-12 10:57:44 +02:00
AddMapLayers(mapLayers, index);
#endif
2025-08-12 10:57:44 +02:00
}
}
2025-08-12 10:57:44 +02:00
private void AddMapLayers(List<FrameworkElement> mapLayers, int index)
{
2025-08-12 11:33:39 +02:00
foreach (var mapLayer in mapLayers)
2025-08-12 10:57:44 +02:00
{
2025-08-12 11:33:39 +02:00
Children.Insert(index, mapLayer);
2025-08-12 23:05:29 +02:00
if (index++ == 0)
2025-08-12 11:33:39 +02:00
{
MapLayer = mapLayer;
}
}
}
2025-08-13 07:56:49 +02:00
private void RemoveMapLayers(IEnumerable layers, int index)
{
2025-08-13 07:56:49 +02:00
foreach (var _ in layers)
2025-08-12 11:33:39 +02:00
{
Children.RemoveAt(index);
}
if (index == 0)
{
MapLayer = null;
}
}
2025-08-13 07:56:49 +02:00
private FrameworkElement GetMapLayer(object layer)
{
FrameworkElement mapLayer = null;
2025-08-13 07:56:49 +02:00
if (layer != null)
{
2025-08-13 07:56:49 +02:00
mapLayer = layer as FrameworkElement ?? TryLoadDataTemplate(layer);
}
2025-08-12 10:57:44 +02:00
return mapLayer ?? new MapTileLayer();
}
2025-08-13 07:56:49 +02:00
private FrameworkElement TryLoadDataTemplate(object layer)
{
FrameworkElement element = null;
#if AVALONIA
2025-08-13 07:56:49 +02:00
if (this.TryFindResource(layer.GetType().FullName, out object value) &&
value is Avalonia.Markup.Xaml.Templates.DataTemplate template)
{
2025-08-13 07:56:49 +02:00
element = template.Build(layer);
}
#elif WPF
2025-08-13 07:56:49 +02:00
if (TryFindResource(new DataTemplateKey(layer.GetType())) is DataTemplate template)
{
element = (FrameworkElement)template.LoadContent();
}
#else
2025-08-13 07:56:49 +02:00
if (TryFindResource(this, layer.GetType().FullName) is DataTemplate template)
{
element = (FrameworkElement)template.LoadContent();
}
#endif
if (element != null)
{
2025-08-13 07:56:49 +02:00
element.DataContext = layer;
}
return element;
}
#if UWP || WINUI
2025-08-12 10:57:44 +02:00
private static object TryFindResource(FrameworkElement element, object key)
{
2025-08-12 10:57:44 +02:00
return element.Resources.ContainsKey(key)
? element.Resources[key]
: element.Parent is FrameworkElement parent
? TryFindResource(parent, key)
: null;
}
#endif
2025-08-12 10:57:44 +02:00
}
}