diff --git a/MapControl/Shared/MapBorderPanel.cs b/MapControl/Shared/MapBorderPanel.cs
new file mode 100644
index 00000000..9d96fad5
--- /dev/null
+++ b/MapControl/Shared/MapBorderPanel.cs
@@ -0,0 +1,94 @@
+// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control
+// Copyright © 2023 Clemens Fischer
+// Licensed under the Microsoft Public License (Ms-PL)
+
+#if WINUI
+using Microsoft.UI.Xaml;
+#elif UWP
+using Windows.UI.Xaml;
+#else
+using System.Windows;
+#endif
+
+namespace MapControl
+{
+ ///
+ /// A MapPanel that adjusts the ViewPosition property of its child elements so that
+ /// elements that would be outside the current viewport are arranged on a border area.
+ /// Such elements are arranged at a distance of BorderWidth/2 from the edges of the
+ /// MapBorderPanel in direction of their original azimuth from the map center.
+ ///
+ public class MapBorderPanel : MapPanel
+ {
+ public static readonly DependencyProperty BorderWidthProperty = DependencyProperty.Register(
+ nameof(BorderWidth), typeof(double), typeof(MapBorderPanel), null);
+
+ public static readonly DependencyProperty OnBorderProperty = DependencyProperty.Register(
+ "OnBorder", typeof(bool), typeof(MapBorderPanel), null);
+
+ public double BorderWidth
+ {
+ get => (double)GetValue(BorderWidthProperty);
+ set => SetValue(BorderWidthProperty, value);
+ }
+
+ public static bool GetOnBorder(FrameworkElement element)
+ {
+ return (bool)element.GetValue(OnBorderProperty);
+ }
+
+ protected override void SetViewPosition(FrameworkElement element, ref Point? position)
+ {
+ var onBorder = false;
+ var w = ParentMap.RenderSize.Width;
+ var h = ParentMap.RenderSize.Height;
+ var minX = BorderWidth / 2d;
+ var minY = BorderWidth / 2d;
+ var maxX = w - BorderWidth / 2d;
+ var maxY = h - BorderWidth / 2d;
+
+ if (position.HasValue &&
+ (position.Value.X < minX || position.Value.X > maxX ||
+ position.Value.Y < minY || position.Value.Y > maxY))
+ {
+ var dx = position.Value.X - w / 2d;
+ var dy = position.Value.Y - h / 2d;
+ var cx = (maxX - minX) / 2d;
+ var cy = (maxY - minY) / 2d;
+ double x, y;
+
+ if (dx < 0d)
+ {
+ x = minX;
+ y = minY + cy - cx * dy / dx;
+ }
+ else
+ {
+ x = maxX;
+ y = minY + cy + cx * dy / dx;
+ }
+
+ if (y < minY || y > maxY)
+ {
+ if (dy < 0d)
+ {
+ x = minX + cx - cy * dx / dy;
+ y = minY;
+ }
+ else
+ {
+ x = minX + cx + cy * dx / dy;
+ y = maxY;
+ }
+ }
+
+ position = new Point(x, y);
+ onBorder = true;
+ }
+
+ element.SetValue(OnBorderProperty, onBorder);
+
+ base.SetViewPosition(element, ref position);
+ }
+ }
+}
diff --git a/MapControl/Shared/MapPanel.cs b/MapControl/Shared/MapPanel.cs
index 3858f2b3..c7937892 100644
--- a/MapControl/Shared/MapPanel.cs
+++ b/MapControl/Shared/MapPanel.cs
@@ -105,8 +105,7 @@ namespace MapControl
}
///
- /// Gets the view position of an element with Location
- /// or null when the element has no Location.
+ /// Gets the view position of an element with Location.
///
public static Point? GetViewPosition(FrameworkElement element)
{
@@ -161,7 +160,7 @@ namespace MapControl
var location = GetLocation(element);
var position = location != null ? GetViewPosition(location) : null;
- SetViewPosition(element, position);
+ SetViewPosition(element, ref position);
if (GetAutoCollapse(element))
{
@@ -203,7 +202,7 @@ namespace MapControl
return finalSize;
}
- protected virtual Point? GetViewPosition(Location location)
+ protected Point? GetViewPosition(Location location)
{
var position = parentMap.LocationToView(location);
diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj
index cbdd7b58..014af40b 100644
--- a/MapControl/UWP/MapControl.UWP.csproj
+++ b/MapControl/UWP/MapControl.UWP.csproj
@@ -104,6 +104,9 @@
MapBase.cs
+
+ MapBorderPanel.cs
+
MapGraticule.cs
diff --git a/MapControl/WPF/MapPanel.WPF.cs b/MapControl/WPF/MapPanel.WPF.cs
index 818a08b1..eb0c4954 100644
--- a/MapControl/WPF/MapPanel.WPF.cs
+++ b/MapControl/WPF/MapPanel.WPF.cs
@@ -39,7 +39,12 @@ namespace MapControl
return (MapBase)element.GetValue(ParentMapProperty);
}
- private static void SetViewPosition(FrameworkElement element, Point? position)
+ ///
+ /// Sets the attached ViewPosition property of an element. The method is called during
+ /// ArrangeOverride and may be overridden to modify the actual view position value.
+ /// An overridden method should call this method to set the attached property.
+ ///
+ protected virtual void SetViewPosition(FrameworkElement element, ref Point? position)
{
element.SetValue(ViewPositionPropertyKey, position);
}
diff --git a/MapControl/WinUI/MapPanel.WinUI.cs b/MapControl/WinUI/MapPanel.WinUI.cs
index cee45253..007781c6 100644
--- a/MapControl/WinUI/MapPanel.WinUI.cs
+++ b/MapControl/WinUI/MapPanel.WinUI.cs
@@ -67,7 +67,12 @@ namespace MapControl
return parentMap;
}
- private static void SetViewPosition(FrameworkElement element, Point? position)
+ ///
+ /// Sets the attached ViewPosition property of an element. The method is called during
+ /// ArrangeOverride and may be overridden to modify the actual view position value.
+ /// An overridden method should call this method to set the attached property.
+ ///
+ protected virtual void SetViewPosition(FrameworkElement element, ref Point? position)
{
element.SetValue(ViewPositionProperty, position);
}