Zoom single Y axis on a multiple axis chart triggered by source change

Hi, I have a chart consisting of multiple areas with their own Y axis.  Each area contains multiple series which have ListenPropertyChange=True so that they update when the source changes. The series are fixed in length at around 100,000 datapoints. The last 500 or so datapoints are empty, but will have values attached as time goes by. When these empty points get values, the relevant y axis needs to increase the scale in case the new values are higher than the previous ones so that they don't get cut off.

Run the attached sample, click the "change price" button a few times until the series extends beyond the top of the chart. The y axis zooms out so that the series fits in the area. Do the same for "change volume 1" and "change volume 2". The behavior is the same. This is the correct behavior. Now, click the zoom 1 day button and scroll the chart. The "price" y axis y changes the visible range continually so that the series fits the area vertically, whereas the axis for "volume 1" and "volume 2" series do not change. This is correct.

The sample application functions exactly as it should. The problem is, CalculateVisibleRange is triggered for all 3 axes regardless which series got changed. You can check this by looking at the Immediate Window in Visual Studio. There is no way to tell which series got changed, or in which axis the change happened. This means that the GetExtremes function which does a lot of calculation, is needlessly performed for two series. This is also the case when scrolling the chart. And when there are many more series and areas, this degrades the performance of the chart.

Is there a way to change this code so that only the relevant y axis is zoomed with the minimum amount of calculation performed?

Thank you.

Attachment: sfChartListenZoomMultipleTest_cd903849.rar

9 Replies

MK Muneesh Kumar G Syncfusion Team June 11, 2018 11:12 AM UTC

Hi Tom, 
 
Thank you for using Syncfusion products. 
 
Please find the response below.  
 
Query 1:  
 
We have checked with dynamic data update to empty points via button click. It works fine. We have attached two buttons (Empty Data, High Data) in sample. When last data points are set empty and set again with high values the axis range Is calculated according to the highest range. Please check the sample attached in below location.  
 
Query 2:  
 
The performance while changing the data points (CalculateVisibleRange is called for all the axes) is fixed by comparing the current series. For this we have extended SfChart for holding the current series property. 
 
Code snippet.  
 
ChartExtension 
  
Class SfChartExt 
    Inherits SfChart 
  
    Public Property CurrentSeries As ChartSeriesBase 
  
End Class 
  
  
Updating Current Series 
  
Class MainWindow 
  
    Private Sub btChangePrice_Click(sender As Object, e As RoutedEventArgs) Handles btChangePrice.Click 
        chart.CurrentSeries = SeriesPrice 
        BaseTicker.Minutes(100).Price *= 1.2 'increase price for this minute by 20% 
    End Sub 
  
    Private Sub btChangeVolume1_Click(sender As Object, e As RoutedEventArgs) Handles btChangeVolume1.Click 
        chart.CurrentSeries = SeriesVolume1 
        BaseTicker.Minutes(100).Volume1 *= 1.2 'increase volume1 for this minute by 20% 
    End Sub 
  
    Private Sub btChangeVolume2_Click(sender As Object, e As RoutedEventArgs) Handles btChangeVolume2.Click 
        chart.CurrentSeries = SeriesVolume2 
        BaseTicker.Minutes(100).Volume2 *= 1.2 'increase volume2 for this minute by 20% 
    End Sub 
  
End Class 
  
  
Comparing Series 
  
    Protected Overrides Sub CalculateVisibleRange(avalableSize As Size) 
        MyBase.CalculateVisibleRange(avalableSize) 
 
        'Comparing the registered series. 
        Dim registeredSeries = CType(Me.GetType().GetProperty("RegisteredSeries", BindingFlags.GetProperty Or BindingFlags.NonPublic Or BindingFlags.Instance).GetValue(Me), ObservableCollection(Of ISupportAxes)) 
  
        Dim isRegisteredSeriesModified As Boolean 
  
        For Each item As ChartSeriesBase In registeredSeries 
            If (area.CurrentSeries.Equals(item)) Then 
                isRegisteredSeriesModified = True 
            End If 
            Exit For 
        Next 
  
        If Not isRegisteredSeriesModified Then Return 
  
        'If primaryAxisVisibleRange = previousXVisibleRange Then Return 
  
        ZoomY(primaryAxisVisibleRange, area) 
  
 
  
    End Sub 
  
 
 
Query 3:  
 
The range have calculated according to the minimum and maximum values of data points while scrolling. Thus, it has to be calculated for all the y axes while scrolling. 
 
Please let us know if you have any queries.  
 
Thanks, 
Muneesh Kumar G. 



TO Tom June 12, 2018 03:38 AM UTC

Thank you, but is there really no way to figure out from within the CalculateVisibleRange method (or any other method of sfChart) which series was changed, or which axis needs to be updated?


TO Tom June 12, 2018 03:41 AM UTC

The reason is, the real application will not have the source modified via buttons, but in a parser thread which parses a stream of strings and modifies the data source as time goes by. 


TO Tom June 12, 2018 03:44 AM UTC

Having to set which series needs to be updated from within the parser thread becomes very impractical and is not consistent with wpf.

Sorry for splitting the post into multiple sentences, but the original post got rejected for a word (l_ive) that was not allowed, clearly a bug with the word filter.


