4

I googled and found a lot of advice, but it all seemed several years old and none of it helped.

I have a string grid with 8 columns and once I get more than a few hundred rows it is taking over 2 seconds to populate (I compared using GetTickCount).

I tried StringGrid.Perform(WM_SETREDRAW, 0, 0) (and 0, 1 at the end). I tried setting Visible := False while updating. Both to no use.

There is no BeginUpdate() method.

Any advice? Delphi XE2 starter. I would be willing to use a FOSS 3rd party VCL string grid if it is tried & tested.


[Update] using TDrawGrid ... "A TDrawGrid doesn't have a property "Cells", like its brother TStringGrid. Your code has to calculate where to display the data and next it must draw a representation of the data on the "Canvas" of the grid."

That sounds like a lot of work to me :-(

Using VirtualTreeView - sounds ok if it is fast enough. I just won't have any child nodes. (update++ I just read this on the homepage "Virtual Treeview is extremely fast. Adding one million nodes takes only 700 milliseconds"). No problems on speed, then. But it woudl nice to just use a string grid. Escpecially one where the user can click & sort.

Alternatively, the stringgrid is only 20 rows high. Maybe I could just handle scrolbar clicks and clear & repopulate those 20 rows when the user scrols?

[Furtehr update] I changed from TStringGrid to TListView which codes have Beginupdate()), but that made a negligible difference. Ops, I forgot "viortual mode" - brb.

Btw, the data are read-only, just for disply.

Surely this is a very common probem?

