I have got it working but I have a new bug with dates sometimes missing on the X axis:
Here is the XAML:
<charts:SfChart Grid.Column="0" Grid.Row="1"
Background="Transparent"
AreaBorderThickness="0">
<charts:SfChart.Behaviors>
<charts:ChartZoomPanBehavior
ResetOnDoubleTap="False"
EnableMouseWheelZooming="False"
EnableSelectionZooming="False"
EnablePinchZooming="False"
EnableZoomingToolBar="False"
ZoomMode="X"
EnablePanning="True" />
</charts:SfChart.Behaviors>
<interactivity:Interaction.Behaviors>
<behaviors:SfChartLoadBehavior
LoadLeft="{Binding DisplayMoreDataCommand}" />
</interactivity:Interaction.Behaviors>
<charts:SfChart.PrimaryAxis>
<charts:DateTimeCategoryAxis
ShowGridLines="False"
LabelFormat="dd/MM/yy HH:mm"
Interval="1"
IntervalType="Minutes"
Style="{StaticResource ChartAxisXTextStyle}"
LabelRotationAngle="-30"
TickLineSize="0"
AxisLineStyle="{StaticResource ChartAxisLineStyle}"
FontSize="10"
EdgeLabelsDrawingMode="Shift" />
</charts:SfChart.PrimaryAxis>
<charts:SfChart.SecondaryAxis>
<charts:NumericalAxis
ShowGridLines="False"
Interval="1"
Style="{StaticResource ChartAxisYTextStyle}"
Minimum="{Binding MinYValue}"
Maximum="{Binding MaxYValue}"
TickLineSize="0"
AxisLineStyle="{StaticResource ChartAxisLineStyle}">
<charts:NumericalAxis.StripLines>
<charts:ChartStripLine
Opacity="0.3"
Background="{StaticResource AppGreenMediumBrush}"
Start="{Binding StripStart}" Width="{Binding StripWidth}" />
</charts:NumericalAxis.StripLines>
</charts:NumericalAxis>
</charts:SfChart.SecondaryAxis>
<charts:AreaSeries
ItemsSource="{Binding Observations}"
XBindingPath="ItemDate"
YBindingPath="Value"
Interior="{StaticResource AppBlueMediumLightBrush}" BorderThickness="0" />
<charts:LineSeries
ItemsSource="{Binding Observations}"
XBindingPath="ItemDate"
YBindingPath="Value">
<charts:LineSeries.CustomTemplate>
<DataTemplate>
<Canvas>
<Line
X1="{Binding X2}" X2="{Binding X2}" StrokeDashArray="4,2"
StrokeDashOffset="0.2" Y1="500" Y2="{Binding Y2}" Stroke="{StaticResource AppYellowLightBrush}"
StrokeThickness="1" />
</Canvas>
</DataTemplate>
</charts:LineSeries.CustomTemplate>
</charts:LineSeries>
<charts:LineSeries
ItemsSource="{Binding Observations}"
XBindingPath="ItemDate"
YBindingPath="Value"
Interior="{StaticResource AppPurpleBrush}"
StrokeThickness="1" BorderThickness="0">
<charts:LineSeries.AdornmentsInfo>
<charts:ChartAdornmentInfo VerticalAlignment="Center"
HorizontalAlignment="Center"
ShowLabel="True" SegmentLabelFormat="#"
LabelTemplate="{StaticResource AdornmentTemplate}"
SegmentLabelContent="LabelContentPath" />
</charts:LineSeries.AdornmentsInfo>
</charts:LineSeries>
</charts:SfChart>
And here is the behavior I wrote to enable dynamic loading:
public class SfChartLoadBehavior : DependencyObject, IBehavior
{
private SfChart _chart;
private bool _movedToEnd;
private double _displayCount = 12d;
private bool _dataLoaded;
public static readonly DependencyProperty LoadLeftProperty =
DependencyProperty.Register("LoadLeft", typeof(AsyncCommand), typeof(SfChartLoadBehavior), new PropertyMetadata(default(AsyncCommand)));
private int _previousMaximum;
public AsyncCommand LoadLeft
{
get { return (AsyncCommand)GetValue(LoadLeftProperty); }
set { SetValue(LoadLeftProperty, value); }
}
private async void PrimaryAxisOnActualRangeChanged(object sender, ActualRangeChangedEventArgs e)
{
ChartAxis axis = (ChartAxis)sender;
if (!_movedToEnd)
{
// moves the chart to the end and limits data shown
if (!e.IsScrolling && e.ActualMaximum != null)
{
e.VisibleMinimum = (double)e.ActualMaximum - _displayCount;
_movedToEnd = true;
Debug.WriteLine("Moving to end on load");
}
return;
}
if (_dataLoaded)
{
// moves the visible view to just before the loaded data
// has to happen in a new event otherwise the chart will override the visible area
int loadedCount = Convert.ToInt32(e.ActualMaximum) - _previousMaximum;
e.VisibleMinimum = loadedCount;
e.VisibleMaximum = loadedCount + _displayCount;
_dataLoaded = false;
Debug.WriteLine("Moving view");
return;
}
if (!e.IsScrolling)
{
return;
}
bool isStart = axis.VisibleRange.Start <= double.Epsilon;
Debug.WriteLine($"Start: {axis.VisibleRange.Start}, End: {axis.VisibleRange.End}, Max: {e.ActualMaximum}");
if (isStart)
{
AsyncCommand loadLeft = LoadLeft;
await loadLeft.ExecuteAsync();
_previousMaximum = Convert.ToInt32(e.ActualMaximum);
_dataLoaded = true;
}
}
public void Attach(DependencyObject associatedObject)
{
_chart = (SfChart)associatedObject;
_chart.Loaded += _chart_Loaded;
}
private void _chart_Loaded(object sender, RoutedEventArgs e)
{
_chart.PrimaryAxis.ActualRangeChanged += PrimaryAxisOnActualRangeChanged;
}
public void Detach()
{
if (_chart != null)
{
_chart.PrimaryAxis.ActualRangeChanged -= PrimaryAxisOnActualRangeChanged;
}
}
public DependencyObject AssociatedObject { get; }
}