Hi,
I'm having some issues with the performance of the ultrawingrid. In my situation, I have a grid with a group by column and about 8000 rows. I need to loop through the individual rows to set values to some unbound columns. However, rather than the assigning of those values, just the looping through the rows (actually: getting the enumerator) causes a delay of a few seconds.
I created a small sample project demonstrating the problem. The project groups a column in the grid and fills it's datasource with 10000 rows. When clicking the test button, it refills the datasource, and loops through the rows. This takes a few seconds, and is too long for a smooth user experience.
My question is, how can I speed up the enumeration of the rows?
I already tried the SuspendUpdate and SuspendRowSynchronization as mentioned in the performance guide, but that didn't help. I also tried the GetRowEnumerator method, but that didn't help either. I hope you can. Thanks in advance.
I'm using version 10.3.20103.1000
Hello TorX,
I`m not sure what is your final goal, but do you think that it is possible to use ultraGrid1_InitializeRow() event, instead of Loop to set the values in your UnboundColumn ? Or maybe you could use UltraCalcManager to set the values in your UnboundColumn ? Of course you could try miltiple thread / backgroundworker to prevent hang up of your application.
Let me know if you have any questions.
Thanks for your reply. I've just tried the InitializeRow approach, but I still have the same delay in the UI (however I can't measure it anymore using a StopWatch). I don't think a background worker can help, because I still have to assign the values in the UI thread, which would freeze up the UI. Is there another way?
By the way, without grouping there is no delay. But unfortunately, our users need the grouping (because of the many rows).
I agree with you that using a background worker thread would not help, and could easily cause more problems.
I'm a little puzzled, though - when you use InitializeRow, when exactly is the delay? What exactly is delayed? The display of the form?
My guess is that your InitializeRow code is probably not as efficient as it could be, because setting the value of a cell in InitializeRow is very fast and should not cause any delay at all.
Can you post your code in InitializeRow?
After more examining, it seems the InitializeRow is not relevant for my problem. Please replace the form code in my sample with the code below. I've removed the enumeration. Because the label's text is changed immediately, the timer is no use anymore, so I replaced it with a counter. As you can see, it takes a while before the label is updated (GUI is frozen). If you ungroup the 'Group' column, the label is updated very fast (desired result).
public partial class Form1 : Form { private readonly BindingList<Foo> Items = new BindingList<Foo>(); private int Counter; public Form1() { InitializeComponent(); } private void DoPerformanceTest() { FillItems(); ultraLabel1.Text = (++Counter).ToString(); } private void FillItems() { Items.Clear(); for (var i = 0; i < 10000; i++) Items.Add(new Foo(i.ToString(), GetGroup(i))); } private string GetGroup(int i) { var c1 = (char)(65 + ((i / 26) % 15)); var c2 = (char)(65 + (i % 26)); return c1.ToString() + c2.ToString(); } private void Form1_Load(object sender, EventArgs e) { ultraGrid1.DataSource = Items; ultraGrid1.DisplayLayout.Bands[0].SortedColumns.Add("Group", false, true); FillItems(); } private void ultraButton1_Click(object sender, EventArgs e) { DoPerformanceTest(); } }
I tried out your sample and it seems that the time is being taken up by the grid painting.
Your code is performing a whole bunch of operations on the data source, which sends over a thousand notifications to the grid, but all of these are being handled almost instantaneously - the grid is just marking the appropriate objects dirty.
When the operation completes, the grid has to paint. At this point, the grid has an entirely different set of rows then it did when it started so it has to rebuild the entire Rows collection and re-group all of the rows. So this is what must be taking up the time. But it's interesting, because if I just ungroup and regroup the grid, it's pretty fast and doesn't take anywhere near as much time. It's also much faster when you run the same code the first time (in Form_Load), probably because the grid is optimized before the first time it paints.
We could look into this and see what's going on, and maybe there is some way we could make this process more efficient. But frankly, I'm not sure it's something we could fix. The grid might be doing some necessary process to completely rebuild the entire set of rows.
What your code is doing here is a bit unusual and inefficient. Why remove all of the rows from the grid and re-add them? If you want to do something like that, it would be a lot more efficient to simple assign a new data source to the grid so that it happens in one operation instead of 1000 operations.
If that's not a viable option, then another workaround I stumbled onto is saving the layout before your operations begin and then restoring it afterward.
private void FillItems() { Infragistics.Win.UltraWinGrid.UltraGridLayout layout = this.ultraGrid1.DisplayLayout.Clone(); Items.Clear(); for (var i = 0; i < 10000; i++) Items.Add(new Foo(i.ToString(), GetGroup(i))); this.ultraGrid1.DisplayLayout.CopyFrom(layout); }
Just a guess, but I think this works because something about what you are doing to the data source is leaving the layout in a weird state and this simply clears it out - in a way telling it to just start over instead of whatever it's doing that's slowing it down.
Thanks! I would appreciate it if you can look into this, because I still think it's a little strange why it takes much longer when grouped than not grouped.
I am a little bit confused by your workarounds though. To answer your first question, I'm removing and re-adding the rows (with different data each time in my business application of course) because I'd like to keep any changes the user has made to column order, widths and grouping. I always use the InitializeLayout event to set default column ordering and widths, and resetting the DataSource fires that event.
Second, the code you provided does not work for me, I still get the same delay. Is it possible that something is missing from that code?
However, the changes you made in that code could in fact help me. The problem I just mentioned about the InitializeLayout event getting fired after setting the DataSource, can be solved by cloning and restoring the layout. I didn't know this trick, and can certainly be helpful.
Do you think this is the way to go, or is there a better solution?
Thank you for sorting this out.
I just wanted to let you know that we looked into this more thoroughly and it's pretty much as I thought.
When something changes, there are basically two code paths the grid can go down when rebuilding the data.
One approach is for the grid to blow away everything - all the GroupByRows and DataRows and then rebuild everything from scratch. This is what would happen if you changed the Grouping - like if you added or removed a GroupByColumn.
The other approach is to try to keep as much as possible and only update the rows that were changed. This second approach is usually much more efficient and it also has the advantage of being able to maintain the state information on the rows. So if you added a single row to the data source, the grid would just add in that one row and put it under the property GroupByRow. And if some other row were selected and the grid was scrolled to a particular point, everything would stay as it was.
The second approach would even be faster, if you were removing 10 rows, or 100 rows. The performance issue here steps from the fact that you are removing and re-adding ALL of the rows, and there are 10,000 of them. So the grid is trying to rebuild them one at a time, because it doesn't know enough to blow away everything.
We considered changing this in the grid, but it's tricky. The grid could detect that none of the original 10,000 rows are still in use and blow away everything.
But we decided against it for 2 reasons:
1) This would cause a change in behavior. It's the same change in behavior that my workaround causes - you will lose the state information - mainly the scroll position. This probably isn't a big deal, and may even be desirable, but we don't want to change it and potentially break existing applications if we don't have to.
2) This solution would only work if you removed all of the rows. If you removed 9,999 rows, it wouldn't make any difference.
Overall, I really think that if you are replacing all of the rows, you are better off just attaching a new DataSource to the grid, rather the re-using the existing one.
I did, however, find another workaround which requires a little less code. Forcing the grid to refresh the rows after you remove all of the items form the data source, but before you've added the new ones works just as well. And you can do that simply by accessing the row count.
private void FillItems() { Items.Clear();
// Force a refresh int x = this.ultraGrid1.Rows.Count;
for (var i = 0; i < 10000; i++) Items.Add(new Foo(i.ToString(), GetGroup(i))); }
Ok, thank you for the clarification.
TorX said:What do you think is the best way to handle this situation?
It's really a coin toss. If the workaround I gave you here is working, I'd stick with it, I guess. No reason to change your code any more than you have to.
Setting the DataSource might be more efficient, in theory, since it avoids sending 1001 notifications to the grid. On the other hand, those 1001 notifications are not causing an immediate performance problem - the grid is handling them very efficiently. Setting the data source is also more destructive. You will lose the Selected and Active rows and the grid's current scroll position, for example. In this case, those don't matter, since you are removing all of the rows, anyway. But if you were only removing a few rows or modifying rows instead of clearing everything, it might make a difference.
Infragistics Mike Saltzman said:If you are still seeing the same issue in my updated sample, then it could be because I am using the latest service release of NetAdvantage 10.3 and you are using an older version.