diff --git a/MapControl/Shared/MapBase.cs b/MapControl/Shared/MapBase.cs index 6240fdb3..2e4e949e 100644 --- a/MapControl/Shared/MapBase.cs +++ b/MapControl/Shared/MapBase.cs @@ -236,6 +236,18 @@ namespace MapControl return ViewTransform.Scale * MapProjection.GetRelativeScale(location); } + /// + /// Gets a transform Matrix for scaling and rotating objects that are anchored + /// at a Location from map coordinates (i.e. meters) to view coordinates. + /// + public Matrix GetMapTransform(Location location) + { + var scale = GetScale(location); + var matrix = new Matrix(scale.X, 0d, 0d, scale.Y, 0d, 0d); + matrix.Rotate(ViewTransform.Rotation); + return matrix; + } + /// /// Transforms a Location in geographic coordinates to a Point in view coordinates. /// diff --git a/MapControl/Shared/MapItem.cs b/MapControl/Shared/MapItem.cs new file mode 100644 index 00000000..9a69cf3c --- /dev/null +++ b/MapControl/Shared/MapItem.cs @@ -0,0 +1,105 @@ +// 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.Controls; +using Microsoft.UI.Xaml.Media; +#elif UWP +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; +#else +using System.Windows.Controls; +using System.Windows.Media; +#endif + +namespace MapControl +{ + /// + /// Container class for an item in a MapItemsControl. + /// + public partial class MapItem : ListBoxItem, IMapElement + { + private MapBase parentMap; + private MatrixTransform mapTransform; + + /// + /// Gets/sets MapPanel.AutoCollapse. + /// + public bool AutoCollapse + { + get => (bool)GetValue(AutoCollapseProperty); + set => SetValue(AutoCollapseProperty, value); + } + + /// + /// Gets/sets MapPanel.Location. + /// + public Location Location + { + get => (Location)GetValue(LocationProperty); + set => SetValue(LocationProperty, value); + } + + /// + /// Implements IMapElement.ParentMap. + /// + public MapBase ParentMap + { + get => parentMap; + set + { + if (parentMap != null) + { + parentMap.ViewportChanged -= OnViewportChanged; + } + + parentMap = value; + + if (parentMap != null && mapTransform != null) + { + // Attach ViewportChanged handler only if MapTransform is actually used. + // + parentMap.ViewportChanged += OnViewportChanged; + UpdateMapTransform(Location); + } + } + } + + /// + /// Gets a Transform for scaling and rotating geometries + /// in map coordinates (meters) to view coordinates (pixels). + /// + public Transform MapTransform + { + get + { + if (mapTransform == null) + { + mapTransform = new MatrixTransform(); + + if (parentMap != null) + { + parentMap.ViewportChanged += OnViewportChanged; + UpdateMapTransform(Location); + } + } + + return mapTransform; + } + } + + private void OnViewportChanged(object sender, ViewportChangedEventArgs e) + { + UpdateMapTransform(Location); + } + + private void UpdateMapTransform(Location location) + { + if (mapTransform != null && parentMap != null && location != null) + { + mapTransform.Matrix = parentMap.GetMapTransform(location); + } + } + } +} diff --git a/MapControl/Shared/MapItemsControl.cs b/MapControl/Shared/MapItemsControl.cs index d4191289..c78b2815 100644 --- a/MapControl/Shared/MapItemsControl.cs +++ b/MapControl/Shared/MapItemsControl.cs @@ -8,77 +8,19 @@ using Windows.Foundation; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Media; #elif UWP using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Media; #else using System.Windows; using System.Windows.Controls; using System.Windows.Data; -using System.Windows.Media; #endif namespace MapControl { - /// - /// Container class for an item in a MapItemsControl. - /// - public partial class MapItem : ListBoxItem - { - /// - /// Gets/sets MapPanel.AutoCollapse. - /// - public bool AutoCollapse - { - get => (bool)GetValue(AutoCollapseProperty); - set => SetValue(AutoCollapseProperty, value); - } - - /// - /// Gets/sets MapPanel.Location. - /// - public Location Location - { - get => (Location)GetValue(LocationProperty); - set => SetValue(LocationProperty, value); - } - - /// - /// Gets a Transform for scaling and rotating geometries - /// in map coordinates (meters) to view coordinates (pixels). - /// - public Transform MapTransform => mapTransform ?? (mapTransform = new MatrixTransform()); - - private MatrixTransform mapTransform; - - protected override Size ArrangeOverride(Size bounds) - { - var size = base.ArrangeOverride(bounds); - - // If the MapTransform property is used, update its Matrix property - // (after calling base.ArrangeOverride to avoid rendering issues). - // - if (mapTransform != null) - { - var parentMap = (VisualTreeHelper.GetParent(this) as MapPanel)?.ParentMap; - - if (parentMap != null && Location != null) - { - var scale = parentMap.GetScale(Location); - var matrix = new Matrix(scale.X, 0d, 0d, scale.Y, 0d, 0d); - matrix.Rotate(parentMap.ViewTransform.Rotation); - mapTransform.Matrix = matrix; - } - } - - return size; - } - } - /// /// Manages a collection of selectable items on a Map. /// diff --git a/MapControl/Shared/MapPanel.cs b/MapControl/Shared/MapPanel.cs index c7937892..581a37b8 100644 --- a/MapControl/Shared/MapPanel.cs +++ b/MapControl/Shared/MapPanel.cs @@ -46,6 +46,9 @@ namespace MapControl private MapBase parentMap; + /// + /// Implements IMapElement.ParentMap. + /// public MapBase ParentMap { get => parentMap; diff --git a/MapControl/Shared/MapPath.cs b/MapControl/Shared/MapPath.cs index c4d07c26..42e31746 100644 --- a/MapControl/Shared/MapPath.cs +++ b/MapControl/Shared/MapPath.cs @@ -40,6 +40,9 @@ namespace MapControl set => SetValue(LocationProperty, value); } + /// + /// Implements IMapElement.ParentMap. + /// public MapBase ParentMap { get => parentMap; @@ -72,9 +75,7 @@ namespace MapControl if (parentMap != null && Location != null && Data != null) { - var scale = parentMap.GetScale(Location); - var matrix = new Matrix(scale.X, 0d, 0d, scale.Y, 0d, 0d); - matrix.Rotate(parentMap.ViewTransform.Rotation); + var matrix = parentMap.GetMapTransform(Location); if (Data.Transform is MatrixTransform transform) { diff --git a/MapControl/Shared/MapTileLayerBase.cs b/MapControl/Shared/MapTileLayerBase.cs index 8cdd1c07..9a9fc1ea 100644 --- a/MapControl/Shared/MapTileLayerBase.cs +++ b/MapControl/Shared/MapTileLayerBase.cs @@ -165,6 +165,9 @@ namespace MapControl private set => SetValue(LoadingProgressProperty, value); } + /// + /// Implements IMapElement.ParentMap. + /// public MapBase ParentMap { get => parentMap; diff --git a/MapControl/UWP/MapControl.UWP.csproj b/MapControl/UWP/MapControl.UWP.csproj index 014af40b..3dda270e 100644 --- a/MapControl/UWP/MapControl.UWP.csproj +++ b/MapControl/UWP/MapControl.UWP.csproj @@ -113,6 +113,9 @@ MapImageLayer.cs + + MapItem.cs + MapItemsControl.cs @@ -248,6 +251,9 @@ MapGraticule.WinUI.cs + + MapItem.WinUI.cs + MapItemsControl.WinUI.cs diff --git a/MapControl/WPF/MapItem.WPF.cs b/MapControl/WPF/MapItem.WPF.cs new file mode 100644 index 00000000..b37da483 --- /dev/null +++ b/MapControl/WPF/MapItem.WPF.cs @@ -0,0 +1,31 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// Copyright © 2023 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace MapControl +{ + public partial class MapItem + { + public static readonly DependencyProperty AutoCollapseProperty = MapPanel.AutoCollapseProperty.AddOwner( + typeof(MapItem)); + + public static readonly DependencyProperty LocationProperty = MapPanel.LocationProperty.AddOwner( + typeof(MapItem), new FrameworkPropertyMetadata(null, + (o, e) => ((MapItem)o).UpdateMapTransform((Location)e.NewValue))); + + static MapItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(MapItem), new FrameworkPropertyMetadata(typeof(MapItem))); + } + + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + (ItemsControl.ItemsControlFromItemContainer(this) as MapItemsControl)?.OnItemClicked( + this, Keyboard.Modifiers.HasFlag(ModifierKeys.Control), Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)); + } + } +} diff --git a/MapControl/WPF/MapItemsControl.WPF.cs b/MapControl/WPF/MapItemsControl.WPF.cs index b7272340..57406e8e 100644 --- a/MapControl/WPF/MapItemsControl.WPF.cs +++ b/MapControl/WPF/MapItemsControl.WPF.cs @@ -4,29 +4,10 @@ using System.Windows; using System.Windows.Controls; -using System.Windows.Input; using System.Windows.Media; namespace MapControl { - public partial class MapItem - { - public static readonly DependencyProperty AutoCollapseProperty = MapPanel.AutoCollapseProperty.AddOwner(typeof(MapItem)); - - public static readonly DependencyProperty LocationProperty = MapPanel.LocationProperty.AddOwner(typeof(MapItem)); - - static MapItem() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(MapItem), new FrameworkPropertyMetadata(typeof(MapItem))); - } - - protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) - { - (ItemsControl.ItemsControlFromItemContainer(this) as MapItemsControl)?.OnItemClicked( - this, Keyboard.Modifiers.HasFlag(ModifierKeys.Control), Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)); - } - } - public partial class MapItemsControl { static MapItemsControl() diff --git a/MapControl/WinUI/MapItem.WinUI.cs b/MapControl/WinUI/MapItem.WinUI.cs new file mode 100644 index 00000000..88424748 --- /dev/null +++ b/MapControl/WinUI/MapItem.WinUI.cs @@ -0,0 +1,65 @@ +// XAML Map Control - https://github.com/ClemensFischer/XAML-Map-Control +// Copyright © 2023 Clemens Fischer +// Licensed under the Microsoft Public License (Ms-PL) + +using Windows.System; +#if WINUI +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +#else +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +#endif + +namespace MapControl +{ + public partial class MapItem + { + public static readonly DependencyProperty AutoCollapseProperty = DependencyProperty.Register( + nameof(AutoCollapse), typeof(bool), typeof(MapItem), new PropertyMetadata(false, + (o, e) => MapPanel.SetAutoCollapse((MapItem)o, (bool)e.NewValue))); + + public static readonly DependencyProperty LocationProperty = DependencyProperty.Register( + nameof(Location), typeof(Location), typeof(MapItem), new PropertyMetadata(null, + (o, e) => ((MapItem)o).LocationPropertyChanged((Location)e.NewValue))); + + private void LocationPropertyChanged(Location location) + { + MapPanel.SetLocation(this, location); + UpdateMapTransform(location); + } + + public MapItem() + { + DefaultStyleKey = typeof(MapItem); + MapPanel.InitMapElement(this); + } + + protected override void OnPointerPressed(PointerRoutedEventArgs e) + { + (ItemsControl.ItemsControlFromItemContainer(this) as MapItemsControl)?.OnItemClicked( + this, e.KeyModifiers.HasFlag(VirtualKeyModifiers.Control), e.KeyModifiers.HasFlag(VirtualKeyModifiers.Shift)); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + var parentMap = MapPanel.GetParentMap(this); + + if (parentMap != null) + { + // If this.Background is not explicitly set, bind it to parentMap.Background + this.SetBindingOnUnsetProperty(BackgroundProperty, parentMap, Panel.BackgroundProperty, nameof(Background)); + + // If this.Foreground is not explicitly set, bind it to parentMap.Foreground + this.SetBindingOnUnsetProperty(ForegroundProperty, parentMap, MapBase.ForegroundProperty, nameof(Foreground)); + + // If this.BorderBrush is not explicitly set, bind it to parentMap.Foreground + this.SetBindingOnUnsetProperty(BorderBrushProperty, parentMap, MapBase.ForegroundProperty, nameof(Foreground)); + } + } + } +} diff --git a/MapControl/WinUI/MapItemsControl.WinUI.cs b/MapControl/WinUI/MapItemsControl.WinUI.cs index f9be6819..38b10ed0 100644 --- a/MapControl/WinUI/MapItemsControl.WinUI.cs +++ b/MapControl/WinUI/MapItemsControl.WinUI.cs @@ -2,61 +2,14 @@ // Copyright © 2023 Clemens Fischer // Licensed under the Microsoft Public License (Ms-PL) -using Windows.System; #if WINUI using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Input; #else using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Input; #endif namespace MapControl { - public partial class MapItem - { - public static readonly DependencyProperty AutoCollapseProperty = DependencyProperty.Register( - nameof(AutoCollapse), typeof(bool), typeof(MapItem), - new PropertyMetadata(false, (o, e) => MapPanel.SetAutoCollapse((MapItem)o, (bool)e.NewValue))); - - public static readonly DependencyProperty LocationProperty = DependencyProperty.Register( - nameof(Location), typeof(Location), typeof(MapItem), - new PropertyMetadata(null, (o, e) => MapPanel.SetLocation((MapItem)o, (Location)e.NewValue))); - - public MapItem() - { - DefaultStyleKey = typeof(MapItem); - MapPanel.InitMapElement(this); - } - - protected override void OnPointerPressed(PointerRoutedEventArgs e) - { - (ItemsControl.ItemsControlFromItemContainer(this) as MapItemsControl)?.OnItemClicked( - this, e.KeyModifiers.HasFlag(VirtualKeyModifiers.Control), e.KeyModifiers.HasFlag(VirtualKeyModifiers.Shift)); - } - - protected override void OnApplyTemplate() - { - base.OnApplyTemplate(); - - var parentMap = MapPanel.GetParentMap(this); - - if (parentMap != null) - { - // If this.Background is not explicitly set, bind it to parentMap.Background - this.SetBindingOnUnsetProperty(BackgroundProperty, parentMap, Panel.BackgroundProperty, nameof(Background)); - - // If this.Foreground is not explicitly set, bind it to parentMap.Foreground - this.SetBindingOnUnsetProperty(ForegroundProperty, parentMap, MapBase.ForegroundProperty, nameof(Foreground)); - - // If this.BorderBrush is not explicitly set, bind it to parentMap.Foreground - this.SetBindingOnUnsetProperty(BorderBrushProperty, parentMap, MapBase.ForegroundProperty, nameof(Foreground)); - } - } - } - public partial class MapItemsControl { public MapItemsControl()