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

Answered
0
0

I’m trying to set the visible range property of a CategoryDateTimeAxis through MVVM. I’m following the general instructions detailed here for converting between pixel & data coordinates on the axis:

https://www.scichart.com/questions/question/categorydatetimeaxis-in-mvvm#sabai-inline-nav

I have the following code in my viewmodel:

XAxis.OnBeginRenderPass();
var calc = XAxis.GetCurrentCoordinateCalculator();
var coordCalc = calc as ICategoryCoordinateCalculator;

XAxis is a CategoryDateTimeAxis injected from the view. I call OnBeginRenderpass as I saw in another forum post that this will ensure that the CoordinateCalculator is initialized.

calc shows in the debugger as:

  • calc {A.} Abt.Controls.SciChart.Numerics.CoordinateCalculators.ICoordinateCalculator {A.}

The coordCalc variable ends up assigned to NULL, as the ICoordinateCalculator< double > cannot be cast to the interface.

How do I accomplish the above?

Thanks, Asher

  • You must to post comments
Best Answer
0
0

Hi Asher,

I’ve created a little test app below to try to explain how the GetCurrentCoordinateCalculator() works during the startup phase of the chart.

Basically, XAxis.GetCurrentCoordinateCalculator() returns the coordinate calculator that is valid for the axis right now. This depends on the variables the axis has right now.

For numeric axes you can force it to refresh its variables by calling AxisBase.OnBeginRenderPass() (in fact, strictly speaking in v3.5 and up this refresh now occurs automatically). This unfortunately doesn’t work for CategoryDateTimeAxis as it requires actual data passed in from the DataSeries during the draw phase. In other words, CategoryDateTimeAxis.GetCurrentCoordinateCalculator() is not valid until the chart is drawn with your data.

Take a look at the attached sample. In MainWindow.xaml.cs there is a really nasty workaround which can force the CategoryCoordinateCalculator to be valid before drawing. Alternatively (and we would recommend), wait until the chart draws before getting your updated coord-calc.

Let me know if this helps,

Best regards,
Andrew

