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

1
0

Hello everyone,

I have a chart which has data added to its plots at inconsistent time intervals.
My intention is to be able to limit the displayed data by their x-values, for example only showing the last 60 seconds. At the same time I wish to be able to zoom/pan back to the old data, so I can not discard it, and from what I saw “VisibleRangeLimit” did not work this way either, as it limited every zoom or pan action to the given range limit. Mainly I need the auto ranging to limit the display to my limits (and return the graph to its scrolling mode), while every other zoom/pan interaction stops the scrolling without being limited.

The solution I came up with for this is to “move” the VisibleRange of the axis whenever new data points outside the current displayed range are added to the chart’s plots. (For example if a point is plotted at 61 seconds, the VisibleRange moves to min=1 and max=61.)

I made this into an axis class, which inherits the NumericAxis and uses DataRangeChanged to change the VisibleRange on data changes and VisibleRangeChanged to react to auto ranging and zooming/panning.

private void UpdateVisibleRange()
{
    DoubleRange visibleRange = (DoubleRange)GetMaximumRange();
    visibleRange.Min = visibleRange.Max > ViewLimit ? visibleRange.Max - ViewLimit : 0;
    VisibleRange = visibleRange;
}

// DataRangeChanged += DataRangeChangedHandler;
private void DataRangeChangedHandler(Object sender, EventArgs e)
{
    if (IsAutoRangeActive)
    {
        UpdateVisibleRange();
        IsAutoRangeActive = true;
    }
}

// VisibleRangeChanged += VisibleRangeChangedHandler;
private void VisibleRangeChangedHandler(Object sender, VisibleRangeChangedEventArgs e)
{
    if (base.AutoRange == AutoRange.Always)
    {
        base.AutoRange = AutoRange.Never;
        UpdateVisibleRange();
        IsAutoRangeActive = true;
    }
    else if (IsAutoRangeActive)
    {
        IsAutoRangeActive = false;
    }
}

(Only relevant code shown.)

How it works:
If the VisibleRange of the axis is changed through other means than new data being plotted or AutoRange being set to Always, a zoom/pan action is assumed and IsAutoRangeActive gets set to false.
If AutoRange is set to Always it causes the VisibleRange to change, which causes a VisibleRangeChanged event to trigger, activating IsAutoRangeActive and recalculating the VisibleRange to fit the desired view limit.
IsAutoRangeActive being true causes data range changes from new data to call UpdateVisibleRange, which moves the displayed range according to the view limit. As changes to the VisibleRange cause a VisibleRangeChanged event to trigger, which sets IsAutoRangeActive to false, IsAutoRangeActive needs to be reset to true after the VisibleRange has been updated in the handler.

The problem:
It all seemed to work fine until I noticed that this method does not update the actually displayed axis VisibleRange to the latest range. It uses the previous VisibleRange whenever the DataRange changes, despite VisibleRange apparently being set to the correct values. For example, if the previous range was at “min=1, max =61”, and the next is at “min=2, max=62”, the chart’s x-axis would display “min=1, max=61” instead.
It seems that “VisibleRange = visibleRange;” in UpdateVisibleRange, called from DataRangeChangedHandler, does not update the displayed range.
It works fine if the change is trigger by setting AutoRange to Always, and the x-axis’ VisibleRange corrects itself when the y-axis is modified.

(Overall this seems like quite a crude solution to me, and I wonder if there isn’t a better way of achieving this. Maybe I overlooked something?)


EDIT:
I got around the issue by using the ViewportManager as recommended and described in Tutorial 06 – Adding Realtime Updates (Scrolling Realtime Charts, see ScrollingViewportManager), though I had to make a change to make it work with my modifiers, which rely on AutoRange being set to “Always” to reset the view to scrolling mode (this way I can reset the x and y axis to extends independently).

private class ScrollingViewportManager : DefaultViewportManager
{
    public Double ViewLimit = 1.0;

    protected override IRange OnCalculateNewXRange(IAxis xAxis)
    {
        DoubleRange dataRange = null;
        if (xAxis.AutoRange == AutoRange.Never) // Zoom/Pan
        {
            dataRange = xAxis?.VisibleRange?.AsDoubleRange();
        }
        else // X-Value limited AutoRange
        {
            dataRange = xAxis?.DataRange?.AsDoubleRange();
            if (dataRange != null)
            {
                dataRange.Min = (dataRange.Max > ViewLimit) ? (dataRange.Max - ViewLimit) : 0.0;
                dataRange.Max = (dataRange.Max == dataRange.Min) ? (dataRange.Min + 1.0) : dataRange.Max;

                Double rangeWidth = dataRange.Max - dataRange.Min;
                dataRange.Min -= (rangeWidth * xAxis.GrowBy.Min);
                dataRange.Max += (rangeWidth * xAxis.GrowBy.Max);
            }
        }
        if (dataRange == null)
            dataRange = new DoubleRange(0.0, ViewLimit);
        return dataRange; 
    }
}

…wonder why I didn’t think of checking for AutoRange instead of ZoomState in the ViewportManager earlier.
While I haven’t tested it thoroughly yet, it seems to work the way I intended it to.
The reason I calculate the GrowBy myself is because it otherwise uses the full DataRange (for example if you had a window of 60 seconds, GrowBy of (0.0, 0.1) and 1000 seconds of data, it would move the data completly out of view, “showing” the timespan from 1040 to 1100).

For me the problem is solved by using a better solution to the actual problem. The question why changes to VisibleRange in the DataRangeChanged-event don’t update the chart is still open though, if someone else comes upon this problem.

Version
5.2.0.11680
  • You must to post comments
Showing 0 results
Your Answer

Please first to submit.