Hi,
I am using ScatterChart with IBindingList as DataSource, and have set ColumnX and ColumnY. And It worked.
And the new data was automatically plotted.
However, as there is no way to fix the index of fields in IBindingList to be 1 and 2 all the time, ScatterChart no longer worked.
Is there a way to fix this issue from ScatterChart's option?
If not, if I use DataTable as a data source, would the new data added to the DataTable plotted automatically, or do I have to manually DataBind each time DataTable's content changes?
GetRawData() returns an instance of the IListToChartAdapter2 attached to your list.
GetStringValue(row, column) returns this
return this.internalNTV[row][column].Value.ToString();
where "internalNTV" is an array of objects containing Name, Type, and Value information on your objects. this is all retrieved using TypeDescriptor.GetProperties and PropertyDescriptor.GetValue.
maybe if any of your properties have a value of null then some problem is occurring. but if this is only occurring on some machines, you might want to try installing the latest .NET Framework service pack.
if you have a sample project or steps to reproduce, i could also help look into this.
Thanks for your response and detailed sample.
Everything works fine, but on some users, I am getting the following exception (at the end of this post.)
I am suspecting that sometimes the custom "GetProperties" is not getting called, or chart is doing something else.
Is it possible that Infragistics Chart tries to Serialize/Deserialize the items, and the bindingList's GetProperties is not getting fired?
Moreover, I looked at the CreateSeriesHashtable method, and wondered what is really happening at "data.GetRawData()." Can you please give me more details about what is data in this case, and what "GetRawData()" does in my case when Chart's Data is bind to a BindingSource whose data source is a bindingList.
Exception
='System.NullReferenceException: Object reference not set to an instance of an object. at Infragistics.UltraChart.Data.IListToChartAdapter2.GetStringValue(Int32 row, Int32 column) at Infragistics.UltraChart.Core.Layers.ScatterLayer.CreateSeriesHashtable(ScatterChartAppearance scatterApp) at Infragistics.UltraChart.Core.Layers.ScatterLayer.FillSceneGraph(SceneGraph scene) at Infragistics.UltraChart.Core.ChartCore.DrawChart() at Infragistics.Win.UltraWinChart.UltraChart.OnPaint(PaintEventArgs e) at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs) at System.Windows.Forms.Control.WmPaint(Message& m) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ScrollableControl.WndProc(Message& m) at System.Windows.Forms.ContainerControl.WndProc(Message& m) at System.Windows.Forms.UserControl.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)' (Loader.Unhandled Thread Exception)
here's my sample which should solve the problem with ICustomTypeDescriptor and a BindingList.
to answer your other question, the chart's API is the way it is because it was designed with only DataTables in mind, then expanded to support other datasources as well. not ideal in this particular situation, but the workaround has always done the job for us.
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { this.ultraChart1.ChartType = ChartType.ScatterChart; this.ultraChart1.ScatterChart.ColumnX = 0; this.ultraChart1.ScatterChart.ColumnY = 1; this.ultraChart1.Data.DataSource = new BindingList<Widget>(new Widget[] { new Widget(1, 3), new Widget(2, 2), new Widget(3, 1), }); } public class Widget : ICustomTypeDescriptor { public Widget(double x, double y) { this.TheXValue = x; this.TheYValue = y; } public double TheXValue { get; set; } public double TheYValue { get; set; } public double IrrelevantProperty { get; set; } #region ICustomTypeDescriptor Members AttributeCollection ICustomTypeDescriptor.GetAttributes() { return null; } string ICustomTypeDescriptor.GetClassName() { return null; } string ICustomTypeDescriptor.GetComponentName() { return null; } TypeConverter ICustomTypeDescriptor.GetConverter() { return null; } EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return null; } PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return null; } object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return null; } EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { return null; } EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { return null; } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { return null; } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { PropertyDescriptorCollection originalProps = TypeDescriptor.GetProperties(this, true); // passing true as the 2nd argument here will ignore the ICustomTypeDescriptor implementation return new PropertyDescriptorCollection(new PropertyDescriptor[] { originalProps["TheXValue"], originalProps["TheYValue"] }); } object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { return this; } #endregion } }
Can you give me any example about getting the proper index for Column X and Y using BINDINGLIST? I already using GetProperties to know the order, but this order varies.
And I have a requirement that I must use BindingList, and not DataTable. The chart's API should not dictate the choice of Data Structure for Data Source.
Why didn't chart's API exposed ColumnX and ColumnY to be set with Name, rather than the index that can vary during run time?
Look at this post:
if you take the class of the objects inside your IBindingList and have them implement ICustomTypeDescriptor, you can determine the index order of properties in the ICustomTypeDescriptor.GetProperties method.
i think the chart will listen to the DataTable's updating events and redraw. if that doesn't work for some reason, then you could try using the DataTable's DefaultView property, which is a DataView, and DataView implements IBindingList.