mirror of
https://github.com/ClemensFischer/XAML-Map-Control.git
synced 2026-04-04 14:08:32 +00:00
MapBase dependency properties
This commit is contained in:
parent
abe3bb75f9
commit
74f4e0176b
14 changed files with 1262 additions and 1037 deletions
68
MapControl/WPF/DependencyPropertyHelper.WPF.cs
Normal file
68
MapControl/WPF/DependencyPropertyHelper.WPF.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// 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.Windows;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public static class DependencyPropertyHelper
|
||||
{
|
||||
public static DependencyProperty Register<TOwner, TValue>(
|
||||
string name,
|
||||
TValue defaultValue = default,
|
||||
bool bindTwoWayByDefault = false,
|
||||
Action<TOwner, TValue, TValue> changed = null,
|
||||
Func<TOwner, TValue, TValue> coerce = null)
|
||||
where TOwner : DependencyObject
|
||||
{
|
||||
var metadata = new FrameworkPropertyMetadata
|
||||
{
|
||||
DefaultValue = defaultValue,
|
||||
BindsTwoWayByDefault = bindTwoWayByDefault
|
||||
};
|
||||
|
||||
if (changed != null)
|
||||
{
|
||||
metadata.PropertyChangedCallback = (o, e) => changed((TOwner)o, (TValue)e.OldValue, (TValue)e.NewValue);
|
||||
}
|
||||
|
||||
if (coerce != null)
|
||||
{
|
||||
metadata.CoerceValueCallback = (o, v) => coerce((TOwner)o, (TValue)v);
|
||||
}
|
||||
|
||||
return DependencyProperty.Register(name, typeof(TValue), typeof(TOwner), metadata);
|
||||
}
|
||||
|
||||
public static DependencyProperty RegisterAttached<TOwner, TValue>(
|
||||
string name,
|
||||
TValue defaultValue = default,
|
||||
bool inherits = false,
|
||||
Action<FrameworkElement, TValue, TValue> changed = null)
|
||||
where TOwner : DependencyObject
|
||||
{
|
||||
var metadata = new FrameworkPropertyMetadata
|
||||
{
|
||||
DefaultValue = defaultValue,
|
||||
Inherits = inherits
|
||||
};
|
||||
|
||||
if (changed != null)
|
||||
{
|
||||
metadata.PropertyChangedCallback = (o, e) => changed((FrameworkElement)o, (TValue)e.OldValue, (TValue)e.NewValue);
|
||||
}
|
||||
|
||||
return DependencyProperty.RegisterAttached(name, typeof(TValue), typeof(TOwner), metadata);
|
||||
}
|
||||
|
||||
public static DependencyPropertyKey RegisterReadOnly<TOwner, TValue>(
|
||||
string name,
|
||||
TValue defaultValue = default)
|
||||
where TOwner : DependencyObject
|
||||
{
|
||||
return DependencyProperty.RegisterReadOnly(name, typeof(TValue), typeof(TOwner), new PropertyMetadata(defaultValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
43
MapControl/WPF/LocationAnimation.cs
Normal file
43
MapControl/WPF/LocationAnimation.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// 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.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public class LocationAnimation : AnimationTimeline
|
||||
{
|
||||
public override Type TargetPropertyType => typeof(Location);
|
||||
|
||||
public Location To { get; set; }
|
||||
|
||||
public IEasingFunction EasingFunction { get; set; }
|
||||
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new LocationAnimation
|
||||
{
|
||||
To = To,
|
||||
EasingFunction = EasingFunction
|
||||
};
|
||||
}
|
||||
|
||||
public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
|
||||
{
|
||||
var from = (Location)defaultOriginValue;
|
||||
var progress = animationClock.CurrentProgress ?? 1d;
|
||||
|
||||
if (EasingFunction != null)
|
||||
{
|
||||
progress = EasingFunction.Ease(progress);
|
||||
}
|
||||
|
||||
return new Location(
|
||||
(1d - progress) * from.Latitude + progress * To.Latitude,
|
||||
(1d - progress) * from.Longitude + progress * To.Longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,53 +3,65 @@
|
|||
// Licensed under the Microsoft Public License (Ms-PL)
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace MapControl
|
||||
{
|
||||
public partial class MapBase
|
||||
{
|
||||
public static readonly DependencyProperty CenterProperty = DependencyProperty.Register(
|
||||
nameof(Center), typeof(Location), typeof(MapBase), new FrameworkPropertyMetadata(
|
||||
new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((MapBase)o).CenterPropertyChanged((Location)e.NewValue)));
|
||||
public static readonly DependencyProperty AnimationEasingFunctionProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, IEasingFunction>(nameof(AnimationEasingFunction),
|
||||
new QuadraticEase { EasingMode = EasingMode.EaseOut });
|
||||
|
||||
public static readonly DependencyProperty TargetCenterProperty = DependencyProperty.Register(
|
||||
nameof(TargetCenter), typeof(Location), typeof(MapBase), new FrameworkPropertyMetadata(
|
||||
new Location(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((MapBase)o).TargetCenterPropertyChanged((Location)e.NewValue)));
|
||||
public static readonly DependencyProperty CenterProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, Location>(nameof(Center), new Location(), true,
|
||||
(map, oldValue, newValue) => map.CenterPropertyChanged(newValue),
|
||||
(map, value) => map.CoerceCenterProperty(value));
|
||||
|
||||
public static readonly DependencyProperty ZoomLevelProperty = DependencyProperty.Register(
|
||||
nameof(ZoomLevel), typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(
|
||||
1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((MapBase)o).ZoomLevelPropertyChanged((double)e.NewValue)));
|
||||
public static readonly DependencyProperty TargetCenterProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, Location>(nameof(TargetCenter), new Location(), true,
|
||||
(map, oldValue, newValue) => map.TargetCenterPropertyChanged(newValue),
|
||||
(map, value) => map.CoerceCenterProperty(value));
|
||||
|
||||
public static readonly DependencyProperty TargetZoomLevelProperty = DependencyProperty.Register(
|
||||
nameof(TargetZoomLevel), typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(
|
||||
1d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((MapBase)o).TargetZoomLevelPropertyChanged((double)e.NewValue)));
|
||||
public static readonly DependencyProperty MinZoomLevelProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, double>(nameof(MinZoomLevel), 1d, false,
|
||||
(map, oldValue, newValue) => map.MinZoomLevelPropertyChanged(newValue),
|
||||
(map, value) => map.CoerceMinZoomLevelProperty(value));
|
||||
|
||||
public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register(
|
||||
nameof(Heading), typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(
|
||||
0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((MapBase)o).HeadingPropertyChanged((double)e.NewValue)));
|
||||
public static readonly DependencyProperty MaxZoomLevelProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, double>(nameof(MaxZoomLevel), 20d, false,
|
||||
(map, oldValue, newValue) => map.MaxZoomLevelPropertyChanged(newValue),
|
||||
(map, value) => map.CoerceMinZoomLevelProperty(value));
|
||||
|
||||
public static readonly DependencyProperty TargetHeadingProperty = DependencyProperty.Register(
|
||||
nameof(TargetHeading), typeof(double), typeof(MapBase), new FrameworkPropertyMetadata(
|
||||
0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, e) => ((MapBase)o).TargetHeadingPropertyChanged((double)e.NewValue)));
|
||||
public static readonly DependencyProperty ZoomLevelProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, double>(nameof(ZoomLevel), 1d, true,
|
||||
(map, oldValue, newValue) => map.ZoomLevelPropertyChanged(newValue),
|
||||
(map, value) => map.CoerceZoomLevelProperty(value));
|
||||
|
||||
private static readonly DependencyPropertyKey ViewScalePropertyKey = DependencyProperty.RegisterReadOnly(
|
||||
nameof(ViewScale), typeof(double), typeof(MapBase), new PropertyMetadata(0d));
|
||||
public static readonly DependencyProperty TargetZoomLevelProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, double>(nameof(TargetZoomLevel), 1d, true,
|
||||
(map, oldValue, newValue) => map.TargetZoomLevelPropertyChanged(newValue),
|
||||
(map, value) => map.CoerceZoomLevelProperty(value));
|
||||
|
||||
public static readonly DependencyProperty HeadingProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, double>(nameof(Heading), 0d, true,
|
||||
(map, oldValue, newValue) => map.HeadingPropertyChanged(newValue),
|
||||
(map, value) => map.CoerceHeadingProperty(value));
|
||||
|
||||
public static readonly DependencyProperty TargetHeadingProperty =
|
||||
DependencyPropertyHelper.Register<MapBase, double>(nameof(TargetHeading), 0d, true,
|
||||
(map, oldValue, newValue) => map.TargetHeadingPropertyChanged(newValue),
|
||||
(map, value) => map.CoerceHeadingProperty(value));
|
||||
|
||||
private static readonly DependencyPropertyKey ViewScalePropertyKey =
|
||||
DependencyPropertyHelper.RegisterReadOnly<MapBase, double>(nameof(ViewScale), 0d);
|
||||
|
||||
public static readonly DependencyProperty ViewScaleProperty = ViewScalePropertyKey.DependencyProperty;
|
||||
|
||||
private static readonly DependencyProperty CenterPointProperty = DependencyProperty.Register(
|
||||
"CenterPoint", typeof(Point), typeof(MapBase), new PropertyMetadata(new Point(),
|
||||
(o, e) =>
|
||||
{
|
||||
var center = (Point)e.NewValue;
|
||||
((MapBase)o).CenterPointPropertyChanged(new Location(center.Y, center.X));
|
||||
}));
|
||||
private LocationAnimation centerAnimation;
|
||||
private DoubleAnimation zoomLevelAnimation;
|
||||
private DoubleAnimation headingAnimation;
|
||||
|
||||
static MapBase()
|
||||
{
|
||||
|
|
@ -57,6 +69,36 @@ namespace MapControl
|
|||
DefaultStyleKeyProperty.OverrideMetadata(typeof(MapBase), new FrameworkPropertyMetadata(typeof(MapBase)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the EasingFunction of the Center, ZoomLevel and Heading animations.
|
||||
/// The default value is a QuadraticEase with EasingMode.EaseOut.
|
||||
/// </summary>
|
||||
public IEasingFunction AnimationEasingFunction
|
||||
{
|
||||
get => (IEasingFunction)GetValue(AnimationEasingFunctionProperty);
|
||||
set => SetValue(AnimationEasingFunctionProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaling factor from projected map coordinates to view coordinates,
|
||||
/// as pixels per meter.
|
||||
/// </summary>
|
||||
public double ViewScale => (double)GetValue(ViewScaleProperty);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a transform Matrix for scaling and rotating objects that are anchored
|
||||
/// at a Location from map coordinates (i.e. meters) to view coordinates.
|
||||
/// </summary>
|
||||
public Matrix GetMapTransform(Location location)
|
||||
{
|
||||
var scale = GetScale(location);
|
||||
|
||||
var transform = new Matrix(scale.X, 0d, 0d, scale.Y, 0d, 0d);
|
||||
transform.Rotate(ViewTransform.Rotation);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
|
||||
{
|
||||
base.OnRenderSizeChanged(sizeInfo);
|
||||
|
|
@ -69,5 +111,179 @@ namespace MapControl
|
|||
{
|
||||
SetValue(ViewScalePropertyKey, scale);
|
||||
}
|
||||
|
||||
private void CenterPropertyChanged(Location center)
|
||||
{
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
UpdateTransform();
|
||||
|
||||
if (centerAnimation == null)
|
||||
{
|
||||
SetValueInternal(TargetCenterProperty, center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TargetCenterPropertyChanged(Location targetCenter)
|
||||
{
|
||||
if (!internalPropertyChange && !targetCenter.Equals(Center))
|
||||
{
|
||||
if (centerAnimation != null)
|
||||
{
|
||||
centerAnimation.Completed -= CenterAnimationCompleted;
|
||||
}
|
||||
|
||||
centerAnimation = new LocationAnimation
|
||||
{
|
||||
To = new Location(targetCenter.Latitude, ConstrainedLongitude(targetCenter.Longitude)),
|
||||
Duration = AnimationDuration,
|
||||
EasingFunction = AnimationEasingFunction
|
||||
};
|
||||
|
||||
centerAnimation.Completed += CenterAnimationCompleted;
|
||||
|
||||
BeginAnimation(CenterProperty, centerAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
private void CenterAnimationCompleted(object sender, object e)
|
||||
{
|
||||
if (centerAnimation != null)
|
||||
{
|
||||
SetValueInternal(CenterProperty, TargetCenter);
|
||||
UpdateTransform();
|
||||
|
||||
centerAnimation.Completed -= CenterAnimationCompleted;
|
||||
centerAnimation = null;
|
||||
|
||||
BeginAnimation(CenterProperty, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void MinZoomLevelPropertyChanged(double minZoomLevel)
|
||||
{
|
||||
if (ZoomLevel < minZoomLevel)
|
||||
{
|
||||
ZoomLevel = minZoomLevel;
|
||||
}
|
||||
}
|
||||
|
||||
private void MaxZoomLevelPropertyChanged(double maxZoomLevel)
|
||||
{
|
||||
if (ZoomLevel > maxZoomLevel)
|
||||
{
|
||||
ZoomLevel = maxZoomLevel;
|
||||
}
|
||||
}
|
||||
|
||||
private void ZoomLevelPropertyChanged(double zoomLevel)
|
||||
{
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
UpdateTransform();
|
||||
|
||||
if (zoomLevelAnimation == null)
|
||||
{
|
||||
SetValueInternal(TargetZoomLevelProperty, zoomLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TargetZoomLevelPropertyChanged(double targetZoomLevel)
|
||||
{
|
||||
if (!internalPropertyChange && targetZoomLevel != ZoomLevel)
|
||||
{
|
||||
if (zoomLevelAnimation != null)
|
||||
{
|
||||
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
||||
}
|
||||
|
||||
zoomLevelAnimation = new DoubleAnimation
|
||||
{
|
||||
To = targetZoomLevel,
|
||||
Duration = AnimationDuration,
|
||||
EasingFunction = AnimationEasingFunction
|
||||
};
|
||||
|
||||
zoomLevelAnimation.Completed += ZoomLevelAnimationCompleted;
|
||||
|
||||
BeginAnimation(ZoomLevelProperty, zoomLevelAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
private void ZoomLevelAnimationCompleted(object sender, object e)
|
||||
{
|
||||
if (zoomLevelAnimation != null)
|
||||
{
|
||||
SetValueInternal(ZoomLevelProperty, TargetZoomLevel);
|
||||
UpdateTransform(true);
|
||||
|
||||
zoomLevelAnimation.Completed -= ZoomLevelAnimationCompleted;
|
||||
zoomLevelAnimation = null;
|
||||
|
||||
BeginAnimation(ZoomLevelProperty, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void HeadingPropertyChanged(double heading)
|
||||
{
|
||||
if (!internalPropertyChange)
|
||||
{
|
||||
UpdateTransform();
|
||||
|
||||
if (headingAnimation == null)
|
||||
{
|
||||
SetValueInternal(TargetHeadingProperty, heading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TargetHeadingPropertyChanged(double targetHeading)
|
||||
{
|
||||
if (!internalPropertyChange && targetHeading != Heading)
|
||||
{
|
||||
var delta = targetHeading - Heading;
|
||||
|
||||
if (delta > 180d)
|
||||
{
|
||||
delta -= 360d;
|
||||
}
|
||||
else if (delta < -180d)
|
||||
{
|
||||
delta += 360d;
|
||||
}
|
||||
|
||||
if (headingAnimation != null)
|
||||
{
|
||||
headingAnimation.Completed -= HeadingAnimationCompleted;
|
||||
}
|
||||
|
||||
headingAnimation = new DoubleAnimation
|
||||
{
|
||||
By = delta,
|
||||
Duration = AnimationDuration,
|
||||
EasingFunction = AnimationEasingFunction
|
||||
};
|
||||
|
||||
headingAnimation.Completed += HeadingAnimationCompleted;
|
||||
|
||||
BeginAnimation(HeadingProperty, headingAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
private void HeadingAnimationCompleted(object sender, object e)
|
||||
{
|
||||
if (headingAnimation != null)
|
||||
{
|
||||
SetValueInternal(HeadingProperty, TargetHeading);
|
||||
UpdateTransform();
|
||||
|
||||
headingAnimation.Completed -= HeadingAnimationCompleted;
|
||||
headingAnimation = null;
|
||||
|
||||
BeginAnimation(HeadingProperty, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue