I’m creating a my own modifier that implements bloomberg behaviour (i need autorange and scale at the same time).
Code you can see below. It’s works good but there are several problems.
1) Method OnParentSurfaceRendered is not called when i change a data series.
2) When i stretch x axis the difference of currentPoint.X and panLastPoint.X in OnModifierMouseMove became a zero. It seems that a ModifierMouseArgs.MousePoint in “data series” coordinate but i need a mouse screen coordinate. How i can get a mouse screen coordinate in OnMoidiferMouseMove?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Abt.Controls.SciChart.ChartModifiers;
using Abt.Controls.SciChart;
using Abt.Controls.SciChart.Visuals.Axes;
using Abt.Controls.SciChart.Visuals;
using System.Collections.Concurrent;
using Aton.Graphics.Models;
using System.Windows.Input;
namespace Aton.Graphics.Modifiers
{
public class BloombergModifier : ChartModifierBase
{
private readonly ConcurrentDictionary<IAxis, Double> currentZoom = new ConcurrentDictionary<IAxis, Double>();
private const Double deltaForMove = 30;
private const Double scaleFactor = 0.008;
private const Double scaleLowBound = -0.45;
private Double yMinPixel = Double.NaN;
private Double yMaxPixel = Double.NaN;
private Double yScale = Double.NaN;
private Double globalYDelta;
private DoubleRange yRange = null;
private Point? panLastPoint = null;
private Point? zoomLastPoint = null;
public override void OnParentSurfaceRendered(SciChartRenderedMessage e)
{
base.OnParentSurfaceRendered(e);
System.Diagnostics.Debug.WriteLine("{0}: Render draw", DateTime.Now);
using (ParentSurface.SuspendUpdates())
{
Draw();
}
}
public override void OnModifierMouseDown(ModifierMouseArgs e)
{
base.OnModifierMouseDown(e);
if (!e.IsMaster) return;
if (!e.MouseButtons.HasFlag(MouseButtons.Left)) return;
foreach (var series in ParentSurface.SeriesSource)
{
series.RenderSeries.IsSelected = false;
}
if (HitTestableContainsMousePoint(YAxis, e))
{
zoomLastPoint = e.MousePoint;
ModifierSurface.CaptureMouse();
e.Handled = true;
}
else if (!HitTestableContainsMousePoint(XAxis, e))
{
panLastPoint = e.MousePoint;
ModifierSurface.CaptureMouse();
e.Handled = true;
}
}
public override void OnModifierMouseUp(ModifierMouseArgs e)
{
base.OnModifierMouseUp(e);
if (!e.MouseButtons.HasFlag(MouseButtons.Left)) return;
if (e.IsMaster)
{
ModifierSurface.ReleaseMouseCapture();
zoomLastPoint = panLastPoint = null;
e.Handled = true;
}
}
public override void OnModifierMouseMove(ModifierMouseArgs e)
{
base.OnModifierMouseMove(e);
if (!e.IsMaster) return;
var currentPoint = e.MousePoint;
if (zoomLastPoint != null)
{
if (yRange == null) return;
var yDelta = zoomLastPoint.Value.Y - currentPoint.Y;
currentZoom.AddOrUpdate(YAxis, 0.0, (k, v) => Math.Max(v + scaleFactor * (yDelta >= 0 ? 1 : -1), scaleLowBound));
using (ParentSurface.SuspendUpdates())
{
Draw();
}
zoomLastPoint = currentPoint;
e.Handled = true;
}
else if (panLastPoint != null)
{
var xDelta = currentPoint.X - panLastPoint.Value.X;
var yDelta = panLastPoint.Value.Y - currentPoint.Y;
globalYDelta += yDelta;
System.Diagnostics.Debug.WriteLine("{1}: Scroll: {0}", xDelta, DateTime.Now);
using (ParentSurface.SuspendUpdates())
{
Draw();
XAxis.Scroll(xDelta, ClipMode.None);
}
panLastPoint = currentPoint;
e.Handled = true;
}
else
{
if (HitTestableContainsMousePoint(YAxis, e))
{
SetCursor(System.Windows.Input.Cursors.SizeNS);
}
else
{
SetCursor(System.Windows.Input.Cursors.Arrow);
}
}
}
protected override void OnXAxesCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnXAxesCollectionChanged(sender, e);
switch (e.Action)
{
case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
foreach (IAxis axis in e.NewItems)
{
axis.VisibleRangeChanged += XAxisVisibleRangeChanged;
}
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
foreach (IAxis axis in e.OldItems)
{
axis.VisibleRangeChanged -= XAxisVisibleRangeChanged;
}
break;
}
}
private void XAxisVisibleRangeChanged(object sender, VisibleRangeChangedEventArgs e)
{
SetYRange();
}
private void SetYRange()
{
this.yRange = YAxis != null ? (DoubleRange)YAxis.GetWindowedYRange(new Dictionary<String, IRange>
{
{ "test", XAxis.VisibleRange }
}) : null;
}
private Boolean HitTestableContainsMousePoint(IHitTestable hitTestable, ModifierMouseArgs e)
{
Rect bounds = hitTestable.GetBoundsRelativeTo(RootGrid);
return bounds.Contains(e.MousePoint);
}
private void Draw()
{
if (yRange == null) { SetYRange(); }
if (yRange == null) return;
Double realYDelta = Math.Abs(globalYDelta) < deltaForMove ? 0 : globalYDelta;
yMinPixel = YAxis.GetCoordinate(yRange.Min) + realYDelta;
yMaxPixel = YAxis.GetCoordinate(yRange.Max) + realYDelta;
yScale = currentZoom.GetOrAdd(YAxis, 0);
if (!Double.IsNaN(yMinPixel) && !Double.IsNaN(yMaxPixel) && !Double.IsNaN(yScale))
{
YAxis.Zoom(yMinPixel, yMaxPixel);
YAxis.ZoomBy(yScale, yScale);
}
}
}
}
- Ivan Zakharov asked 9 years ago
- You must login to post comments
It’s funny about 5 people have asked in the last few days ‘how do I zoom with autorange’. We have some workarounds documented below
To answer your questions:
-
OnParentSurfaceRendered should be called if the data series is changed. This is called whenever the chart redraws, which is whenever a property on the chart changes, or data changes. If not, you can always force it by calling SciChartSurface.InvalidateElement()
-
To convert pixel to data coordinates, see our article ‘How to convert pixel to data coordinates‘
Finally, if you haven’t already seen it, you might want to check out our excellent series on the ChartModifier API.
Hope this helps!
Best regards,
Andrew
- Andrew Burnett-Thompson answered 9 years ago
- You must login to post comments
Please login first to submit.