Images
  • ashernew
    Andrew – this is great. Thank you so much for getting back to me so quickly and in so much detail. I’m looking forward to fixing this and working with your library – which is really quite nice. Unfortunately, I’m getting a 404 when I try to download the attachment. (https://www.scichart.com/questions/answers/8946/file/7961/WpfApplication54.zip).
  • Andrew
    That’s because our forum software has a really dumb bug … I will email you the attachment. Apologies.
  • ashernew
    No problem – thank you again!
  • You must to post comments
0
0

I believe that the axis should properly have data from which to calculate coordinates. Here’s what I’ve done:

From the view:

<s:SciChartSurface Grid.Row="1"  s:ThemeManager.Theme="ExpressionLight" x:Name="ChartSurface">


        <s:SciChartSurface.Resources>
            <styles:IntradayChartXAxisLabelProvider x:Key="XAxisLabelProvider" />
        </s:SciChartSurface.Resources>
        <s:SciChartSurface.RenderableSeries>
            <s:FastLineRenderableSeries DataSeries="{Binding Series1}" SeriesColor="#279B27" ResamplingMode="Auto"/>
            <s:FastLineRenderableSeries DataSeries="{Binding Series2}" SeriesColor="#A021A8" ResamplingMode="Auto"/>
            <s:FastLineRenderableSeries DataSeries="{Binding Series3}" SeriesColor="#2125A8" ResamplingMode="Auto"/>
            <s:FastLineRenderableSeries DataSeries="{Binding Series4}" SeriesColor="#E8ED24" ResamplingMode="Auto"/>
            <s:FastLineRenderableSeries DataSeries="{Binding Series5}" SeriesColor="#21E9E9" ResamplingMode="Auto"/>
            <s:FastLineRenderableSeries DataSeries="{Binding Series6}" SeriesColor="#EF2725" ResamplingMode="Auto"/>
        </s:SciChartSurface.RenderableSeries>

        <s:SciChartSurface.XAxis>

            <s:CategoryDateTimeAxis LabelProvider="{StaticResource XAxisLabelProvider}"  >
                <Style TargetType="s:CategoryDateTimeAxis">
                    <Setter Property="DrawMajorBands" Value="True"/>
                    <Setter Property="DrawMinorGridLines" Value="False"/>
                    <Setter Property="VisibleRange" Value="{Binding XVisibleRange, Mode=TwoWay}"/>
                    <Setter Property="GrowBy" Value="0, 0.05"/>
                </Style>    
            </s:CategoryDateTimeAxis>
        </s:SciChartSurface.XAxis>

        <s:SciChartSurface.YAxis>
            <s:NumericAxis AutoRange="Always" GrowBy=".1, .1"/>
        </s:SciChartSurface.YAxis>

        <s:SciChartSurface.ChartModifier>
            <s:ModifierGroup s:MouseManager.MouseEventGroup="MySharedMouseGroup">
                <s:LegendModifier x:Name="LegendModifier" ShowLegend="True" Orientation="Vertical" Margin="10" LegendPlacement="Inside" GetLegendDataFor="AllSeries" ShowVisibilityCheckboxes="True"/>

                <s:CursorModifier IsEnabled="True"
                                      ShowAxisLabels="True"
                                      ShowTooltip="False" />

                <s:RubberBandXyZoomModifier IsEnabled="True" IsXAxisOnly="True" ReceiveHandledEvents="True"/>

                <s:MouseWheelZoomModifier IsEnabled="True"/>
                <s:ZoomExtentsModifier ExecuteOn="MouseDoubleClick" IsEnabled="True"/>
            </s:ModifierGroup>
        </s:SciChartSurface.ChartModifier>
    </s:SciChartSurface>

And then I dynamically assign the Series1..6 variable in the VM to which each chart series is bound:

Series1 = CompileDataSeries(..., ...);
Series2 = CompileDataSeries(..., ...);

After assigning data to the bound variables, I call in the viewmodel:

XAxis.OnBeginRenderPass();
var calc = XAxis.GetCurrentCoordinateCalculator();
var coordCalc = calc as ICategoryCoordinateCalculator;

Am I not doing things in the correct sequence?

The purpose of the above is to manually set the visible range:

var minIx = coordCalc.TransformDataToIndex(ConvertIntDatetime(minDateInt, minTimeInt));
var maxIx = coordCalc.TransformDataToIndex(ConvertIntDatetime(maxDateInt, maxTimeInt));

 XVisibleRange = new IndexRange(minIx, maxIx);

Thanks,

Asher

  • Andrew
    Maybe you should try removing XAxis.OnBeginRenderPass()? Just looking at the code it expects the data to be passed (this is done internally – the CategoryDateTimeAxis is a special case). If it has the data already it should work, if not, it will return a DoubleCoordinateCalculator.
  • ashernew
    That XAxis.OnBeginRenderPass() line was added as a hail-mary in trying to solve this problem, as I saw it suggested in another post on a similar topic. I’ve just removed it and tested, and I get the same behavior.
  • ashernew
    I don’t have source access, so I’m curious what you mean by: looking at the code it expects the data to be passed (this is done internally – the CategoryDateTimeAxis is a special case). Is there a method that I can call on the chart or axis to force this process to occur?
  • You must to post comments
0
0

As an additional detail, here’s how I am simply injecting the Axis into my viewmodel from the code-behind in the view’s constructor, AFTER InitializeComponents() is called:

//inject the xaxis into the vm for some magic
var xaxis = this.ChartSurface.XAxis;
viewModel.XAxis = xaxis as CategoryDateTimeAxis;

Is the problem the timing of when I grab the XAxis? Will that property get reassigned at some point, and I am using an invalid reference?

My injection methodology above is only used because it’s simple – happy to consider alternatives.

Thanks,

Asher

  • You must to post comments
0
0

Andrew,

I took a look at your solution. Thank you very much for putting that together – it’s most helpful.

I did a bit of testing, and it appears that a new CoordinateCalculator is created on each new screen render when dealing with a CategoryDateTimeAxis (haven’t tested other types).

I’ve injected the ChartSurface into my VM in order to hook the Rendered event, and I’m catching new instances of the CoordinateCalculator as they appear:

private ICategoryCoordinateCalculator _calc = null;
private void OnChartRendered(object sender, EventArgs e)
{
    try
    {
        _calc = XAxis.GetCurrentCoordinateCalculator() as ICategoryCoordinateCalculator;
    }
    catch (Exception ex)
    {
        _logger.Error("Error getting CoordinateCalculator", ex);
    }
}

It’s not the cleanest solution, but it appears to work. If what I’m doing is a Bad Idea for some reason to do with the chart, please let me know.

Otherwise, thank you again for the assistance!

Best,

Asher

  • You must to post comments
Showing 4 results
Your Answer

Please first to submit.