using System; namespace MapControl { [Flags] public enum ManipulationModes { None = 0, Translate = 1, Rotate = 2, Scale = 4, All = Translate | Rotate | Scale } /// /// MapBase with default input event handling. /// public class Map : MapBase { public static readonly StyledProperty ManipulationModesProperty = DependencyPropertyHelper.Register(nameof(ManipulationModes), ManipulationModes.Translate | ManipulationModes.Scale); public static readonly StyledProperty MouseWheelZoomDeltaProperty = DependencyPropertyHelper.Register(nameof(MouseWheelZoomDelta), 0.25); public static readonly DependencyProperty MouseWheelZoomAnimatedProperty = DependencyPropertyHelper.Register(nameof(MouseWheelZoomAnimated), true); private IPointer pointer1; private IPointer pointer2; private Point position1; private Point position2; public ManipulationModes ManipulationModes { get => GetValue(ManipulationModesProperty); set => SetValue(ManipulationModesProperty, value); } /// /// Gets or sets the amount by which the ZoomLevel property changes by a PointerWheelChanged event. /// The default value is 0.25. /// public double MouseWheelZoomDelta { get => GetValue(MouseWheelZoomDeltaProperty); set => SetValue(MouseWheelZoomDeltaProperty, value); } /// /// Gets or sets a value that controls whether zooming by a PointerWheelChanged event is animated. /// The default value is true. /// public bool MouseWheelZoomAnimated { get => (bool)GetValue(MouseWheelZoomAnimatedProperty); set => SetValue(MouseWheelZoomAnimatedProperty, value); } protected override void OnPointerWheelChanged(PointerWheelEventArgs e) { var delta = e.Delta.Y; var zoomLevel = TargetZoomLevel + MouseWheelZoomDelta * delta; var animated = false; if (delta % 1d == 0d) { // Zoom to integer multiple of MouseWheelZoomDelta when delta is an integer value, // i.e. when the event was actually raised by a mouse wheel and not by a touch pad // or a similar device with higher resolution. // zoomLevel = MouseWheelZoomDelta * Math.Round(zoomLevel / MouseWheelZoomDelta); animated = MouseWheelZoomAnimated; } ZoomMap(e.GetPosition(this), zoomLevel, animated); base.OnPointerWheelChanged(e); } protected override void OnPointerMoved(PointerEventArgs e) { var point = e.GetCurrentPoint(this); if (point.Pointer == pointer1 || point.Pointer == pointer2) { if (pointer2 != null) { HandleManipulation(point.Pointer, point.Position); } else if (point.Pointer.Type == PointerType.Mouse || ManipulationModes.HasFlag(ManipulationModes.Translate)) { TranslateMap(new Point(point.Position.X - position1.X, point.Position.Y - position1.Y)); position1 = point.Position; } } else if (pointer1 == null && point.Pointer.Type == PointerType.Mouse && point.Properties.IsLeftButtonPressed && e.KeyModifiers == KeyModifiers.None || pointer2 == null && point.Pointer.Type == PointerType.Touch && ManipulationModes != ManipulationModes.None) { point.Pointer.Capture(this); if (pointer1 == null) { pointer1 = point.Pointer; position1 = point.Position; } else { pointer2 = point.Pointer; position2 = point.Position; } } base.OnPointerMoved(e); } protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) { if (e.Pointer == pointer1 || e.Pointer == pointer2) { if (e.Pointer == pointer1) { pointer1 = pointer2; position1 = position2; } pointer2 = null; } base.OnPointerCaptureLost(e); } private void HandleManipulation(IPointer pointer, Point position) { var oldDistance = new Vector(position2.X - position1.X, position2.Y - position1.Y); var oldOrigin = new Point((position1.X + position2.X) / 2d, (position1.Y + position2.Y) / 2d); if (pointer == pointer1) { position1 = position; } else { position2 = position; } var newDistance = new Vector(position2.X - position1.X, position2.Y - position1.Y); var newOrigin = oldOrigin; var translation = new Point(); var rotation = 0d; var scale = 1d; if (ManipulationModes.HasFlag(ManipulationModes.Translate)) { newOrigin = new Point((position1.X + position2.X) / 2d, (position1.Y + position2.Y) / 2d); translation = newOrigin - oldOrigin; } if (ManipulationModes.HasFlag(ManipulationModes.Rotate)) { rotation = 180d / Math.PI * (Math.Atan2(newDistance.Y, newDistance.X) - Math.Atan2(oldDistance.Y, oldDistance.X)); } if (ManipulationModes.HasFlag(ManipulationModes.Scale)) { scale = newDistance.Length / oldDistance.Length; } TransformMap(newOrigin, translation, rotation, scale); } } }