SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and now iOS Charting & Android Chart Components

Answered
0
0

I need to graph have autorange and ZoomPanModifier. As a result the graph can moved only by X axis. But i want to be able to move by Y axis. It is behavior like a bloomberg.

  • You must to post comments
Best Answer
0
0

After a brainstorm i create this modifier, that satisfies my reqirements. If anyone sees an errors in my code write here please.

public class BloombergModifier : ChartModifierBase
{
    private class ChangableValue
    {
        public Double Current { get; private set; }
        public Double New { get; set; }
        public Boolean IsChange { get { return Math.Abs(New - Current) > Double.Epsilon; } }

        public ChangableValue()
        {
            Current = Double.NaN;
        }

        public void ApplyChange()
        {
            Current = New;
        }
    }

    private readonly ConcurrentDictionary<IAxis, Double> currentZoom = new ConcurrentDictionary<IAxis, Double>();
    private readonly ChangableValue yMinPixel = new ChangableValue();
    private readonly ChangableValue yMaxPixel = new ChangableValue();
    private readonly ChangableValue yScale = new ChangableValue();

    private Double globalYDelta;
    private DoubleRange yRange = null;
    private Point? panLastPoint = null;
    private Point? zoomLastPoint = null;

    public override void OnParentSurfaceRendered(SciChartRenderedMessage e)
    {
        base.OnParentSurfaceRendered(e);

        using (ParentSurface.SuspendUpdates())
        {
            Draw();
        }
    }

