I know the immediate response to this question is "sounds like a threading issue" and I don't disagree, but I don't see it being on our end.
The setup:
- Using the very latest service release of 10.3.- I have a form with a typical grid bound to BindingSource. The BindingSource in turn is bound to a BindingList. - There are multiple instances of this form (grid/bindingsource), and each one is bound to the same, single instance of the BindingList.- The BindlingList is updated every quarter second on a UI thread timer and ONLY on the UI thread (easily seen by looking at the code, and for sanity a logging method hooked to each bindingsource's ListChanged event shows it is only occuring on the UI thread). It's a very simple setup.- After the update the grid is resorted (on the UI thread) via _grid.DisplayLayout.Bands[0].SortedColumns.RefreshSort(false);
The exception:
This is only seen every once in a while with no noticable pattern and isn't prompted by any user interaction.
System.IndexOutOfRangeException: Index was outside the bounds of the array. at Infragistics.Shared.SparseArray.ValidateIndex(Int32 index) at Infragistics.Shared.SparseArray.RemoveAt(Int32 index) at Infragistics.Win.UltraWinGrid.RowsCollection.OnListChangedHelper(ListChangedEventArgs e, Boolean calledFromBandListChanged) at Infragistics.Win.UltraWinGrid.RowsCollection.OnListChanged(Object sender, ListChangedEventArgs e) at System.ComponentModel.ListChangedEventHandler.Invoke(Object sender, ListChangedEventArgs e) at System.Windows.Forms.BindingSource.OnListChanged(ListChangedEventArgs e) at System.Windows.Forms.BindingSource.InnerList_ListChanged(Object sender, ListChangedEventArgs e) at System.ComponentModel.ListChangedEventHandler.Invoke(Object sender, ListChangedEventArgs e) at System.ComponentModel.BindingList`1.OnListChanged(ListChangedEventArgs e) at System.ComponentModel.BindingList`1.FireListChanged(ListChangedType type, Int32 index) at System.ComponentModel.BindingList`1.RemoveItem(Int32 index) at System.Collections.ObjectModel.Collection`1.Remove(T item)
The question:
I know that a when you change the sort by clicking a column header, the sort will be done on a background thread by default (we are setting ProcessMode.Synchronous in the beforesortchange event handler to avoid issues there), but does RefreshSort do any manipulation on a different thread?
Hm. Sounds like a real catch-22 there.
If you can duplicate this in a small sample, we would be happy to look into it and see if there is some way to work around it.
But at this point, I am pretty much out of suggestions since it seems like your application is so complex.
Yep, that's the pattern use. The problem is that I need control over when the grid is resorted by whatever column sort criteria is selected and if I suspend row synchronization the grid will just go ahead and resort itself. Without suspending row synchronization I need to explicitly call RefreshSort, which is what I want, but results in the grid getting out of sync.
Sad day.
Hi,
The grid never uses any additional threading, but there are cases when it can get out of synch, because it does do certain things asynchronously. For example, if you add a row to the grid's DataSource, the grid gets a notification from the DataSource that this happened. But the grid does not immediately update it's own rows collection, it just marks the collection dirty.
This is advantageous, and even necessary, because the BidningManager in DotNet is a little quirky about the messages it sends. Sometimes a single operation can result in multiple messages.
The grid responds to the notification only when it need to access the rows collection which changes. This often does not happen until the next time the grid paints.
Anyway, are you also using BeginUpdate and EndUpdate? You should always use these in conjunction with Suspend/ResumeRowSynchronization because if the grid paints, it has no choice but to synchronize, regardless of whether synchronization is suspended. So the common pattern looks something like this:
this.ultraGrid1.BeginUpdate(); this.ultraGrid1.SuspendRowSynchronization(); try { // Perform multiple operations on the DataSource here. } finally { this.ultraGrid1.ResumeRowSynchronization(); this.ultraGrid1.EndUpdate(); }
It appears I am unable to utilize SuspendRowSynchronization().
For this grid, while the mouse is over a row sorting is suspended to keep the rows from flying all over the screen (due to the rapid updates). This is checked in the timer and if we are over a row we skip the call to RefreshSort. If row syncronization is suspended, then it appears the grid applies the sort automatically, I'm guessing this happens when it is repainted and there are rows marked as dirty?
The grid only has a single band, but I had actually rewritten some of the code last night so that I could use Suspend/ResumeRowSynchronization for performance reasons (it was very decoupled, to the point where the form was not aware of when the datasource would be changing). I left it running overnight and will be running it all day today but so far I haven't seen the exception resurface.
Thanks as always for your help. I knew the issue was too vague to have an obvious or guarenteed solution (if i could reliably reproduce it I probably wouldn't have needed to pose the question).
My biggest questions is/was if everything is being done on the same thread within the WinGrid how an internal array get out of sync? It seems to me that no matter how hard you hit it the method execution should always happen in the same order, unless you are using timestamps to perform delayed actions as things are processed?