Hello,
I have a question again regarding a hierarchical dataset as datasource for my ultragrid. I have on every level a checkbox column in the header and therefore in every row. This is for checking or unchecking the row for later processing later on. Unfortunately this header checkbox is only working for the level it is on, that means if I check it, it doesn't check the child rows automatically. Thus I manually programmed this functionality but it has bad performance for a higher amount of datarows. I my current test example I have 1500 rows on the first level and multiple bands which are holding lots of childrows, one of em even about 8000-12000 rows. I also need to set a headercheckbox to indeterminate if at least one datarow in the lower childlevels is not checked. And here is my problem, because therefore I have to iterate through ALL rows and have to look if it is checked. Currently I'm doing this with the example code of this:
http://help.infragistics.com/Help/NetAdvantage/WinForms/2011.1/CLR2.0/html/Infragistics2.Win.UltraWinGrid.v11.1~Infragistics.Win.UltraWinGrid.UltraGridRow~ChildBands.html
That takes in my example (I have single core 64bit, 2ghz, 32bit winxp, 2gb ram) almost one minute.
my question is, do you think this is normal with that mass of datarows or do you have any idea too make things differently?
here is my part of code:
Protected Function IsEveryRowChecked(ByVal Rows As RowsCollection) As Boolean For Each Row As UltraGridRow In Rows If DirectCast(Row.Cells("check").Value, Boolean) Then If Not Row.ChildBands Is Nothing Then For Each ChildsChildBand As UltraGridChildBand In Row.ChildBands If Not IsEveryRowChecked(ChildsChildBand.Rows) Then Return False End If Next End If Else Return False End If Next Return True
End Function
Greetings,
Renelope
Hi Renelope,
I took a look at the link you posted and that code is out of date. There's actually a much easier way to loop through every row in the grid, now:
For Each Row As UltraGridRow In Me.UltraGrid1.Rows.GetAllNonGroupByRows() ' Do somehting to the row Next
This will loop through all of the data rows in the grid that are not GroupByRows. There are other methods on the Rows collection to get enumerators like this, depending on what you want. For example, you might want to use GetFilteredInNonGroupByRows if you want to ignore rows that have been filtered out.
Anyway... I doubt that has anything to do with the performance issue you are experiencing.
Regarding performance, my guess is that the major part of the performance hit is not coming from the grid, it's coming from the data source. Asking the DataSet for the child rows of any particular parent row can be a very expensive operation. This makes sense if you think about it, because let's say, for example, you have a DataSet with 2 tables and a Relationship between those tables. Let's further assume that your child table has a total of 10,000 row in it. When you ask for the child rows for any individual parent row, the DataSet has to loop through all 10,000 child rows and evaluate each one. And then when you ask for the child rows of another parent, it has to loop through all 10,000 rows again.
If that is the case, then there's not much you can do. You could use some other data source, of course, but there will always be a hit somewhere as you load the data and link up the parent and child rows.
There are certain things you can do to make the grid code more efficient. For example, you can save a lot of memory by not forcing the grid to create cell objects. This is described in detail in the WinGrid Performance Guide.
If you can post the code you are using that actually accesses the grid rows and cells so I can see what you are doing there, I might be able to offer you some other suggestions about improving that code. But if I am right about the DataSource, then the efficiency of the grid code probably won't make much of a dent.
Hi Mike,
thanks for your answer! I considered your performance guide and changed some things in my application. Regarding my problem in this post it didn't change anything unfortunately. So I'm pretty sure you're right with your guess about the DataSet Relationship problem.
Here is what I'm actually trying to do:
In my InitialzeLayoutOfGrid event method I call a selfwritten method to accomplish this check behaviour I described in my first post. This selfwritten method looks like this:
Protected Sub SetHeaderCheckState(ByVal Rows As RowsCollection) If IsNonRowChecked(Rows, "chkBox") Then Rows.Band.Columns("chkBox").SetHeaderCheckedState(Rows, CheckState.Unchecked) ElseIf IsEveryRowChecked(Rows) Then Rows.Band.Columns("chkBox").SetHeaderCheckedState(Rows, CheckState.Checked) Else If Not Rows.Band.Columns("chkBox").GetHeaderCheckedState(Rows) = CheckState.Indeterminate Then Me.ultraGrid.DisplayLayout.Override.HeaderCheckBoxSynchronization = HeaderCheckBoxSynchronization.None Rows.Band.Columns("chkBox").SetHeaderCheckedState(Rows, CheckState.Indeterminate) Me.ultraGrid.DisplayLayout.Override.HeaderCheckBoxSynchronization = HeaderCheckBoxSynchronization.Default End If End If End Sub
The performance problem is caused by the recursive method IsEveryRowChecked(Rows) I pasted in my first post. Did you ever came across a case when somebody wanted to do the same thing I want to do and had a better way to handle this? Any suggestions are welcome, thank you.
Hi,
Like I said, if it's the DataSet that causing the problem, then there really isn't much you can do. There's no way to make the DataSet perform any faster.
If the CheckBox field if bound then one thing you could do is loop through the rows in the data source instead of the rows in the grid. This would be faster because you would not be creating the grid rows and also because you could loop through the rows in the child table only once.
This, of course, assumes that every row in your child table is displayed in the grid. If you have some child rows in your data that have no corresponding parent row, then they would have to know about it and skip those rows.
If the CheckBox is not bound, then looping through the grid rows is the only option. In that case, you might be able to make your code more efficient in some cases. It's not clear to me exactly what your code is doing right now, but it looks like you might be looping through the rows more than once. It also looks like there are cases where you could bail out early and you are not.
What I would do is use the GetAllNonGroupByRows method I posted earlier. Keep a flag for hasCheckedRow and hasUncheckedRow. The first time I hit a row that is checked, I would set hasCheckedRow to true. The first time I hit a row that is unchecked, I would set hasUncheckedRow to true.
If both are true, there is no reason to continue looping - you already know that you want the indeterminate state.
Still another option might be for you to track the states as you go, instead of trying to loop through everything. This only works if you know the initial state, of course. So if you know, in your application, that all the CheckBoxes start out unchecked, then you could trap CellChange, and other events and try to keep track of when changes occur without having to loop through everything.
Hi Mike, I had a requirement to select rows across 2 bands. I followed the above method for selecting all the rows
GetAllNonGroupByRows() but it is providing me only the primary band rows collection. When I tried to loop through all selected rows of first band and trying to select the child band rows my for loop is taking longer time to complete the operation.
I am using 10000 rows with 15 columns in the primary band and 1 child row for each primary row in each child band with 5 columns.
case "SelectAll": this.Cursor = Cursors.WaitCursor; ultraGrid1.Selected.Rows.AddRange(ultraGrid1.Rows.GetAllNonGrouplse, false); id SelectChildBandRo(); this.Cursor = Cursors.Default; private void SelectChildBandRows() { foreach(UltraGridRow rowSelected in ultraGrid1.Selected.Rows) ultraGrid1.Selected.Rows.AddRange(rowSelected.ChildBands[0].Rows.GetFilteredInNonGroupByRows()); }
I would recommend against selecting rows from both bands though code as the end user isn't able to do it in the UI and it might seem weird to them to have the rows selected in a way that they can't duplicate.
For the performance option, I would still advise against changing the Selected.Rows collection while iterating through it. Instead of looping through the Selected.Rows, maybe you could loop through the array of rows that is returned by GetFilteredInNonGroupByRows of the Rows collection on the grid since those are the rows that are currently selected since they were just added.
To improve performance, you may also want to build the string for the clipboard as you loop through the rows, at least for the parent rows. You could also see if calling PerformAction on the grid and passing in UltraGridAction.Copy meets your needs for adding data to the clipboard.
Thank you for the suggestion, but my requirement is bit different needed synch between the UI display and the performing of operation.
I have to copy the information to the clipboard at the same time I have to mimic the user that these are the selected rows that are being copied.
For providing a clarity for the user I am selecting all the rows and copying them to the clipboard. I am extracting data from all the selected rows to build a string.
The logic provided seems as though you are selecting all of the rows in the grid. If the only reason is to put the data in the clipboard, do you really need to select the rows in the grid?
You could build your own string to set the clipboard data to without needing to select the rows. There is logic in the microsoft forums to do this for a dataset that you could use as a guide on how to build the string. Note that it might be more efficient to use the ListObject of the row to build the sting for each row than to access the data from the UltraGridRow or UltraGridCell objects.
Hi Halama, Mike, We are using UltraWingrid (Infragistics v10.3) for our application. I have to copy all the selected rows information to a clip board, this should spawn across the bands (we have only 2 bands right now).
It sounds like what you really want here is more of a CheckBox where the parent checkbox reflects the parent, rather than selection. If you are not using any grid-specific features like filtering, summaries, or exporting, you might be better off using am UltraTree control instead of UltraGrid and then using the SynchronizedCheckBox Style for your nodes.