Mawg says reinstate Monica
  • 38,334
  • 103
  • 306
  • 551
  • 2
    The best performance will be achieved by changing your `TStringGrid` into a virtual grid, e.g. a `TDrawGrid` in virtual mode, which will retrieve the cells content via an Event from a separated data list. It will be *much* faster than this IMHO. I use `TDrawGrid` with thousands of rows with instant access, e.g. for [our log viewer](http://blog.synopse.info/post/2011/08/20/Enhanced-Log-viewer) - For instance, a 280 MB log file is opened in less than one second on my laptop. Under Seven, it takes more time to display the "Open file" dialog window than reading and indexing the 280 MB content. – Arnaud Bouchez Oct 12 '12 at 08:10
  • +1 @ArnaudBouchez That sounds excellent. I will go and google on how "a TDrawGrid in virtual mode, which will retrieve the cells content via an Event from a separated data list". Thanks – Mawg says reinstate Monica Oct 12 '12 at 08:14
  • 2
    Instead of `TDrawGrid` you may learn VirtualTreeView. It's *not just a tree view* as it sounds to be ;-) – TLama Oct 12 '12 at 08:22
  • @TLama I undertsand I could use VirtualTreeView with no child nodes, but would it give me perfomance that I want? – Mawg says reinstate Monica Oct 12 '12 at 08:43
  • 1
    Every control using virtual mode will give you a better performance than `TStringGrid`. It's not possible to compare `TDrawGrid` with `VirtualTreeView` just because they *looks* different, but both will be more efficient than `TStringGrid`, that's for sure. I've suggested you `VirtualTreeView` from my personal preference and because you can do much, much more things than with `TDrawGrid`. But if you really need just an old looking grid with very basic functionality, `TDrawGrid` might be sufficient for you. – TLama Oct 12 '12 at 08:57
  • To your *click & sort* update, `VirtualTreeView x TDrawGrid` score `1:0`. – TLama Oct 12 '12 at 09:00
  • 2
    @Mawg, could you tell us for what purpose are you using `TStringGrid` in this case - is it for displaying or editing data? If you only need to display data, then even `TListView` should give you more than sufficient performance gains, and if you use it in "virtual mode" you will gain even more. However, I have to agree with @TLama about `VirtualTreeView` - it is simply the best component out there. I've stopped using `TStringGrid` a long time ago and I've never looked back. – LightBulb Oct 12 '12 at 09:25
  • 2
    In response to your edit: putting the data in a memory client dataset and showing it by a `DBGrid` - which 'loads' only the visible rows - might also better perform. – NGLN Oct 12 '12 at 09:33
  • +1 it's read only, displaying data. How do I use a TListView in "virtual mode"? WAIT ... "a DBGrid - which 'loads' only the visible rows - might also better perform" *MIGHT* better perform?? It would only have to "load" 20 rows, not 700. I must look into this. Thanks – Mawg says reinstate Monica Oct 12 '12 at 09:38
  • 1
    @Mawg, I have used `TListView` (in normal mode) with thousands of rows and I never noticed any performance issues. You should check two things: first, how you're obtaining the data that you're displaying and second, how you're inserting the data into the `TListView`. Both of those can be a bottleneck, so I advise you re-examine your code. If you can, update your question with the peace of code that you're using to insert the data so we may be able to provide further assistance. – LightBulb Oct 12 '12 at 10:02
  • @NGLN 'putting the data in a memory client dataset and showing it by a DBGrid - which 'loads' only the visible rows" - that is rock solid. If you post it as answer, I will award. Thanks! – Mawg says reinstate Monica Oct 12 '12 at 10:20
  • I don't think the `TStringGrid` has changed much at all over the past number of years, in exception of the VCL Styles. – Jerry Dodge Oct 12 '12 at 12:05
  • @Mawg: "How do I use a TListView in virtual mode?". Set its `OwnerData` property to True and use its `OnData` event to provide values to TListView when it requests them. Store your data in memory wherever and however you want. If you need to implement more advanced caching, so you don't have all of the data in memory at one time, use the `OnDataHint` event to help you store in memory only the data that the TListView actually needs at any given moment, and discarding the memory for data it doesn't need. I use this approach to display millions of records and it works very well. – Remy Lebeau Oct 13 '12 at 00:47

3 Answers3

5

Add rows from bottom to top, and/or set RowCount afterwards. I just did a little test adding 90.000 rows, gaining ca. 25% in speed.

Though, this takes 1,5 seconds at most. Since you are talking about just a few hundred rows, I am sure populating the grid is not the burden here. Instead the time needed to retrieve and/or convert the data seems to be.

Community
  • 1
  • 1
NGLN
  • 43,011
  • 8
  • 105
  • 200
  • @Mawg Then the time needed decreases by more then 50%: the speed more then doubles. – NGLN Oct 14 '12 at 13:59
5

There is no BeginUpdate() method.

TStringGrid does have BeginUpdates: you need to access it via either the Rows[] or Cols[] arrays of TStrings, though for adding new data, using the Cols[] array makes most sense:

for i := 0 to Grid.ColCount - 1 do
begin
  Grid.Cols[i].BeginUpdate;
  try
    //Add row data
    for j := 1 to Grid.RowCount - 1 do
      Grid.Cols[i][j] := ...;
  finally  
    Grid.Cols[i].EndUpdate;
  end;
end;

I'm sure I've used this before with an increase in speed noted.

NGLN
  • 43,011
  • 8
  • 105
  • 200
Matt Allwood
  • 1,448
  • 12
  • 25
  • `Rows` and `Cols` are indexed properties and don't give access to `BeginUpdate` nor `EndUpdate`. Filling the grid isn't the problem though. – NGLN Oct 12 '12 at 08:27
  • 3
    cols is just an array of TStrings and each element of this array has BeginUpdate/EndUpdate `for i:= 0 to grid.colcount-1 do grid.cols[i].beginupdate` with corresponding try..finally – Matt Allwood Oct 12 '12 at 10:58
  • +1 That indeed speeds things up! Sorry for not understanding the first time. – NGLN Oct 12 '12 at 12:47
2

Adding to others suggesting a virtual string grid I'd like to plug TdzVirtualStringGrid which builds on a TDrawGrid and adds events to return the strings to display. I am using it for very "large" grids and it works fine.

Download it here.

(You need more files from the same repository, this is just the main component source code.)

EDIT: By "more files from the same repository" I mean, that this component uses other units from the dzlib library, so you should probably check out the whole shebang and either add it to your program's search path (there is a lot more useful stuff in there, because I add to it whenever I come across something that needs a more generalized solution) or just extract those units which the component depends on. dzlib is licensed under MPL.

dummzeuch
  • 10,975
  • 4
  • 51
  • 158