Greetings.
I am having a problem with the UltraChart control that is beyond my abilities to resolve. I am creating this post in hope that someone may set me on the right path.
I have built a custom windows control that employs a number of sub-controls in its design – including one each of an Infragistics UltraGrid and UltraChart control. The image below shows my “Scheduler” control beneath an UltraListView control in Details view.
The scheduler control pretty much consists of an UltraGrid control on the left and an UltraChart control on the right. Other controls are used for showing and controlling visible date and time range selection. As can be seen the UltraChart is being shown as a Gantt chart, and I am making use of the UltraGridBand.SortColumnsCollection to achieve a tree-view style of display in the UltraGrid.
The scheduler control displays a hierarchy of objects with time entries. In the example above, as a different Document is selected in the Document List the hierarchy of its Phases and Performers (and their time entries) are displayed in the scheduler control below. A lot of complex coding has been written to ensure that the two controls are kept in synchronization. For example if an UltraGroupByRow is collapsed in the UltraGrid then the UltraChart GanttSeries related to that row is hidden.
The problem that I am having is that occasionally, after having selected different documents, an error is thrown when the mouse cursor is moved over the UltraChart control. The error is an ArgumentOutOfRangeException with the message “Index was out of range. Must be non-negative and less than the size of the collection”. The stack trace when the error is thrown is given below:
at System.Collections.ArrayList.get_Item(Int32 index) at System.Collections.Specialized.StringCollection.get_Item(Int32 index) at Infragistics.UltraChart.Data.GanttSeriesCollection.get_Item(Int32 index) at Infragistics.UltraChart.Core.ChartCore.GetChartInfoFromPrimitive(Primitive prim) at Infragistics.Win.UltraWinChart.DefaultTool.ProcessMouseMove() at Infragistics.Win.UltraWinChart.DefaultTool.MouseMove() at Infragistics.Win.UltraWinChart.UltraChart.OnMouseMove(MouseEventArgs evt) at System.Windows.Forms.Control.WmMouseMove(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)
As you can see none of my code is being run when the error is thrown thus I am unable to work out exactly where or why the error is occurring. I have reviewed my code extensively to ensure that I am not maintaining any references to primitives that should otherwise have been disposed, there I am at a loss as to how to overcome this error. Yes I am using a MouseDownTool for managing the dragging of time entries which is based on the InteractionTool but I cannot see any code within that class which could cause this error.
I have tried in vain to create a demo program which reproduces the error. Furthermore, due to the software pre-requisites of my program I am unable to distribute it for testing in its own right.
Is anyone able to help me please?
Thanks, Tony.
this will be hard to track down without a sample project, but let's see what we can find out. i have some questions for you based on clues from that stack trace:
are you adding custom primitives (shapes) to the SceneGraph?
Do you have any ILayer implementations in your project, or are you handling the FillSceneGraph or ChartDrawItem events?
is your datasource a GanttSeriesCollection?
are you using anything other than the GanttSeriesCollection.Add(GanttSeries) method to add the series?
Hi David,
Thanks for your reply. In answer to your questions:
1. Are you adding custom primitives (shapes) to the SceneGraph?
Yes, I am. I am creating boxes as background regions to highlight those time periods which fall within weekends, public holidays and/or outside of business hours. I am also adding small symbols over those GanttTimeEntries which extended beyond the visible date range.
I also change the properties of a primitive provided through UltraChart.Axis.Y.MinorGridLines being visible where it matches the currently selected UltraGrid row (I make it wider and change its color). This, however, is only a recent change to the control and comes after the afore mentioned error was already in existence.
2. Do you have any ILayer implementations in your project?
No.
3. Are you handling the FillSceneGraph or ChartDrawItem events?
FillSceneGraph – yes, ChartDrawItem – no. The chart’s FillSceneGraph event is handled in three separate places:
a. within the custom control itself to add the primitives mentioned in my answer to Q1.
b. within an InteractionTool that manages primitive dragging / resizing (this is called a DraggingGanttElementTool and came from source code I got off the forum somewhere about a year ago).
c. within another InteractionTool that draws a dotted line when an area on the chart is highlighted when dragging the mouse (I call this a DraggingRegionTool).
4. Is your datasource a GanttSeriesCollection?
No. I use the Series property of the UltraChart to manually add a GanttSeries for each row in the UltraGrid control. Each GanttSeries has a single GanttItem to which GanttTimeEntry objects are added to its GanttTimeEntryCollection. I use this method because I have a complex collection of custom objects that manages the data for the custom control.
5. Are you using anything other than the GanttSeriesCollection.Add(GanttSeries) method to add the series?
I hope this all helps. Thank, Tony.
check your Series collection and make sure there are only GanttSeries in there, as that might cause a problem.
what's happening is, a hit test occurs (for tooltips, most likely), finds a primitive, and sees that its Layer property is a GanttLayer. it then tries to index into an internal GanttSeries collection using the primitive.Row property value. if there are any tooltip-capable primitives in your SceneGraph with a Row higher than the index of the last GanttSeries, the exception will be thrown.
so, my best guess is an issue with the Series collection containing non-Gantt series. my second best guess is a FillSceneGraph event handler adding primitives with an invalid row number.
if none of those work, and if you're unable to provide a sample project, try downloading the controls' source code if you own a copy; this can be a valuable debugging resource under circumstances like this. for example, if you debug into our source code where the exception is thrown, you can find out more information about the primitive that's causing a problem in hit testing.
It’s taken me a while to get back to this – but I have. I downloaded the source code as suggested and am pretty sure that the problem lies within the UltraWinChart code.
I believe the problem is to do within the DefaultTool class in that it contains a class-level variable called “oldPrimitive” which holds a reference to the last primitive over which the mouse was moved. The problem is that this reference is never set to null. I will do my best to describe the process by which the error occurs:
ChartDataEventArgs hitEvent = this.UltraChart.ChartCore.GetChartInfoFromPrimitive(oldPrimitive);
The problem here is that the oldPrimitive variable still contains a reference to a primitive that no longer exists. And hence an exception is thrown within the GetChartInfoFromPrimitive method on the line GanttSeries currentSeries = series[prim.Row]. It is only later in the ProcessMouseMove sub that the oldPrimitive field is given a new value.
So the solution I came up with was to create some code that releases the reference to oldPrimitive from within the DefaultTool class. This is what I did:
1. Within the IInteractionTool interface I added: /// <summary> /// Releases references to any primitives. /// </summary> void ReleasePrimitiveReferences();
/// <summary> /// Releases references to any primitives. /// </summary> void ReleasePrimitiveReferences();
2. Within the InteractionTool class I added: /// <summary> /// Releases references to any primitives. /// </summary> public virtual void ReleasePrimitiveReferences() { }
/// <summary> /// Releases references to any primitives. /// </summary> public virtual void ReleasePrimitiveReferences() { }
3. Within the DefaultTool class I added: /// <summary> /// Releases references to any primitives. /// </summary> public override void ReleasePrimitiveReferences() { oldPrimitive = null; }
/// <summary> /// Releases references to any primitives. /// </summary> public override void ReleasePrimitiveReferences() { oldPrimitive = null; }
4. Within the UltraWinChart controls Tool property set code I added a call to this new ReleasePrimitiveReferences sub as follows:
internal IInteractionTool Tool { get { if (tool == null) tool = this.defaultTool; return tool; } set { if (this.tool != value) { if (this.tool != null) this.tool.Stop();
if (value == null) { this.defaultTool.ReleasePrimitiveReferences(); this.tool = this.defaultTool; } else { this.tool = value; } if (this.tool != null) this.tool.Start(); } } }
if (value == null) { this.defaultTool.ReleasePrimitiveReferences(); this.tool = this.defaultTool; } else { this.tool = value; }
if (this.tool != null) this.tool.Start(); } } }
Through this code then whenever the UltraWinCharts tool property is set to null, and hence the defaultTool is used, then the reference to the oldPrimitive is released.
I’m sure you guys might have a another (possibly better) way of achieving the same result, but I believe that the oldPrimitive variable has to release its reference to any primitives once the mouse leaves the UltraWinChart control.
Cheers, Tony.
thanks for the detailed report. we will keep this for review next time we need to make an adjustment to the mouse interaction code.
for now, your solution sounds good, but since i haven't been able to reproduce the ArgumentOutOfRangeException, i can't really justify the risk of side effects.
i did, however, add some bounds checking to prevent from indexing into the Series collection with an invalid value ... this fix will be included in an upcoming service release. i can't tell if that will make your application work properly where the exception was thrown before, but i expect it will.