Hello. I need a real-time RadCartesianChart with Spline Series which could contain a large number of points and could be scrolled (or panned) in time. Suppose the program works and generates a chart for an hour. The chart moves from right to left. Every second the program polls the external device (flowmeter) and receives data to form a new point on the chart. In the chart window only last 30 points are seen. The others are hidden behind the left border of the chart window. To see the hidden part of the chart the user has to do one of the two floowing options:
- Scroll the chart. I.e., the user moves the mouse cursor to the scroll bar thumb and sets the mouse cursor over it, presses the right mouse button and while holding it, tows scroll bar thumb from right to left until the chart will be scrolled until the required distance.
Or
-Pan the chart. I.e., the user sets the mouse cursor over the chart, presses the right mouse button and while holding it, drags the chart from right to left until the chart will be moved until the required distance.
Below, I present to you View and ViewModel of how the chart is now done. Please see the View:
<telerik:RadCartesianChart Grid.Row="0"> <!--Turn off scrollbars on the chart--> <telerik:RadCartesianChart.Resources> <Style TargetType="telerik:PanZoomBar"> <Setter Property="Visibility" Value="Collapsed"/> </Style> </telerik:RadCartesianChart.Resources> <telerik:SplineSeries CategoryBinding="Category" ValueBinding="Value" ItemsSource="{Binding Data}"/> <telerik:RadCartesianChart.HorizontalAxis> <telerik:DateTimeContinuousAxis MajorStepUnit="Second" LabelInterval="5" LabelFormat="hh:mm:ss" FontFamily="Segoe UI" PlotMode="OnTicks" TickOrigin="{Binding AlignmentDate}"/> </telerik:RadCartesianChart.HorizontalAxis> <telerik:RadCartesianChart.VerticalAxis> <telerik:LinearAxis FontFamily="Segoe UI" Title="Meters per second [m/s]"/> </telerik:RadCartesianChart.VerticalAxis> <!--Coordinate grid--> <telerik:RadCartesianChart.Grid> <telerik:CartesianChartGrid MajorLinesVisibility="XY" MajorXLineDashArray="3,4" MajorYLineDashArray="3,4"/> </telerik:RadCartesianChart.Grid> <!--Changing of scale and panning of the chart----> <telerik:RadCartesianChart.Behaviors> <telerik:ChartPanAndZoomBehavior DragMode="Pan" ZoomMode="Both" PanMode="Both"/> </telerik:RadCartesianChart.Behaviors></telerik:RadCartesianChart>Please see the ViewModel
public class t_SoundVelocityViewModel : BindableBase{ #region Fields #region Constant Fields /// <summary> /// Outer device polling timer period. /// </summary> private const int TIMER_INTERVAL = 1000; . . . . . . . . . . . . . . . . . . . . . . . . . #endregion #region Common Fields . . . . . . . . . . . . . . . . . . . . . . . . . /// <summary> /// Chart points collection. /// </summary> private RadObservableCollection<ChartPoint> _data; /// <summary> /// Outer device polling timer. /// </summary> private DispatcherTimer _timer; /// <summary> /// /// </summary> private DateTime _lastDate; /// <summary> /// /// </summary> private DateTime? _alignmentDate; /// <summary> /// /// </summary> private bool _useAlignmentDate; /// <summary> /// Текущее значение скорости звука. /// </summary> private double _soundVelocityValue = 0; #endregion #endregion #region Constructors /// <summary> /// Конструктор. /// </summary> public t_SoundVelocityViewModel(IEventAggregator eventAggregator) { . . . . . . . . . . . . . . . . . . . . . . . . this.FillData(); this._useAlignmentDate = true; // Create timer. this._timer = new DispatcherTimer(); this._timer.Interval = TimeSpan.FromMilliseconds(TIMER_INTERVAL); this._timer.Tick += this.onTimer; // Запустить таймер опроса. this._timer.Start(); } #endregion #region Properties /// <summary> /// Gets or sets chart points collection. /// </summary> public RadObservableCollection<ChartPoint> Data { get { return this._data; } set { this.SetProperty(ref this._data, value); } } public DateTime? AlignmentDate { get { return this._alignmentDate; } set { this.SetProperty(ref this._alignmentDate, value); } } public bool UseAlignmentDate { get { return this._useAlignmentDate; } set { if (this.SetProperty(ref this._useAlignmentDate, value)) this.updateAlignmentDate(); } } . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . #endregion #region Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . /// <summary> /// Polling timer tick's handler. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void onTimer(object sender, EventArgs e) { try { this._lastDate = this._lastDate.AddMilliseconds(TIMER_INTERVAL); // Turn off collection changed notifications when very old chart point is removed // from chart points collection and a new chart point add there. this.Data.SuspendNotifications(); this.Data.RemoveAt(0); this.Data.Add(this.CreateBusinessObject()); // Turn on collection changed notifications after new chart point was added there. this.Data.ResumeNotifications(); . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . } catch (Exception ex) { if (ex.InnerException != null) raiseNotification(ex.InnerException.Message, "Ошибка"); else raiseNotification(ex.Message, "Ошибка"); } } /// <summary> /// Creates chart point. /// </summary> /// <returns></returns> private ChartPoint CreateBusinessObject() { this._soundVelocityValue = GlobalStaticMembers.SoundVelocityBuffer; ChartPoint point = new ChartPoint(); point.Value = this._soundVelocityValue; point.Category = this._lastDate; return point; } /// <summary> /// /// </summary> private void FillData() { RadObservableCollection<ChartPoint> collection = new RadObservableCollection<ChartPoint>(); this._lastDate = DateTime.Now; this._alignmentDate = this._lastDate; for (int i = 0; i < 31; i++) { this._lastDate = DateTime.Now; collection.Add(this.CreateBusinessObject()); } this.Data = collection; } /// <summary> /// Starts polling timer. /// </summary> public void startTimer(bool param) { if (!this._timer.IsEnabled) this._timer.Start(); } /// <summary> /// Stops polling timer. /// </summary> public void stopTimer(bool param) { this._timer.Stop(); } /// <summary> /// Updates polling timer interval. /// </summary> /// <param name="interval"></param> public void UpdateTimer(double interval) { this._timer.Interval = TimeSpan.FromMilliseconds(interval); } /// <summary> /// /// </summary> private void updateAlignmentDate() { this.AlignmentDate = this._useAlignmentDate ? (DateTime?)this._lastDate : null; } . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . #endregion}