protected override void OnMouseWheel(MouseWheelEventArgs args) { base.OnMouseWheel(args); // If there is a mouse op in progress, forward the event var op = MouseOperations.Active; if (op != null && !op.Cancelled) { op.MouseWheel(args); if (args.Handled) { return; } } var location = args.GetPosition(this); var along_ray = Options.MouseCentredZoom || Keyboard.Modifiers.HasFlag(ModifierKeys.Alt); var chart_pt = ClientToChart(location); var hit = HitTestZoneCS(location, Keyboard.Modifiers, args.ToMouseBtns()); // Batch mouse wheel events into 100ms groups var defer_nav_checkpoint = DeferNavCheckpoints(); Dispatcher_.BeginInvokeDelayed(() => { Util.Dispose(ref defer_nav_checkpoint !); SaveNavCheckpoint(); }, TimeSpan.FromMilliseconds(100)); var scale = 0.001f; if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) { scale *= 0.1f; } if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { scale *= 0.01f; } var delta = Math_.Clamp(args.Delta * scale, -0.999f, 0.999f); var chg = (string?)null; // If zooming is allowed on both axes, translate the camera if (hit.Zone == EZone.Chart && XAxis.AllowZoom && YAxis.AllowZoom) { // Translate the camera along a ray through 'point' var loc = Gui_.MapPoint(this, Scene, location); Scene.Window.MouseNavigateZ(loc.ToPointF(), args.ToMouseBtns(Keyboard.Modifiers), args.Delta, along_ray); chg = nameof(SetRangeFromCamera); } // Otherwise, zoom on the allowed axis only else if (hit.Zone == EZone.XAxis || (hit.Zone == EZone.Chart && !YAxis.AllowZoom)) { if (hit.ModifierKeys.HasFlag(ModifierKeys.Control) && XAxis.AllowScroll) { // Scroll the XAxis XAxis.Shift(XAxis.Span * delta); chg = nameof(SetCameraFromRange); } else if (!hit.ModifierKeys.HasFlag(ModifierKeys.Control) && XAxis.AllowZoom) { // Change the aspect ratio by zooming on the XAxis var x = along_ray ? chart_pt.X : XAxis.Centre; var left = (XAxis.Min - x) * (1f - delta); var rite = (XAxis.Max - x) * (1f - delta); XAxis.Set(x + left, x + rite); if (Options.LockAspect != null) { YAxis.Span *= (1f - delta); } chg = nameof(SetCameraFromRange); } } else if (hit.Zone == EZone.YAxis || (hit.Zone == EZone.Chart && !XAxis.AllowZoom)) { if (hit.ModifierKeys.HasFlag(ModifierKeys.Control) && YAxis.AllowScroll) { // Scroll the YAxis YAxis.Shift(YAxis.Span * delta); chg = nameof(SetCameraFromRange); } else if (!hit.ModifierKeys.HasFlag(ModifierKeys.Control) && YAxis.AllowZoom) { // Change the aspect ratio by zooming on the YAxis var y = along_ray ? chart_pt.Y : YAxis.Centre; var left = (YAxis.Min - y) * (1f - delta); var rite = (YAxis.Max - y) * (1f - delta); YAxis.Set(y + left, y + rite); if (Options.LockAspect != null) { XAxis.Span *= (1f - delta); } chg = nameof(SetCameraFromRange); } } switch (chg) { // Update the axes from the camera position case nameof(SetRangeFromCamera): SetRangeFromCamera(); NotifyPropertyChanged(nameof(ValueAtPointer)); Invalidate(); break; // Set the camera position from the new axis ranges case nameof(SetCameraFromRange): SetCameraFromRange(); NotifyPropertyChanged(nameof(ValueAtPointer)); Invalidate(); break; } }