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
}