The purpose of this article is to provide some general programming practice guidelines and troubleshooting tips to improve performance when using the UltraWinGrid control. Not all of the tips provided here will apply to every application, but it should help with the most common performance issues a developer is likely to encounter.
If you are concerned about reducing the amount of memory used by the grid, then there are several important things you can do to reduce it.
The grid does not create UltraGridCell objects for every row of data. Cells are only created as neccessary. So whenever possible, you should avoid accessed a cell. For example, suppose your code is using the InitializeRow event of the grid in order to calculate a total. For example:
}
This code references three cells in each row of the grid, thus creating a lot of objects which are potentially unneccessary. This can be avoided using methods on the row to deal with cell values, rather than the cell objects themselves. Like so:
By using the GetCellValue method, we can eliminate 2 cells per row in this example and save memory.
Another common use for the InitializeRow event is to apply an appearance to a cell based on the Value of that cell. Applying an appearance to a cell requires getting a reference to the UltraGridCell, so that is unavoidable. But you can save memory by re-using the same appearance object for multiple cells. Suppose, for example, that you want to change the ForeColor of a cell to red for negative numbers and black for positive numbers. You might do something like this:
This code will create a cell for every row. That is unavoidable, since the cell must be created in order to have an Appearance. The bigger problem here is that the Appearance property on the cell is lazily created. That means that we are not only creating a cell for each row, but a new Appearance object for each row. And since there are only two possible colors, we end up creating a large number of identical appearance objects.
A better way to do this is to create the Appearances we need up front and then re-use the same appearance wherever it is needed.
Another benefit to this approach is that you can change the appearance everywhere en masse. For example, suppose your users want to use Green instead of Black for positive appearances. You could, at run-time, set the ForeColor of the “Positive” Appearance object, and every cell in the grid that was using that appearance would update automatically.
It would be nice if this was a configuration option. Something that I could set as a global default and something that I could set at a project level.
Hi,
We could probably change the default to a more reasonable number, but I doubt it would help much. The problem is that it's really hard to come up with a number that is reasonable in all cases. You suggested 5 here, but I'm sure there are applications that use more than 5 levels of band depth and work perfectly fine. So changing the default to 5 would cause those applications to suddenly stop showing any data beyond the 5th level.
The level of depth that you can use depends on a wide range of factors like what kind of data source you are using and the processing power of your machine. On some machines with some data sources, a setting of 10 or even 20 might work fine, whereas on other machines, 8 would be too high.
We would probably be justified in setting the default to something like 20 or 30. But this could still cause problems and break existing applications for some users. And chances are, it won't help much. Since the performance issues increase geometrically, once you cross the threshhold by even 1 or 2 levels, you're pretty much doomed.
Mike Saltzman"]set MaxBandDepth to a more reasonable number. Very few users will find it useful to drill down into the data 100 levels deep
I agree this is an unreasonable number. In fact, I have had more lockups in Visual Studio from this setting than anything else. Its okay if you catch it, but if you modify your business entities after the fact you may find yourself unable to open your form in the designer.
Since this is such an unreasonable number, is there any way the default can be changed to 5 in a future release?
vrn said: Mike, I remember reading somewhere in this forum about turning off some features of Grid if one does not need it and thereby gaining performance. I thought you had mentioned it in this thread but I do not see to find it here. Thanks!
Mike, I remember reading somewhere in this forum about turning off some features of Grid if one does not need it and thereby gaining performance. I thought you had mentioned it in this thread but I do not see to find it here.
Thanks!
It sounds like you are talking about item 2 on the list - the CellDisplayStyle property. This allows you to turn off some features of a column.
jammerms said: Mike, In regards to your first point, do BeginUpdate/EndUpdate and/or SuspendRowSynchronization/ResumeRowSynchronization need to be used when just binding the data source to the grid?Example: try { DataTable dtTable = myDAO.GetTable(); ugGrid.BeginUpdate(); ugGrid.SuspendRowSynchronization(); ugGrid.DataSource = dtTable; } catch .... finally { ugGrid.ResumeRowSynchronization(); ugGrid.EndUpdate(); } Just wondering if there's any performance benefit to that approach, or if the control already suspends itself until InitializeLayout and all InitializeRow events have completed. Thanks, Jamal
Mike,
In regards to your first point, do BeginUpdate/EndUpdate and/or SuspendRowSynchronization/ResumeRowSynchronization need to be used when just binding the data source to the grid?Example:
try
{
DataTable dtTable = myDAO.GetTable();
ugGrid.BeginUpdate();
ugGrid.SuspendRowSynchronization();
ugGrid.DataSource = dtTable;
catch ....
finally
ugGrid.ResumeRowSynchronization();
ugGrid.EndUpdate();
Just wondering if there's any performance benefit to that approach, or if the control already suspends itself until InitializeLayout and all InitializeRow events have completed.
Thanks,
Jamal
Hi Jamal,
That's an interesting question. My first instinct is to say, there's probably no benefit to this. But I decided to test it out very quickly just to see.
I bound a grid to a DataTable with 100,000 rows and used the StopWatch class to time it.I ran the test twice each way, which is not really enough for a full statistical analysis, but it should do for a quick test.
Without the BeginUpdate / SuspendRowSynchronization, the results were:
1652 milliseconds1745 milliseconds
With the methods, the results where:
16741700
So it doesn't look like it make a whole lot of difference.