MK Muneesh Kumar G Syncfusion Team June 12, 2018 10:49 PM UTC

Hi Tom,  
  
Instead of using CurrentSeries we have achieved your requirement by setting YBindingPath index to CurrentSeriesIndex property as per the below code snippet.  
 
Code snippet [MainWindow] 
  Private Sub btChangePrice_Click(sender As Object, e As RoutedEventArgs) Handles btChangePrice.Click 
        chart.CurrentSeriesIndex = 0 
        BaseTicker.Minutes(100).Price *= 1.2 'increase price for this minute by 20% 
    End Sub 
 
    Private Sub btChangeVolume1_Click(sender As Object, e As RoutedEventArgs) Handles btChangeVolume1.Click 
        chart.CurrentSeriesIndex = 1 
        BaseTicker.Minutes(100).Volume1 *= 1.2 'increase volume1 for this minute by 20% 
    End Sub 
 
    Private Sub btChangeVolume2_Click(sender As Object, e As RoutedEventArgs) Handles btChangeVolume2.Click 
        chart.CurrentSeriesIndex = 2 
        BaseTicker.Minutes(100).Volume2 *= 1.2 'increase volume2 for this minute by 20% 
    End Sub 
 
.. 
 
Class SfChartExt 
    Inherits SfChart 
 
    Public Property CurrentSeriesIndex As Integer 
 
End Class 
 
 
Code snippet [Model] 
 
    'index as 0 
    Private _Price As Double 
    Public Property Price As Double 
.. 
 
    'index as 1 
    Private _Volume1 As Integer 
    Public Property Volume1 As Integer 
.. 
 
    'index as 2 
    Private _Volume2 As Integer 
    Public Property Volume2 As Integer 
        Get 
            Return _Volume2 
 
 
We have modified our sample based on this, please find the sample from the following location.  
 
 
Please let us know if you have any queries.  
 
Thanks, 
Muneesh Kumar G. 



TO Tom June 13, 2018 05:05 PM UTC

Thank you, however this solution requires that a property on the chart needs to be set wherever the source changes. As explained in my previous post, this is not practical. The function where the source changes should not have any knowledge of the chart. What complicates things is that every "Baseticker" will have its own chart. Your solution requires that at every point where the source changes I need to have extra code that gets the chart the baseticker is attached to. All this makes it very cumbersome to maintain. I suggest you add functionality to sfChart so that it knows which axis to modify, to improve performance.


MK Muneesh Kumar G Syncfusion Team June 14, 2018 11:42 AM UTC

Hi Tom,

We have achieved your requirement by extending the series itself, so no need any index or series value while updating value.  
 
We have updated the CurrentSeries property of the SfChartExt when the data model is modified for the series data in series extension. 
 
Code Snippet 
 
Updating the range 
 
 
    Protected Overrides Sub CalculateVisibleRange(avalableSize As Size) 
        MyBase.CalculateVisibleRange(avalableSize) 
 
                … 
 
        Dim currentSeries = CType(area.CurrentSeries, CartesianSeries) 
 
        If area.CurrentSeries IsNot Nothing Then 
 
            Dim isRegisteredSeriesModified As Boolean = False 
 
            If (currentSeries.YAxis.Equals(Me)) Then 
                isRegisteredSeriesModified = True 
            End If 
 
            If Not isRegisteredSeriesModified Then Return 
 
        End If 
 
    … 
 
    End Sub 
 
 
Updating current series 
 
 
Class SfChartExt 
    Inherits SfChart 
 
    Public Property CurrentSeries As ChartSeriesBase 
 
    Friend Sub RefreshCurrentSeries() 
        Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, Sub() NullCurrentSeries()) 
    End Sub 
 
    Private Sub NullCurrentSeries() 
        CurrentSeries = Nothing 
    End Sub 
 
End Class 
 
Class FastSteplineBitmapSeriesExt 
    Inherits FastStepLineBitmapSeries 
 
    Protected Overrides Sub SetIndividualPoint(index As Integer, obj As Object, replace As Boolean) 
        MyBase.SetIndividualPoint(index, obj, replace) 
 
        Dim sfChartExt = CType(Me.Area, SfChartExt) 
 
        sfChartExt.CurrentSeries = Me 
    End Sub 
 
End Class 
 
 
 
We have modified our sample based on this, please find the sample from the following location.  
 
 
Please let us know if you have any queries.  
 
Thanks, 
Muneesh Kumar G. 



TO Tom June 15, 2018 02:32 PM UTC

Thank you, this has been a great help for putting me on the right path to solving the problem. There were some additional issues that cropped up. For instance, when setting the visualrange for an individual axis, the other two were automatically reset, I so I had to set them to the previous visualrange whenever that happens. Also, when multiple series were modified at once, only the last modified series became the Currentseries, so I replaced the Currentseries property with a IsUpdateNeeded property for each axis. Anyway, it all works fine now, and the issue has been resolved.


SR Samuel Rajadurai Edwin Rajamanickam Syncfusion Team June 18, 2018 05:49 AM UTC

Hi Tom, 
  
Thanks for your response
  
We are glad that the provided solution has resolved the issue and please get back to us if you need any other assistance
  
Regards,
Samuel 


Loader.
Up arrow icon