    public override void OnModifierMouseDown(ModifierMouseArgs e)
    {
        base.OnModifierMouseDown(e);

        if (!e.IsMaster) return;

        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);
        ModifierSurface.ReleaseMouseCapture();
        zoomLastPoint = panLastPoint = null;
    }

    public override void OnModifierMouseMove(ModifierMouseArgs e)
    {
        base.OnModifierMouseMove(e);

        var currentPoint = e.MousePoint;

        if (zoomLastPoint != null)
        {
            const Double factor = 0.008;
            const Double lowBound = -0.45;
            var yDelta = zoomLastPoint.Value.Y - currentPoint.Y;
            var r1 = YAxis.GetCoordinate(yRange.Min) + globalYDelta;
            var r2 = YAxis.GetCoordinate(yRange.Max) + globalYDelta;

            currentZoom.AddOrUpdate(YAxis, 0.0, (k, v) => Math.Max(v + factor * (yDelta >= 0 ? 1 : -1), lowBound));

            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;

            using (ParentSurface.SuspendUpdates())
            {
                XAxis.Scroll(xDelta, ClipMode.None);
                Draw();
            }

            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()
    {
        yRange = (DoubleRange)YAxis.GetWindowedYRange(new Dictionary<String, IRange>
            {
                { "test", XAxis.VisibleRange }
            });
    }

    private Boolean HitTestableContainsMousePoint(IHitTestable hitTestable, ModifierMouseArgs e)
    {
        Rect bounds = hitTestable.GetBoundsRelativeTo(RootGrid);
        return bounds.Contains(e.MousePoint);
    }

    private void Draw()
    {
        if (yRange == null) { SetYRange(); }

        yMinPixel.New = YAxis.GetCoordinate(yRange.Min) + globalYDelta;
        yMaxPixel.New = YAxis.GetCoordinate(yRange.Max) + globalYDelta;
        yScale.New = currentZoom.GetOrAdd(YAxis, 0);

        if (yMinPixel.IsChange || yMaxPixel.IsChange || yScale.IsChange)
        {
            YAxis.Zoom(yMinPixel.New, yMaxPixel.New);
            YAxis.ZoomBy(yScale.New, yScale.New);
        }

        yMinPixel.ApplyChange();
        yMaxPixel.ApplyChange();
        yScale.ApplyChange();
    }
}
  • You must to post comments
1
0

Hi there,

I think you can find some answers to your question in the article How to have a fixed scrolling time range that works with Modifiers. Here we present some ideas / examples of how to mimic AutoRange while keeping modifiers interactive. Some of our examples actually do this now.

Take a look above and see what you think.

Best regards,
Andrew

  • You must to post comments
0
0

Thank you for your answering. This article help me to create this modifier:

public class SimpleZoomPanModifier : ChartModifierBase
{
    private Point? _lastPoint;
    private Double _yDelta = 0;

    public override void OnParentSurfaceRendered(SciChartRenderedMessage e)
    {
        base.OnParentSurfaceRendered(e);

        using (ParentSurface.SuspendUpdates())
        {
            AutoRange();
        }
    }

    public override void OnModifierMouseDown(ModifierMouseArgs e)
    {
        base.OnModifierMouseDown(e);

        e.Handled = true;
        _lastPoint = e.MousePoint;
        ModifierSurface.CaptureMouse();
    }

    public override void OnModifierMouseMove(ModifierMouseArgs e)
    {
        base.OnModifierMouseMove(e);

        if (_lastPoint == null) return;

        var currentPoint = e.MousePoint;
        var xDelta = currentPoint.X - _lastPoint.Value.X;

        _yDelta += _lastPoint.Value.Y - currentPoint.Y;

        using (ParentSurface.SuspendUpdates())
        {
            XAxis.Scroll(XAxis.IsHorizontalAxis ? xDelta : -_yDelta, ClipMode.None);
            AutoRange();
        }

        _lastPoint = currentPoint;
    }

    private void AutoRange()
    {

        var rng = YAxis.GetWindowedYRange(new Dictionary<String, IRange>
            {
                { "test", XAxis.VisibleRange }
            });
        var r1 = YAxis.GetCoordinate(rng.Min);
        var r2 = YAxis.GetCoordinate(rng.Max);
        var yVMinPrev = YAxis.GetCoordinate((Double)YAxis.VisibleRange.Min);
        var yCurDelta = YAxis.GetCoordinate((Double)rng.Min) - yVMinPrev;

        YAxis.Zoom(r1, r2);
        YAxis.Scroll(_yDelta, ClipMode.None);
    }

    public override void OnModifierMouseUp(ModifierMouseArgs e)
    {
        base.OnModifierMouseUp(e);
        _lastPoint = null;
        ModifierSurface.ReleaseMouseCapture();
    }
}

This works good with one chart. But when i create two charts with common MouseEventGroup that i can scroll by yAxis only upper chart (OnModifierMouseDown called only for first chart).

<s:SciChartSurface s:SciChartGroup.VerticalChartGroup="{Binding Parent.VerticalChartGroupId}"
                   ViewportManager="{Binding Parent.ViewportManager}">
    <s:SciChartSurface.ChartModifier>
        <s:ModifierGroup s:MouseManager.MouseEventGroup="{Binding Parent.SharedMouseGroupId, Mode=TwoWay}">
            <c:SimpleZoomPanModifier ExecuteOn="MouseLeftButton"/>
            <s:MouseWheelZoomModifier />
            <s:YAxisDragModifier/>
            <s:XAxisDragModifier/>
            <s:ZoomExtentsModifier ExecuteOn="MouseDoubleClick"/>
        </s:ModifierGroup>
    </s:SciChartSurface.ChartModifier>
  • l1pton17
    I solve the problem by using property IsMaster:public class SimpleZoomPanModifier : ChartModifierBase { private Point? _lastPoint; private Double _yDelta;public override void OnParentSurfaceRendered(SciChartRenderedMessage e) { base.OnParentSurfaceRendered(e);System.Diagnostics.Debug.WriteLine("{0}: Render not master for {1}", DateTime.Now.TimeOfDay, ParentSurface.RenderableSeries.FirstOrDefault().DataSeries.SeriesName); using (ParentSurface.SuspendUpdates()) { AutoRange(); } }public override void OnModifierMouseDown(ModifierMouseArgs e) { base.OnModifierMouseDown(e);if (!e.IsMaster) return;System.Diagnostics.Debug.WriteLine("{0} for {1}", e.IsMaster, ParentSurface.RenderableSeries.FirstOrDefault().DataSeries.SeriesName);e.Handled = true;_lastPoint = e.MousePoint; ModifierSurface.CaptureMouse(); System.Diagnostics.Debug.WriteLine("{0}: Mouse down for {1}", DateTime.Now.TimeOfDay, ParentSurface.RenderableSeries.FirstOrDefault().DataSeries.SeriesName); }public override void OnModifierMouseMove(ModifierMouseArgs e) { base.OnModifierMouseMove(e);if (_lastPoint == null) return;var currentPoint = e.MousePoint; var xDelta = currentPoint.X - _lastPoint.Value.X;_yDelta += _lastPoint.Value.Y - currentPoint.Y;using (ParentSurface.SuspendUpdates()) { XAxis.Scroll(XAxis.IsHorizontalAxis ? xDelta : -_yDelta, ClipMode.None); AutoRange(); }_lastPoint = currentPoint; }private void AutoRange() { var rng = YAxis.GetWindowedYRange(new Dictionary<String, IRange> { { "test", XAxis.VisibleRange } }); var r1 = YAxis.GetCoordinate(rng.Min); var r2 = YAxis.GetCoordinate(rng.Max); var yVMinPrev = YAxis.GetCoordinate((Double)YAxis.VisibleRange.Min); var yCurDelta = YAxis.GetCoordinate((Double)rng.Min) - yVMinPrev;YAxis.Zoom(r1, r2); YAxis.Scroll(_yDelta, ClipMode.None); }public override void OnModifierMouseUp(ModifierMouseArgs e) { base.OnModifierMouseUp(e); _lastPoint = null; ModifierSurface.ReleaseMouseCapture(); } }
  • l1pton17
    But now YAxisDragModifier doesn't work, because i zoom manually if my modifier. I'm trying to implement YAxisDragModifier behaviour but i don't know how to check that mouse down event occured on axis. Could you help me?
  • You must to post comments
Showing 3 results
Your Answer

Please first to submit.