Hi,
I have a grid where some of the values in the grid are animating on a timer. I would like to iterate through the visible (ie, visible to the user) rows and refresh the necessary UltraGridCells if the cells are animating.
Is there a way to iterate through the on-screen, visible rows of the grid?
I have tried using the VisibleRowCount, but that returns all of the rows in the grid, and not just the rows being displayed.
I have also tried using the ActiveRowScrollRegion.VisibleRows, but that method occasionally throws an exception.
So far, my best solution is to iterate through all of the rows in the grid, check the UIElement to know if the row is being displayed, and refresh the row as necessary. This does not seem to perform well as the grid could contain 1000s of rows whereas only 50 or so are visible to the user.
Any ideas?Thanks in advance,Steve
BlockSwong said:I have also tried using the ActiveRowScrollRegion.VisibleRows, but that method occasionally throws an exception.
This is what I would use. What exception are you getting? You should never get an exception accessing this property - unless you are accessing it from another thread, maybe.
Hi Mike,
Thanks for writing back. The exception stack trace is included below.
The property is being accessed via a timer on the same thread as the UltraGrid (the main UI thread). The exception causes my application to unexpectedly crash which is unfortunate as it can never be handled in my code. Any thoughts?
Thanks, Steve
Exception details: Message=Index was outside the bounds of the array., StackTrace=
at Infragistics.Shared.SparseArray.GetItemAtScrollIndex(Int32 scrollIndex, ICreateItemCallback createItemCallback) at Infragistics.Win.UltraWinGrid.ScrollCountManagerSparseArray.GetItemAtScrollIndex(Int32 scrollIndex, Boolean allocate) at Infragistics.Win.UltraWinGrid.RowsCollection.GetRowAtScrollIndex(Int32 scrollIndex, Boolean allocate) at Infragistics.Win.UltraWinGrid.RowsCollection.GetRowAtScrollIndex(Int32 scrollIndex) at Infragistics.Win.UltraWinGrid.UltraGridLayout.GetRowAtScrollPos(Int32 pos) at Infragistics.Win.UltraWinGrid.ViewStyleBase.CreateRowsList_FixedRowsFeature(VisibleRowFetchRowContext& context, VisibleRow parentVisibleRow, UltraGridRow firstSibling) at Infragistics.Win.UltraWinGrid.ViewStyleBase.RecreateRowList(RowScrollRegion rsr) at Infragistics.Win.UltraWinGrid.RowScrollRegion.GetMaxScrollPosition(Boolean scrollToFill) at Infragistics.Win.UltraWinGrid.RowScrollRegion.EnsureScrollRegionFilled(Boolean calledFromRegenerateVisibleRows) at Infragistics.Win.UltraWinGrid.RowScrollRegion.RegenerateVisibleRows(Boolean resetScrollInfo) at Infragistics.Win.UltraWinGrid.RowScrollRegion.WillScrollbarBeShown(ScrollbarVisibility assumeColScrollbarsVisible) at Infragistics.Win.UltraWinGrid.RowScrollRegion.PositionScrollbar(Boolean resetScrollInfo) at Infragistics.Win.UltraWinGrid.ScrollRegionBase.SetOriginAndExtent(Int32 origin, Int32 extent) at Infragistics.Win.UltraWinGrid.RowScrollRegion.SetOriginAndExtent(Int32 origin, Int32 extent) at Infragistics.Win.UltraWinGrid.DataAreaUIElement.ResizeRowScrollRegions() at Infragistics.Win.UltraWinGrid.DataAreaUIElement.PositionChildElements() at Infragistics.Win.UIElement.VerifyChildElements(ControlUIElementBase controlElement, Boolean recursive) at Infragistics.Win.UltraWinGrid.DataAreaUIElement.VerifyChildElements(ControlUIElementBase controlElement, Boolean recursive) at Infragistics.Win.UltraWinGrid.DataAreaUIElement.set_Rect(Rectangle value) at Infragistics.Win.UltraWinGrid.UltraGridUIElement.PositionChildElements() at Infragistics.Win.UIElement.VerifyChildElements(ControlUIElementBase controlElement, Boolean recursive) at Infragistics.Win.UltraWinGrid.UltraGridUIElement.VerifyChildElements(ControlUIElementBase controlElement, Boolean recursive) at Infragistics.Win.UIElement.DrawHelper(Graphics graphics, Rectangle invalidRectangle, Boolean doubleBuffer, AlphaBlendMode alphaBlendMode, Boolean clipText, Boolean forceDrawAsFocused, Boolean preventAlphaBlendGraphics) at Infragistics.Win.UIElement.Draw(Graphics graphics, Rectangle invalidRectangle, Boolean doubleBuffer, AlphaBlendMode alphaBlendMode, Boolean forceDrawAsFocused, Boolean preventAlphaBlendGraphics) at Infragistics.Win.ControlUIElementBase.Draw(Graphics graphics, Rectangle invalidRectangle, Boolean doubleBuffer, AlphaBlendMode alphaBlendMode, Size elementSize, Boolean preventAlphaBlendGraphics) at Infragistics.Win.ControlUIElementBase.Draw(Graphics graphics, Rectangle invalidRectangle, Boolean doubleBuffer, AlphaBlendMode alphaBlendMode, Size elementSize) at Infragistics.Win.ControlUIElementBase.Draw(Graphics graphics, Rectangle invalidRectangle, Boolean doubleBuffer, AlphaBlendMode alphaBlendMode) at Infragistics.Win.UltraWinGrid.UltraGridUIElement.Draw(Graphics graphics, Rectangle invalidRectangle, Boolean doubleBuffer, AlphaBlendMode alphaBlendMode) at Infragistics.Win.UltraControlBase.OnPaint(PaintEventArgs pe) at Infragistics.Win.UltraWinGrid.UltraGrid.OnPaint(PaintEventArgs pe) 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.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).
Hm, that's odd. This shows that the exception occurs during a paint, not even during the firing of the timer tick. Is it a System.Timers.Timer or a System.Windows.Forms.Timer?
Can you reproduce this reliably? If so, you should probably Submit an incident to Infragistics Developer Support so they can check it out.
If you are concerned about getting the best possible efficiency, then you can use the UIElements of the grid directly. Here's some code that uses the UIElements to get the rows in the visible area. This code is assuming that there is only one ScrollRegion in the grid and that thereare no GroupByRows.
private List<UltraGridRow> GetVisibleRows(UltraGrid grid) { List<UltraGridRow> rows = new List<UltraGridRow>(); UIElement element = this.ultraGrid1.DisplayLayout.UIElement; if (element != null) { element = element.GetDescendant(typeof(DataAreaUIElement)); if (element != null) { element = element.GetDescendant(typeof(RowColRegionIntersectionUIElement)); if (element != null) { foreach (UIElement childElement in element.ChildElements) { if (childElement is RowUIElement) { UltraGridRow row = childElement.GetContext() as UltraGridRow; if (row != null) rows.Add(row); } } } } } return rows; }
Hi Steve,
Well, that's a pretty clear indication that this is a threading issue. You say you are updaing the data source in a thread-safe manner. If you are updating the grid's data source on a thread other than the UI thread, then that's not safe under any circumstances because the data source may fire events that the grid is responding to on another thread. You must always suspend the notifications.
The method you provided works well, but I am still seeing the same exception. I did notice one thing, though. We have multiple threads updating the underlying datasource in a thread-safe manner. Each update to the datasource conditionally calls SuspendBindingNotifications depending on the number of updates being done (let's say, more than 10). If I remove that condition, and always suspend the binding notifications, I do not run into any unexpected exceptions with the stacktrace above. When I include the condition to not suspend the binding notifications, I am able to reproduce the error after waiting an undeterministic amount of time. The downside to always suspending the binding notifications is that the overall performance seems slower.
I know this is a rather vague description of the symptoms, but just throwing it out there.
Thanks for your help,
Steve