I'm able to setup and bind a chart very easily using the following code:
Dim rand As New Random(DateTime.UtcNow.Millisecond) Dim col As New DataCollection col.Add(#2/1/2023#, rand.NextDouble) col.Add(#2/2/2023#, rand.NextDouble) col.Add(#2/8/2023#, rand.NextDouble) Dim sharedXAxis As New TimeXAxis With {.LabelLocation = AxisLabelsLocation.OutsideBottom, .DataSource = col, .DateTimeMemberPath = "X"} sharedXAxis.LabelFormats.Add(New TimeAxisLabelFormat With {.Format = "HH:mm"}) Me.UltraDataChart1.Axes.Add(sharedXAxis) Dim yAxis1 As New NumericYAxis With {.LabelLocation = AxisLabelsLocation.OutsideLeft, .MinimumValue = 0, .MaximumValue = 1} Me.UltraDataChart1.Axes.Add(yAxis1) Dim series1 As New LineSeries With {.DataSource = col, .ValueMemberPath = "Y", .XAxis = sharedXAxis, .YAxis = yAxis1} Me.UltraDataChart1.Series.Add(series1)
The DataCollection class was adapted from the one defined here.
The above code works as expected. But if I try to add another item to the DataCollection after the LineSeries is added to the UltraDataChart, I get an ArgumentOutOfRangeException with the following stack trace:
at System.ThrowHelper.ThrowArgumentOutOfRange_IndexException() at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection) at Infragistics.Portable.FastItemsSource.DataSourceAdd(Int32 position, IList newItems) at ChartTester.DataCollection.Add(DateTime x, Double y) in D:\Source\DOTNET\TestProjects\ChartTester\Form1.vb:line 94 at ChartTester.Form1.Form1_Load(Object sender, EventArgs e) in D:\Source\DOTNET\TestProjects\ChartTester\Form1.vb:line 55
I'm sure there's something really obvious that I'm missing. Any suggestions would be greatly appreciated.
Edit: forgot to specify that I'm using Infragistics Windows Forms 22.1 and .NET 6.
Edit 2: here's the code for the DataCollection and DataPoint classes.
Public Class DataCollection Implements INotifyCollectionChanged Implements IEnumerable Protected Data As New List(Of DataPoint) Public Event CollectionChanged As NotifyCollectionChangedEventHandler Implements INotifyCollectionChanged.CollectionChanged Protected Sub OnCollectionChanged(e As NotifyCollectionChangedEventArgs) RaiseEvent CollectionChanged(Me, e) End Sub Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return Me.Data.GetEnumerator End Function Public Sub Add(x As DateTime, y As Double) Dim dataPoint As New DataPoint(x, y) Me.Data.Add(dataPoint) Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, dataPoint)) End Sub End Class Public Class DataPoint Public ReadOnly Property X As DateTime Public ReadOnly Property Y As Double Public Sub New(x As DateTime, y As Double) Me.X = x Me.Y = y End Sub End Class
That does seem to work but it's very counterintuitive. The index of any new point added to the collection would always equal the count of items in the collection minus 1. Interestingly, I confirmed that this works too and it makes more sense to me:
Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, dataPoint, Me.Data.Count - 1))
In any case, your answer is what I needed. Thanks very much Michael. You might want to update the Chart Performance help article since it describes updating the DataSource of an already-bound series, and the code example given doesn't work without explicitly specifying an index.
Use the third parameter of NotifyCollectionChangedEventArgs and add an index of 0. This ensures the added items don't collide with existing ones.
eg.
Public Sub Add(dataPoint As DataPoint) Me.Data.Add(dataPoint) Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, dataPoint, 0)) End Sub
Hi Michael, thanks for the reply. See attached.
ChartTester.zip
Hello,
Thank you for contacting Infragistics. Please attach a complete sample project isolating the behavior. We'll need to investigate your DataCollection and how it behaves with the control.