I have created a XamDataChart that is bound to a real-time data source. I need the x-axis (Date) to update in real-time. Currently the x-axis labels are only updated when the chart is re-sized horizontally. Is there any way to have the x-axis labels update in real-time?
Kevin, does the collection that the XAxis is bound to support INotifyCollectionChanged? Can you post any kind of sample of how you have this configured?
The collection is bound to an ObservableCollection.
Kevin,How does what you are doing differ from the below. This appears to work for me. Or am I not correctly modelling what you are trying to accomplish?
<ig:XamDataChart Name="xamDataChart1" > <ig:XamDataChart.Axes> <ig:CategoryXAxis x:Name="XAxis" VerticalAlignment="Bottom" ItemsSource="{Binding}" Label="{}{TimeStamp:mm:ss}"> <ig:CategoryXAxis.LabelSettings> <ig:AxisLabelSettings Location="OutsideBottom" Extent="25"/> </ig:CategoryXAxis.LabelSettings> </ig:CategoryXAxis> <ig:NumericYAxis x:Name="YAxis" /> </ig:XamDataChart.Axes> <ig:XamDataChart.Series> <ig:LineSeries XAxis="{Binding ElementName=XAxis}" YAxis="{Binding ElementName=YAxis}" ItemsSource="{Binding}" ValueMemberPath="Value" /> </ig:XamDataChart.Series> </ig:XamDataChart>
The code behind:
public partial class MainPage : UserControl { private Random _rand = new Random(); private DispatcherTimer _timer = new DispatcherTimer(); private double _curr = 0.0; private TestData _td = new TestData(); public MainPage() { InitializeComponent(); DataContext = _td; _timer.Interval = new TimeSpan(0,0,0,0,250); _timer.Tick += Timer_Tick; _timer.Start(); } void Timer_Tick(object sender, EventArgs e) { if (_rand.NextDouble() > .5) { _curr += _rand.NextDouble(); } else { _curr -= _rand.NextDouble(); } _td.Add( new TestDataItem() { TimeStamp = DateTime.Now, Value = _curr }); if (_td.Count > 15) { _td.RemoveAt(0); } } } public class TestData : ObservableCollection<TestDataItem> { } public class TestDataItem { public DateTime TimeStamp { get; set; } public double Value { get; set; } }
-Graham
Graham, your example works where the collection is bound to an observable collection. In my case I am trying to prevent the real-time chart from being updated any time a new point is added (30 Hz). In this case I am using a collection that implements INotifyCollectionChanged. I am performing a bulk insert/delete from my real-time collection and then signaling NotifyCollectionChangedAction.Reset. This causes the problem I am seeing with the X-Axis refresh.
How can I send you my sample code?
The Code Behind:
public partial class MainPage : UserControl { private Random _rand = new Random(); private DispatcherTimer _timer = new DispatcherTimer(); private double _curr = 0.0; //private TestData _td = new TestData(); private RealTimeDataBuffer _td = new RealTimeDataBuffer() { HistoryTimeSpan = new TimeSpan(0, 1, 0) }; public MainPage() { InitializeComponent(); DataContext = _td; _timer.Interval = new TimeSpan(0, 0, 0, 0, 100); _timer.Tick += Timer_Tick; _timer.Start(); } TimeSpan max = new TimeSpan(0, 1, 0); List<TestDataItem> _bufferedData = new List<TestDataItem>(); void Timer_Tick(object sender, EventArgs e) { if (_rand.NextDouble() > .5) { _curr += _rand.NextDouble(); } else { _curr -= _rand.NextDouble(); } _bufferedData.Add( new TestDataItem() { TimeStamp = DateTime.Now, Value = _curr }); // Add the data to the chart in groups of 10 if (_bufferedData.Count >= 10) { _td.Add(_bufferedData); _bufferedData.Clear(); } } } //public class TestData // : ObservableCollection<TestDataItem> //{ //} public class TestDataItem { public DateTime TimeStamp { get; set; } public double Value { get; set; } } public class RealTimeDataBuffer : DependencyObject, INotifyCollectionChanged, IEnumerable { public event NotifyCollectionChangedEventHandler CollectionChanged; private List<TestDataItem> historicalDataSource = new List<TestDataItem>(); private TimeSpan dataPointInterval; public TimeSpan HistoryTimeSpan { get { return dataPointInterval; } set { dataPointInterval = value; } } public IEnumerator GetEnumerator() { return historicalDataSource.GetEnumerator(); } /// <summary> /// Add a specified number of records from /// historical dataset to dataset /// </summary> /// <param name="recordCount"></param> public void Add(IEnumerable<TestDataItem> items) { foreach (var item in items) historicalDataSource.Add(item); // Remove any old data DateTime minTime = historicalDataSource.Last().TimeStamp - dataPointInterval; while (historicalDataSource.First().TimeStamp < minTime) { // Remove this item from the total historicalDataSource.RemoveAt(0); } if (CollectionChanged != null) { CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } }
Kevin,
Good catch! Looks like the category axes are currently blissfully ignoring the reset and replace actions, while the series respect them. I'll get back to you with a bug number. In the meantime, something like this would make sure the axes take notice.
if (CollectionChanged != null) { CollectionChanged(this, new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset)); int lastIndex = historicalDataSource.Count - 1; CollectionChanged(this, new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, historicalDataSource[lastIndex - 1], lastIndex)); CollectionChanged(this, new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Add, historicalDataSource[lastIndex - 1], lastIndex)); }
I would actually only bother throttling the collection like this if you realize a good performance improvement from it. Depending on the volume of your updates, you may find it not making much difference.-Graham
Was the fix for this bug ever released? If so, which version is it in?