Sign in Join
Wiki copytemporary

Grids Reference Page: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Objective
22
33This text will try to show the user some aspects of the grids components in Lazarus. It is also intended to serve as a guide for users who have never used grids before (as experienced users generally only need a reference for new functionality). This text will therefore try to reach the following objectives:
44
551. To introduce the grids components to people with little or no previous Delphi experience.
662. To document the differences with respect to Delphi grids components.
773. To document the new functionality in Lazarus grids.
884. Create reference and examples for the components.
99
1010## Overview
1111
1212A grid is a component that provides a means for displaying data in tabular format. The most obvious characteristic of grids is that they are composed of cells forming rows and columns.
1313
1414The type of information that can be shown in a grid varies and mainly depends on what the user wants to show. Generally this information consists of text, colors, images or a combination of those three.
1515
1616Given the great variety of information that can be represented, a series of grids exist whose purpose is to facilitate the user in showing specific kinds of information. For instance, there is a grid designed to show text: the **StringGrid**. Documentation for that grid can be found [here](http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tstringgrid.html)
1717
1818## Inheritance Tree
1919
2020`                     `[[TCustomControl]]`           `\
2121`                             |                    `\
2222`                             |                    `\
2323`                        TCustomGrid               `\
2424`                             |                    `\
2525`               +-------------+------------+       `\
2626`               |                          |       `\
2727`         TCustomDrawGrid             TCustomDbGrid`\
2828`               |                          |       `\
2929`      +--------+--------+                 |       `\
3030`      |                 |              `[[TDBGrid]]`    `\
3131`  `[[TDrawGrid]]`      TCustomStringGrid                `\
3232`                        | `\
3333`               ---------+----------`\
3434`               |                  |    `\
3535`         `[[TStringGrid]]`      `[[TValueListEditor]]
3636
3737## A Starting Example
3838
3939As one of the objectives of this page is to help people with little or no previous Lazarus knowledge let's do a quick starting example of grids in action. Why not, let's make a traditional "hello world" example using the TStringGrid Component.
4040
41411. Create a new application.
4242 - From the main menu select: project-\>New Project
4343 - In the Create New Project dialog press the "Create Button"
4444 - A new empty form will be shown.
45452. Place a grid on the form
4646 - From the component palette select the "additional" tab
4747 - Click over the TStringGrid icon ![](/wiki-images/6/67/Tstringgrid.png)
4848 - Click over the form, near to the top left corner. A new empty grid appears.
49493. Place a button on the form
5050 - From the component palette select the "Standard" tab
5151 - Click over the TButton icon ![](/wiki-images/b/b2/Tbutton.png)
5252 - Click over a empty area of the form. A new button appears.
53534. Doubleclick the button from step 3, and write down the following code in the click button handler:
5454 - ``` Pascal
5555 StringGrid1.Cells[1,1] := 'Hi World!';
5656 ```
57575. Run the program by clicking the play icon ![](/wiki-images/8/85/Menu_run.png)
5858 - by pressing the Button1, the hello world text should appear in cell column 1, row 1. (note that left column and top row are 0)
5959
6060## Differences between Lazarus and Delphi grids
6161
6262The current grid components differ from Delphi grids in several ways. When first developed the Lazarus grids were created from scratch without any attempt to make them fully Delphi-compatible.
6363
6464At a later stage, compatibility with Delphi's grids became a desired objective and the Lazarus grids started to conform more closely to the Delphi grid interface. However even this was done without attempting to make every single Lazarus grid property or method match its Delphi counterpart. Also (because Lazarus grid internals are very different from Delphi grid internals) some Delphi functionality is not possible or needs to be emulated differently in a Lazarus grid from how it would be done in a Delphi grid. We have achieved greater Delphi compatibility as the Lazarus grids have evolved, and this is desirable.
6565
6666### Differences
6767
6868Known differences are listed below, in no special order.
6969
7070- Cell Editors
7171- Designtime Behaviour
7272- Cell Drawing has some differences, see customizing grid section.
7373
7474### New Functionality
7575
7676- Custom Columns
7777- Events
7878- Grid Editor
7979
8080### Ways in which you can make a Lazarus grid more Delphi-compatible
8181
8282You can make a Lazarus grid look like and behave like a corresponding Delphi. Listed below are property settings which will achieve this. These adjustments are based in a newly created grid. Entries tagged with \[Code\] need to be set in code, \[Design\] entries can be changed at design time.
8383
8484- \[Design\] TitleStyle := tsStandard;
8585- \[Design\] DefaultRowHeight := 24;
8686- \[Code\] EditorBorderStyle := bsNone; // this might work only on Windows.
8787- \[Code\] UseXORFeatures := true;
8888- \[Code\] AllowOutBoundEvents := False; // SVN revision 10992 or later
8989- \[Code\] FastEditing := False; // supported in dbgrid. StringGrid requires SVN revision 10992 or later
9090- \[Design\] AutoAdvance := aaNone;
9191
9292## Grids Reference
9393
9494### Information
9595
9696The starting point for reference about TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid and TStringGrid is the [unit Grids.pas reference](http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html)
9797
9898For TCustomDBGrid and TDBgrid it is the [unit DBGrids.pas reference](http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html)
9999
100100In general, any Delphi reference about the grids should help us to use Lazarus grids (but don't forget that there are several differences between Delphi and Lazarus grids that we have documented); with this in mind and as a temporary place for reference information, this place will be used to document things that don't work the same as in Delphi, as well as any new functionality.
101101
102102TODO: the rest of this section will disappear; its content will be moved to [unit Grids.pas reference](http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html)
103103
104104### TCustomGrid
105105
106106See the full [[doc:lcl/grids/tcustomgrid.html|TCustomGrid Reference]]
107107
108108#### property AllowOutboundEvents
109109
110110Protected in TCustomGrid, public in TCustomDrawGrid and descendants. Normally when user click on a point over empty space after cells (for example if grid has three rows but user clicks on an imaginary fourth row), the currently focused cell will move to the nearest cell to the point just clicked. We call this an outbound event. The default value is true as this has been grid's behaviour since begining. This property was added to simulate Delphi behaviour where outbound events are not available, so to enable Delphi compatibility set this property to false.
111111
112112#### property Columns
113113
114114Lazarus includes the property **columns** in TStringGrid and TDrawGrid grids. This property adds what we call custom columns. Custom columns are a collection of objects that hold properties that apply to whole columns, for example column titles (for a StringGrid it will override the value specified in the corresponding Cells\[ColumnIndex, RowTitle\] property), text alignment, background color, preferred editor, etc.
115115
116116Custom columns add extra properties or replace default property values on normal grid columns. Not only this, the **grid.ColCount** value may increase or decrease in order to account for the number of custom columns being attached to the grid. At this point, this means that **grid.ColCount** = **grid.FixedCols** + **grid.Columns.Count**.
117117
118118For example, if to a base grid with ColCount := 5 and FixedCols := 2 we:
119119
120120- Add 3 custom columns, the base grid will be exactly as before, 2 fixed cols and 3 normal cols.
121121- Add 1 custom column, the base grid will have ColCount := 3, that is 2 fixed cols and 1 normal cols.
122122- Add 4 custom columns, the base grid will have ColCount := 6, that is 2 fixed cols and 4 normal cols.
123123
124124From this we can conclude that:
125125
126126- Fixed column properties or count are not enhanced or modified respectively by custom columns.
127127- **grid.ColCount** is usually different from **grid.Columns.Count** (**grid.ColCount**=**grid.Columns.Count** only when **FixedCols**=0)
128128
129129At design time the user can access the **columns** property in the Object Inspector to bring up the columns editor. From there you can add, remove or modify custom columns. The editor shows a list of current custom columns; by selecting items in this list the object inspector gets filled with the properties available for each column. The list of custom columns is also available in the Object Inspector component tree view, where columns can be added, deleted or modified. They appear on a lower level under the container grid.
130130
131131At runtime, columns can be modified with code like this:
132132
133133``` pascal
134134 var
135135 c: TGridColumn;
136136 begin
137137 // add a custom column a grid
138138 c := Grid.Columns.Add;
139139 // modify
140140 c.title.caption := 'Price'; // Set columns caption
141141 c.align := taRightJustify; // Align column content to the right
142142 c.color := clMoneyGreen; // Change default color to clMoneyGreen
143143 c.Index := 0; // Make it the first column
144144 // access an existing column
145145 grid.columns[0].Width := 60; // Change column 0 width to 60 pixels
146146 // delete an existing column
147147 grid.columns.delete(0); // Delete column 0
148148 ....
149149 end;
150150```
151151
152152Additionally, when using custom columns, the grids do not allow direct modification of **grids.colcount**; adding or removing columns should be done using the **columns** property. The explanation is that there is an inconsistency on gradually removing custom columns using **ColCount**, when **ColCount** reaches **FixedCols**, the grid has no more custom columns. If we now increase **ColCount**, the new column will not be a custom column but a normal column.
153153
154154Currently there are no plans to make the grids use only custom columns.
155155
156156#### property AutoFillColumns
157157
158158Often the grids are wider than the horizontal space needed by the columns -- this leaves an unpleasant empty area at the right of the last column. The LCL grids provide a mechanism to expand the width of specified columns such that the empty space is filled automatically.
159159
160160For this purpose, **Columns** must be added either as described above, or by setting the `ColCount` property, and the grid property `AutoFillColumns` must be set to `true`. Each Column has a property `SizePriority`. When it has the value `0` the column width is taken from the `Width` property of the column. But when it has a non-zero value the column width is adjusted to the average available size remaining for all columns with a non-zero `SizePriority`.
161161
162162#### procedure SaveToFile(AFileName: String);
163163
164164Saves the grid to an XML file. The property `SaveOptions` determines what exactly is to be saved:
165165
166166``` Pascal
167167type
168168 TGridSaveOptions = (
169169 soDesign, // Save grid structure (col/row count and Options)
170170 soAttributes, // Save grid attributes (Font, Brush, TextStyle)
171171 soContent, // Save Grid Content (Text in StringGrid)
172172 soPosition // Save Grid cursor and selection position
173173 );
174174 TSaveOptions = set of TGridSaveOptions;
175175```
176176
177177#### procedure LoadFromFile(AFileName: String);
178178
179179Loads the XML file created by `SaveToFile`. Again, the `SaveOptions` define what exactly is to be loaded from the file.
180180
181181### TCustomDBGrid
182182
183183TCustomDBGrid is the base for TDBGrid.
184184
185185They do not expose Col and Row properties. To go to a certain column, use e.g. the SelectedIndex property.
186186
187187An interesting public method is AutoAdjustColumns.
188188
189189#### procedure AutoAdjustColumns
190190
191191This procedure sets the column width to the size of the widest text it finds. It can be used after loading a dataset/setting it Active.
192192
193193However, contrary to TCustomStringGrid.AutoAdjustColumns (see below), this will set your columns very wide unless you have the property dgAutoSizeColumns enabled.
194194
195195#### procedure InplaceEditor
196196
197197See example from bug 23103 - *and insert explanation of what it does/why it is needed. Validate input values? Change what is shown?*
198198
199199``` pascal
200200
201201procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
202202var
203203 S: String;
204204begin
205205 if (Key in [',','.']) then
206206 begin
207207 //unlike Delphi not all InPlaceEditors are editors for string type, so check!
208208 if (DBGrid1.InplaceEditor is TStringCellEditor) then
209209 begin
210210 S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
211211 if Pos(',',S) > 0 then
212212 Key := #0
213213 else
214214 Key := ',';
215215 end;
216216 end;
217217end;
218218```
219219
220220### TCustomStringGrid
221221
222222TCustomStringGrid serves as the base for TStringGrid. It can be used for derived TStringGrid components that want to hide published properties. See [[new intermediate grids]] for more information.
223223
224224The following properties or methods are public and are also available to TStringGrid.
225225
226226See the full [[doc:lcl/grids/tcustomstringgrid.html|TCustomStringGrid Reference]]
227227
228228#### procedure AutoSizeColumn(aCol: Integer);
229229
230230This procedure sets the column width to the size of the widest text it finds in all rows for the column aCol. Tip: see the goDblClickAutoSize option to allow columns to be automatically resized when doubleClicking the column border.
231231
232232#### procedure AutoAdjustColumns or AutoSizeColumns
233233
234234Automatically resizes all columns by adjusting them to fit in the longest text in each column. This is a quick method of applying AutoSizeColumn() for every column in the grid.
235235
236236#### procedure Clean; overload;
237237
238238Cleans all cells in the grid, fixed or not.
239239
240240#### procedure Clean(CleanOptions: TGridZoneSet); overload;
241241
242242Cleans all cells in the grid subject to the given CleanOptions. See [[doc:lcl/grids/tgridzoneset.html|TGridZoneSet]] for more information. Some examples:
243243
244244- Clean all cells: grid.Clean(\[\]); (the same as grid.clean)
245245- Clean all non fixed cells: grid.Clean(\[gzNormal\]);
246246- Clean all cells but don't touch grid column headers: Grid.Clean(\[gzNormal, gzFixedRows\]);
247247
248248#### procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload;
249249
250250does the same as Clean(CleanOptions:TGridZoneSet) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:
251251
252252- Clean column index 4 to 6 but don't touch grid column headers: many variations, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,\[\]); Grid.Clean(4,0,6,Grid,RowCount-1, \[gzNormal\]); etc.
253253
254254#### procedure Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;
255255
256256The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,\[\]); ==== procedure SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ==== Save grid content to a comma separated values format (CSV) file (added in Lazarus r32179).
257257
258258The AFilename argument specifies a file name where the content will be saved. If the file exists, the content will be overwritten. If it doesn't exist, the file will be created.
259259
260260ADelimiter (an optional argument) is used to supply a custom separator if required. By default a CSV format is produced (that is, ADelimiter:=',';) for a TAB separated file ADelimiter should be \#9.
261261
262262The WithHeader parameter is used to decide if a "row header" should be included or not. The row header is a list of field names at the beginning of the output file; its content comes from the last fixed row in the grid. There is an exception to this rule: if the grid has custom columns, the row header content comes from the custom column titles and not from fixed row cell content.
263263
264264If WithHeader is true and the grid does not include a fixed row or custom columns, the row header content will be taken from the first row in the grid.
265265
266266Normal CSV data output will start at the first non-fixed row in the grid.
267267
268268==== procedure LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ==== Loads grid content from a comma separated values format (CSV) file (added in Lazarus r32179).
269269
270270Columns will be added or deleted to or from the grid as needed according to the number of fields included in each line of the CSV file. Note that, in case of an empty (newly created) grid, columns will be added only if at least a single column exists already. Loading a CSV file will not modify the number of fixed rows that already existed in the grid.
271271
272272The *AFileName* argument specifies the name of the source file with the CSV content.
273273
274274*ADelimiter* optional parameter may be used to specify a different separator or delimiter. An example: for a tab-separated file, *ADelimiter* should be \#9. Another popular file format is semicolon-delimited file, where *ADelimiter* should be ;
275275
276276The *WithHeader* parameter is used to decide if the first row in the CSV file should be considered as the "header row" or not. If the grid has fixed rows and *WithHeader* is true, the column captions for the last fixed row will be taken from the header row. Note however that if the grid has custom columns, the header row will be used as source for the column titles and custom column titles are always shown in the grid's first fixed row or hidden if there are no fixed rows in the grid.
277277
278278If the *LoadFromCSVFile* procedure has difficulty loading your CSV file (e.g. quotes or spaces being incorrectly interpreted), you could manually load the grid using e.g. [[CsvDocument]]... and of course a patch for *LoadFromCSVFile* is always welcome.
279279
280280#### property Cols\[index: Integer\]: TStrings read GetCols write SetCols;
281281
282282Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1.
283283
284284##### Examples
285285
286286- Set Example: Set the content of the third column in the grid from a ListBox:
287287
288288``` pascal
289289Grid.Cols[2] := ListBox1.Items;
290290```
291291
292292- Get Example: Set the content of a Listbox from the grid's column index 4:
293293
294294``` pascal
295295procedure TForm1.FillListBox1;
296296var
297297 StrTempList: TStringList;
298298begin
299299 StrTempList := TStringList(Grid.Cols[4]);
300300 if StrTempList<>nil then begin
301301 ListBox1.Items.Assign(StrTempList);
302302 StrTempList.Free;
303303 end;
304304end;
305305```
306306
307307##### Notes.
308308
309309This property works differently in Lazarus and in Delphi when getting the data from the grid. In Lazarus a temporary TStringList object is created for retrieving the column content. It is the responsibility of the user to free this object after use.
310310
311311This means also that changes in the returned list will not affect the grids content or layout.
312312
313313See the Get Example.
314314
315315#### property Rows\[index: Integer\]: TStrings read GetRows write SetRows;
316316
317317Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1.
318318
319319##### Notes.
320320
321321This property works differently in Lazarus and in Delphi when getting the data from the grid. In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use.
322322
323323This means also that changes in the returned list will not affect the grid's content or layout.
324324
325325##### Examples
326326
327327- Set Example: Set the content of the third row in the grid from a ListBox:
328328
329329``` Pascal
330330Grid.Rows[2] := ListBox1.Items;
331331```
332332
333333- Get Example: Set the content of a Listbox from the grid's row index 4:
334334
335335``` pascal
336336procedure TForm1.FillListBox1;
337337var
338338 StrTempList: TStringList;
339339begin
340340 StrTempList := TStringList(Grid.Rows[4]);
341341 if StrTempList<>nil then begin
342342 ListBox1.Items.Assign(StrTempList);
343343 StrTempList.Free;
344344 end;
345345end;
346346```
347347
348348- An Example that doesn't work, and its Fix: Retrieved string list is read only
349349
350350``` pascal
351351// this will not work and will cause memory leak
352352// because returned StringList is not being freed
353353Grid.Rows[1].CommaText := '1,2,3,4,5';
354354Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g';
355355
356356// fixing the first case
357357Lst:=TStringList.Create;
358358Lst.CommaText := '1,2,3,4,5';
359359Grid.Rows[1] := Lst;
360360Lst.Free;
361361```
362362
363363#### property UseXORFeatures;
364364
365365Boolean property, default value: False;
366366
367367This property controls how the dotted focus rectangle appears in the grid. When True, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When False, the user can control the color of the dotted focus rectangle using the [[FocusColor property]]
368368
369369It also controls the look of the column/row resizing. When True, a line shows visually the size that the the column or row will have if the user ends the operation. When False, the column or row resizing takes effect just as the user drags the mouse.
370370
371371### TValueListEditor
372372
373373[[TValueListEditor]] is a control derived from TCustomStringGrid for editing Key-Value pairs.
374374
375375#### Property DisplayOptions
376376
377377Controls various aspects of the TValueListEditor's appearance.
378378
379379#### Property TitleCaptions
380380
381381Sets the values of the title captions (if doColumnTitles is in DisplayOptions). If DisplayOptions lacks the value doColumnTitles then default captions are used.
382382
383383#### Property Strings
384384
385385Provides access to the list of strings that hold the Key-Value pairs.\
386386Key-Value pairs must be in the form:\
387387'KeyName=Value'
388388
389389#### Property ItemProps
390390
391391You can use this property to control how the items in the "Value" columns can be edited.\
392392This is controlled by setting the ItemProp's EditStyle and ReadOnly properties.
393393
394394#### Property KeyOptions
395395
396396KeyOptions is a set of TKeyOptions controlling whether the user can modify the contents of the "Key" column.
397397
398398- KeyEdit: the user can edit the name of the Key
399399- KeyAdd: the user can add keys (by pressing Insert in the grid). KeyAdd requires KeyEdit.
400400- KeyDelete: the user can delete Key-Value pairs (by pressing Ctrl+Delete).
401401- KeyUnique: if set, then Keys must have unique names. Attempting to enter a dulpicate Key will raise an exception.
402402
403403#### Property DropDownRows
404404
405405If the cell editor is a picklist (ValueEdit1.ItemProps\['key1'\].EditStyle=esPickList) this property sets the DropDownCount of the displayed list. The default is 8.
406406
407407#### Function DeleteRow
408408
409409Deletes the Key-Value pair of the indexed row removing the row entirely.
410410
411411#### Function InsertRow
412412
413413Inserts a row in the grid and sets the Key-Value pair. Returns the index of the newly inserted row.
414414
415415#### Function IsEmptyRow
416416
417417Returns true if the indexed row's cells are empty (Keys\[aRow\]=*; Values\[aRow\]=*).
418418
419419#### Function FindRow
420420
421421Returns the row that has the specified key name.
422422
423423#### Function RestoreCurrentRow
424424
425425Undoes the editing in the current row (if the editor is still focused). Happens when the user presses the Escape key.
426426
427427#### Altered behaviour of some properties derived from TCustomStringGrid
428428
429429##### Property Options
430430
431431Due to the nature of TValueListEditor its Options property has certain restrictions
432432
433433- goColMoving is not allowed in Options (you cannot set it).
434434- goAutoAddRows can only be set if KeyAdd is in KeyOptions. Setting KeyAdd will automatically set goAutoAddRows.
435435- goAutoAddRowsSkipContentCheck is not allowed (for the time being, it causes a crash in TValueListeditor: needs fixing).
436436
437437##### Property FixedRows
438438
439439Can only be 1 (show column titles) or 0 (don't show column titles).
440440
441441##### Property ColCount
442442
443443Is always 2.
444444
445445##### Property Objects
446446
447447It is not recommended to use this property at all.\
448448When some (internal) code manipulates the Strings property, changes are Objects will be lost.
449449
450450#### General comments on the use of TValueListEditor
451451
452452When manipulating the contents of the ValueListEditor (the grid), you can manipulate the underlying Strings property directly.\
453-Be aware however, that is you do this, you <b>must</b> hide the editor, otherwise the Strings may get out of sync with the contents of the grid you will see on the screen.\
454-\
453+Be aware however, that is you do this, you must hide the editor, otherwise the Strings may get out of sync with the contents of the grid you will see on the screen.\
454+
455455If you want to insert or delete rows etc. then the preferred way is to use the public methods from TValueListEditor: DeleteRow(), InsertRow(), MoveRow(), ExchangeRow() or the various Sort() methods.\
456-\
456+
457457Trying to use ancestor's methods to manipulate rows or columns (e.g. Columns.Add) might result in a crash.
458458
459459#### Saving and loading the contenst of a TValueListEditor
460460
461461##### Using the Strings property
462462
463463If you just want to save and load the content of the TValueListEditor (e.g. the Key/Value pairs, not the layout) you can use Strings.SaveToFile and Strings.LoadFromFile.\
464464In case of LoadFromFile the `RowCount` property will be autmatically adjusted.\
465465No sanity checks are performed upon LoadFromFile (so lines that do not represent a Key/Value pair will end up as Keys without Value).
466466
467467##### Using TValueList.SaveToFile and TValueList.LoadFromFile
468468
469469When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.\
470470SaveToFile will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.\
471-\
471+
472472In contrast to it's ancestors TValueList.LoadFromFile performs sanity checks on the file it tries to read. In particular RowCount must be specified and there cannot be a cell with RowIndex \> RowCount or ColumnIndex other than 0 or 1.\
473473If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.\
474-\
474+
475475TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions \<= 2.1 r62044.
476476
477477##### Using SaveToCSVFile and LoadFromCSV methods
478478
479-These methods should <b>not</b> be used at the moment, since for a TValueListEditor they are flawed.
479+These methods should not be used at the moment, since for a TValueListEditor they are flawed.
480480
481481## Working with grids
482482
483483### Customizing grids
484484
485485Grid are components derived from the [TCustomControl](http://lazarus-ccr.sourceforge.net/docs/lcl/controls/tcustomcontrol.html) class, and don't have a native widget associated with them which means that grids are not restricted by the look of current interface theme. This can be both an advantage and a disadvantage: usually programmers want to create a uniform-look application. The good news is that Lazarus grids are flexible enough to get something from both worlds; programmers can easily make grids look similar to other native controls, or they can customize the grid to the finest detail so they can obtain almost the same look in any platform or widget interface (that is, with the exception of scrollbars, because their look is still determined by the current theme).
486486
487487### Properties and Events for customizing grids
488488
489489Some properties can affect the way the grid looks by acting when the cell is about to be painted in PrepareCanvas/OnPrepareCanvas by changing default canvas properties like brush color or font. Following is a list of such properties:
490490
491491- **AlternateColor.** With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.
492492- **Color.** This sets the primary color used to draw non fixed cells background.
493493- **FixedColor.** This is the color used to draw fixed cells background.
494494- **Flat.** This eliminates the 3d look of fixed cells.\
495495- **TitleFont.** Font used to draw the text in fixed cells.\
496496- **TitleStyle.** This property changes the 3D look of fixed cells, there are 3 settings:
497497 - *tsLazarus.* This is the default look
498498 - *tsNative.* This tries to set a look that is conforms with the current widgetset theme.
499499 - *tsStandard.* This style is a more contrasted look, like Delphi grids.
500500- **AltColorStartNormal.** Boolean. If true: alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color. If false: default color is set to the first row as if there were no fixed rows.
501501- **BorderColor.** This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;
502502- **EditorBorderStyle.** If set to bsNone under Windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.\
503503- **FocusColor.** The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.
504504- **FocusRectVisible.** Turns on/off the drawing of focused cell.
505505- **GridLineColor.** Color of grid lines in non fixed area.
506506- **GridLineStyle.** Pen style used to draw lines in non fixed area, possible choices are: *psSolid*, *psDash*, *psDot*, *psDashDot*, *psDashDotDot*, *psinsideFrame*, *psPattern*,*psClear*. Default is *psSolid*.
507507- **SelectedColor.** Color used to draw cell background on selected cells.
508508- **UseXORFeatures.** If set, focus rect is drawn using XOR mode so it should make visible the focus rect in combination with any cell color ackground. It also affects the moving columns look.
509509- **DefaultDrawing.** Boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell that is being painted. If the user writes an OnDrawCell event handler, a set DefaultDrawing also paints the cell background. If the user draws the cell himself, it is better to turn off this property so painting is not duplicated. In a StringGrid, a set DefaultDrawing draws the text in each cell.
510510- **AutoAdvance.** Where the cell cursor will go when pressing Enter, or after editing.
511511- **TabAdvance.** Where the cell cursor will go when pressing Tab or Shift-Tab.
512512- **ExtendedColSizing.** If true user can resize columns not just at the headers but along the columns height.
513513
514514Other properties that also affect the grids look.
515515
516516**Options**.
517517
518518
519519Options property is a set with some elements to enable diverse functionality but some are related directly with grid's look. This options can be set at designtime or runtime.
520520
521521- **goFixedVertLine, goFixedHorzLine** it draws a vertical or horizontal line respectively delimiting cells or columns in fixed area, active by default.
522522- **goVertLine, goHorzLine** the same as previous, but for normal browseable area. A grid can be made to simulate a listbox by unsetting both of this elements.
523523- **goDrawFocusSelected** if this element is enabled a selection background is painted in focused cell in addition to focused dotted rectangle (note this doesn't work yet when goRowSelect option is set, in such case row is always painted as if goDrawFocusSelected is set)
524524- **goRowSelect** select the full row instead of individual cells
525525- **goFixedRowNumbering** if set, grid will do numbering of rows in first fixed column
526526- **goHeaderHotTracking** if set, the grid will try to show a different look when the mouse cursor is overing any fixed cell. In order for this to work, desired cell zone needs to be enabled with property HeaderHotZones. Try combining this option with property TitleStyle:=tsNative to get themed hot tracking look.
527527- **goHeaderPushedLook** if set, this element enables a pushed look when clicking any fixed cell. The zone of "pushable" cells is enabled using HeaderPusedZones property.
528528
529529(write more)
530530
531531### Description of grid's drawing process
532532
533533Like other custom controls, the grid is drawn using the paint method. In general terms the grid is drawn by painting all rows, and each row by painting its individual cells.
534534
535535The process is as follow:
536536
537537- First the visible cells area is determined: each row is tested to see if it intersects the canvas clipping region; if it's ok, then the visible area is painted by drawing columns of each row.
538538- The column and row values are used to identify the cell that is about to be painted and again each column is tested for intersection with the clippling region; if everything is ok, some additional properties like the cell's rectangular extent and visual state are passed as arguments to the DrawCell method.
539539- As the drawing process is running, the visual state of each cell is adjusted according to grid options and position within grid. The visual state is retained in a varible of type TGridDrawState which is a set with following elements:
540540 - *gdSelected* The cell will have a selected look.
541541 - *gdFocused* The cell will have a focused look.
542542 - *gdFixed* Cell have to be painted with fixed cell look.
543543 - *gdHot* the mouse is over this cell, so paint it with hot tracking look
544544 - *gdPushed* the cell is being clicked, paint it with pushed look
545545- **DrawCell.** The DrawCell method is virtual and may be overriden in descendant grids to do custom drawing. The information passed to DrawCell helps to identify the particular cell is being painted, the physical area ocuppied in screen and its visible status. See DrawCell reference for details. For each cell the following occurs:
546546- **PrepareCanvas.** In this method, if the DefaultDrawing property is set, the grid canvas is setup with default properties for brush and font based on current visual state. For several design and runtime properties, the text alignment is set to match programmer selection in custom columns if they exists. If DefaultDrawing is false, brush color is set to clWindow and Font color to clWindowText, the text alignment is set with grids defaultTextStyle property value.
547547- **OnPrepareCanvas.** If the programmer wrote an event handler for OnPrepareCanvas event, it is called at this point. This event can be used for doing simple customization like changing cell's background color, font's properties like color, fontface and style, Text layout like different combinations of left, center, top, bottom, right alignment, etc. Any change made to the canvas in this event would be lost, because the next cell drawing will reset canvas again to a default state. So it's safe doing changes only for particular cell or cells and forget about it for the rest. Using this event sometimes helps to avoid using the OnDrawCell grid event, where users would be forced to duplicate the grid's drawing code. Todo: samples of what can be made and what to leave for OnDrawCell?...
548548- **OnDrawCell.** Next if no handler for OnDrawCell event was specified, the grid calls the DefaultDrawCell method which simply paints the cell background using the current canvas brush color and style. If the OnDrawCell handler exists, the grid first paints the cell background but only if DefaultDrawing property was set, then it calls OnDrawCell event to do custom cell painting. Usually programmers want to do custom drawing only for particular cells, but standard drawing for others; in this case, they can restrict custom operation to certain cell or cells by looking into ACol, ARow and AState arguments, and for other cells simply call DefaultDrawCell method and let the grid to take care of it.
549549- **Text.** At this point (only for TStringGrid) if DefaultDrawing property is true, the cell's text content is painted.
550550- **Grid lines**. The last step for each cell is to paint the grid lines: if grid options goVertLine, goHorzLine, goFixedVertLine and goFixedHorzLine are specified the cell grid is drawn at this point. Grids with only rows or only cols can be obtained by changing these options. If the programmer elected to have a "themed" look it is done at this point also (see property TitleStyle).
551551- **FocusRect.** When all columns of current row have been painted it is time to draw the focus rectangle for the current selected cell or for the whole row if goRowSelect option is set.
552552
553553#### Differences with Delphi
554554
555555- In Lazarus TCustomGrid.DrawCell method is not abstract and its default implementation does basic cell background filling.
556556- In Delphi, the cell's text is drawn before entering the OnDrawCell event (see [bug report \#9619](http://www.freepascal.org/mantis/view.php?id=9619)).
557557
558558### Grid's cell selection
559559
560560The location of a grid's current (focused) cell (or row) can be changed using keyboard, mouse or through code. In order to change cell focus successfully to another position, we must test the target position to see if it is allowed to receive cell focus. When using keyboard, the property AutoAdvance performs part of the process by finding what should be the next focused cell. When using mouse clicks or moving by code, focus will not move from the current cell unless the target cell is permitted to receive focus.
561561
562562The grid calls function SelectCell to see if a cell is focusable: if this function returns true, then the target cell identified with arguments aCol and aRow is focusable (the current implementation of TCustomGrid simply returns true). TCustomDrawGrid and hence TDrawGrid and TStringGrid override this method to check first if cell is any wider than 0; normally you don't want a 0 width cell selected so a cell with these characteristics is skipped automatically in the process of finding a suitable cell. The other thing the overridden method SelectCell does is to call the user configurable event OnSelectCell: this event receives the cell coordinates as arguments and always returns a default result value of true.
563563
564564Once a cell is known to be focusable and we are sure a movement will take place, first the method BeforeMoveSelection is called; this in turns triggers the OnBeforeSelection event. This method's arguments are the coordinates for the new focused cell. At this point any visible editor is hidden too. The "before" word means that selection is not yet changed and current focused coordinates can be accessed with grid.Col and grid.Row properties.
565565
566566After that, the internal focused cell coordinates are changed and then MoveSelection method is called; this method's purpose is to trigger the OnSelection event if set (this is a notification that the focused cell has, by this time, already changed and cell coordinates are now available through grid.row and grid.col properties).
567567
568568Note that is not good to use OnSelectCell event to detect cell focus changes, as this event will be triggered several times even for the same cell in the process of finding a suitable cell. Is better to use OnBeforeSelection or OnSelection events for this purpose.
569569
570570#### Differences with Delphi
571571
572572- SelectCell and OnSelectCell behaviour is probably different - can't really comment on the differences. In Lazarus they are used in functionality like AutoAdvance which as far as I know doesn't exist in Delphi.
573573
574574### When built-in properties are not enough: derived grids
575575
576576Derived grids usually have to override the following methods:\
577577DrawAllRows: Draws all visible rows.\
578578DrawRow: Draws all cells in a row.\
579579DrawRow draws all cells in the row by first checking if cell is within clipping region, and only draws the cell if it is.\
580580DrawCell:\
581581DrawCellGrid:\
582582DrawCellText:\
583583DrawFocusRect:\
584584(write me).\
585585
586586### What happens in the `TCustomGrid.Paint` method?
587587
588588The following list is to show the internal order of method calls for painting a `TCustomGrid` (or descendant). Every item in the lists represents method calls during the paint operation. This ought to help finding the right point to change behaviour when it comes to descending from `TCustomGrid`.
589589
590590- **`DrawEdges`**: Draws the grid's outer border.
591591- **`DrawAllRows`** (virtual): Draws all rows in the visible part of the grid. This is the only method called in the painting process that is declared *`virtual`*.
592592 - **`DrawRow`** (virtual): Is called for each row inside the current viewport.
593593 - **`DrawCell`** (virtual): Is first called for each 'normal' (i.e. not fixed) cell in the row.
594594 - **`PrepareCanvas`** (virtual): Sets the canvas's drawing styles according to the current cell's visual properties.
595595 - **`DrawFillRect`**: Draws the cell's background with the styles set in `PrepareCanvas`.
596596 - **`DrawCellGrid`** (virtual): Draws the cell's border lines.
597597 - **`DrawFocusRect`** (virtual): In `TCustomGrid` this method does nothing. In descendent grids this method is used to draw the focus rectangle inside the active cell.
598598 - **`DrawCell`** (virtual): (see above) Is called for each fixed cell in the row's visible viewport.
599599- **`DrawColRowMoving`**: only active while moving a column or row. This method draws the line that indicates the row's/column's new position.
600600- **`DrawBorder`**: If needed (`Flat=TRUE` and `BorderStyle=bsSingle`), this draws an inner border line.
601601
602602#### Additional methods to draw in `TCustomGrid`
603603
604604These methods are declared and (partly) implemented in `TCustomGrid`, but they are not called directly from here. They are used by descendant classes to draw the cell's content.
605605
606606- **`DrawCellText`** (virtual): Writes/draws the text that is passes as parameter into the cell. The text is formatted by using the styles that are active in `Canvas.TextStyle` (see also `PrepareCanvas`).
607607- **`DrawThemedCell`** (virtual): Is only used for fixed cells and if `TitleStyle=tsNative`. It draws the cell's background using `ThemeServices`.
608608- **`DrawColumnText`** (virtual): Is only used for column header cells.
609609 - **`DrawColumnTitleImage`**: If the grid as an assigned `TitleImageList` and `Columns.Title[x].Title.ImageIndex` contains a valid value, that image is drawn into the cell.
610610 - **`DrawCellText`** : (see above)
611611- **`DrawTextInCell`** (virtual): Is used for 'normal' (i.e. not fixed) cells.In `TCustomGrid` this method does nothing. In descendent grids this method is used to draw the cells content. In `TStringGrid` theis method calls `DrawCellText` (see above).
612612
613613#### Drawing methods introduced by `TCustomDrawGrid`
614614
615615As you can see the base class `TCustomGrid` does not draw any content into the cells. This is done in **`TCustomDrawGrid`**. **`TCustomDrawGrid`** overrides the method `DrawCell` in the following way:
616616
617617- **`DrawCell`** (override):
618618 - **`PrepareCanvas`**: calls the inherited method from **`TCustomGrid`**.
619619 - **`DefaultDrawCell`** (virtual): This method is only called if the event handler for `OnDrawCell` is NOT assigned.
620620 - **`DrawFillRect / DrawThemedCell`** (von `TCustomGrid`): Draws the cell's background. If `TitleStyle=tsNative`, the background of fixed cells is drawn using `DrawThemedCell`.
621621 - **`DrawCellAutonumbering`** (virtual): If `goFixedAutonumbering in Options`, row numbers are drawn into the first fixed column using `TCustomGrid.DrawCellText`.
622622 - **`DrawColumnText`** (from `TCustomGrid`): Is only called for column header cells (see *Additional methods to draw in `TCustomGrid`*).
623623 - **`DrawTextInCell`** (from `TCustomGrid`): Is only called for 'normal' (i.e. not fixed) cells (see *Additional methods to draw in `TCustomGrid`*).
624624 - **`DrawCellGrid`** (virtual): Draws the cell's borders
625625
626626### Save and Retrieve Grid Content
627627
628628The **SaveToFile** and **LoadFromFile** methods allows a grid to save and retrieve it's layout and data to/from a XML format file. TStringGrid, as inherited from TCustomStringGrid, has also the ability to "export" and "import" its content to/from a Comma Separated Values format file, best known as CSV files. This is described in the **SaveToCSVFile** and **LoadFromCSVFile** methods reference (TODO: make links).
629629
630630The kind of information that can be saved and then retrieved when using **SaveToFile** and **LoadFromFile** is determined by the SaveOptions property (of type TSaveOptions) which is a set of options described as follows:
631631
632632`soDesign:     Save & load ColCount,RowCount,FixedCols,FixedRows,`\
633633`              ColWidths, RowHeights and Options (TCustomGrid)`\
634634`soPosition:   Save & load Scroll position, Row, Col and Selection (TCustomGrid)`\
635635`soAttributes: Save & load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)`\
636636`soContent:    Save & load Text (TCustomStringGrid)`\
637637`soAll:        set of all options (soAll:=[soDesign,soPosition,soAttributes,soContent];)`
638638
639639Not all options apply to all kind of grids, for example, soContent do not apply to TCustomGrid derived grids like TDrawGrid as this kind of grid does not have the concept of "content". TStringGrid is a special kind of grid that "knows" how to handle strings and can use the soContent option if specified.
640640
641641The soAttributes option is not used in Lazarus standard grids, is there for derived grids.
642642
643643When using **LoadFromFile** the grid also uses the SaveOptions property in order to know what kind of information needs to retrieve from the file, so is perfectly possible to specify SaveOptions:=\[soDesign,soContent\] on saving and only SaveOptions:=\[soContent\] on loading.
644644
645645For a TStringGrid the default SaveOptions value is \[soContent\], for other kind of grids, SaveOptions is the empty set.
646646
647647**Note:** One common issue when saving & retrieving grid data occurs when the user specify the SaveOptions property before **SaveToFile** but not before **LoadFromFile**. When using **LoadFromFile** some time after **SaveToFile** have been used, the SaveOptions property is properly set, but if **LoadFromFile** is executed on next program run, the SaveOptions property might not have been properly setup, for this reason is recommended to always specify SaveOptions property just before **LoadFromFile**, or doing it globally at program start like in the following example:
648648
649649------------------------------------------------------------------------
650650
651651**Example:**
652652
6536531. First, go to menu "File -\> New -\> Application";
6546542. Put an empty TStringGrid on the form;
6556553. Put a TButton and TOpenDialog on the form;
6566564. Add the event OnCreate for the form;
6576575. Add the event OnClick for the button.
658658
659659``` pascal
660660unit Unit1;
661661
662662{$mode objfpc}{$H+}
663663
664664interface
665665
666666uses
667667 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,
668668 Buttons, StdCtrls, XMLCfg;
669669
670670type
671671
672672 { TForm1 }
673673 TForm1 = class(TForm)
674674 StringGrid1: TStringGrid;
675675 Button1: TButton;
676676 OpenDialog1: TOpenDialog;
677677 procedure Button1Click(Sender: TObject);
678678 procedure Form1Create(Sender: TObject);
679679 private
680680 { private declarations }
681681 public
682682 { public declarations }
683683 end;
684684
685685var
686686 Form1: TForm1;
687687
688688implementation
689689
690690{ TForm1 }
691691
692692procedure TForm1.Form1Create(Sender: TObject);
693693begin
694694 //sets the SaveOptions at creation time of the form
695695 stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
696696end;
697697
698698procedure TForm1.Button1Click(Sender: TObject);
699699begin
700700 //Ask if thew Execute method of the OpenDialog was launched
701701 //when this occurs, the user selects an XML file to Load
702702 //wich name was stored in the FileName prop.
703703
704704 if opendialog1.Execute then
705705 begin
706706 //Clear the grid
707707 StringGrid1.Clear;
708708 //Load the XML
709709 StringGrid1.LoadFromFile(OpenDialog1.FileName);
710710 //Refresh the Grid
711711 StringGrid1.Refresh;
712712 end;
713713end;
714714
715715initialization
716716 {$I unit1.lrs}
717717
718718end.
719719```
720720
721721------------------------------------------------------------------------
722722
723723**The sample xml file:** (Copy the text below into a txt file. Don't forget put the xml header :-))
724724
725725``` xml
726726<?xml version="1.0"?>
727727<CONFIG>
728728 <grid version="3">
729729 <saveoptions create="True" position="True" content="True"/>
730730 <design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20">
731731 <options>
732732 <goFixedVertLine value="True"/>
733733 <goFixedHorzLine value="True"/>
734734 <goVertLine value="True"/>
735735 <goHorzLine value="True"/>
736736 <goRangeSelect value="True"/>
737737 <goDrawFocusSelected value="False"/>
738738 <goRowSizing value="False"/>
739739 <goColSizing value="False"/>
740740 <goRowMoving value="False"/>
741741 <goColMoving value="False"/>
742742 <goEditing value="False"/>
743743 <goTabs value="False"/>
744744 <goRowSelect value="False"/>
745745 <goAlwaysShowEditor value="False"/>
746746 <goThumbTracking value="False"/>
747747 <goColSpanning value="False"/>
748748 <goRelaxedRowSelect value="False"/>
749749 <goDblClickAutoSize value="False"/>
750750 <goSmoothScroll value="True"/>
751751 </options>
752752 </design>
753753 <position topleftcol="1" topleftrow="1" col="1" row="1">
754754 <selection left="1" top="1" right="1" bottom="1"/>
755755 </position>
756756 <content>
757757 <cells cellcount="10">
758758 <cell1 column="0" row="0" text="Title Col1"/>
759759 <cell2 column="0" row="1" text="value(1.1)"/>
760760 <cell3 column="0" row="2" text="value(2.1)"/>
761761 <cell4 column="0" row="3" text="value(3.1)"/>
762762 <cell5 column="0" row="4" text="value(4.1)"/>
763763 <cell6 column="1" row="0" text="Title Col2"/>
764764 <cell7 column="1" row="1" text="value(1.2)"/>
765765 <cell8 column="1" row="2" text="value(2.2)"/>
766766 <cell9 column="1" row="3" text="value(3.2)"/>
767767 <cell10 column="1" row="4" text="value(4.2)"/>
768768 </cells>
769769 </content>
770770 </grid>
771771</CONFIG>
772772```
773773
774774### Grid Cell Editors
775775
776776The grid uses cell editors to change the content of cells.
777777
778778For a specialized grid like TStringGrid, the editor is the usual single line text editor control, but sometimes it's desirable to have other means to enter information. For example:
779779
7807801. show the open file dialog to find the location of a file so the user doesn't have to type the full path manually
7817812. if the text in the cell represents a date, popup a calendar so we can choose a specific date easily.
782782
783783Sometimes the information the user should enter in a cell is restricted to a limited list of words; in this case typing the information directly might introduce errors and validation routines might need to be implemented. We can avoid this by using a cell editor that presents the user with a list containing only the legal values.
784784
785785This is also the case for generic grids like TDrawGrid where the user needs some kind of structure to hold the data that will be shown in the grid. In this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid.
786786
787787#### Builtin cell editors
788788
789789The grids.pas unit already includes some of the most used cell editors ready for use in grids. It is also possible to create new cell editors (custom cell editors) if the built-in editors are not appropiate for a specific task.
790790
791791The built-in cell editors are Button, Edit, and Picklist.
792792
793793#### Using cell editors
794794
795795Users can specify what editor will be used for a cell using one of two methods.
796796
7977971. **Using a custom column and selecting the ButtonStyle property of the column**. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
7987982. **Using OnSelectEditor grid event**. Here the user specifies in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle(), that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn. This method takes precedence over the first one using custom columns. A custom cell editor can be specified here (see chapter 5 of [this document](http://www.freepascal.org/~michael/articles/grids/grids.pdf) by Michaël Van Canneyt which explains how to correctly implement a custom editor). This event is also the place to setup the editor with values specific to the cell, row or column (e.g. a custom popupmenu for the cell editor).
799799
800800Setting the *ButtonStyle* property only works if a column is created with the *StringGrid1.Columns.Add;* statement. Using expression like *StringGrid1.ColCount:=X;* will cause an exception. Setting the ButtonStyle property can be done with a similar code:
801801
802802``` pascal
803803if ColCB< StringGrid1.Columns.Count
804804 then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;
805805```
806806
807807#### Description of editor styles
808808
809809The following is a description of the editor styles. They are enumerated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used to remain compatible with Delphi's DBGrid.
810810
811811- **cbsAuto**
812812
813813
814814This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content depends on several factors. For TCustomGrids it uses a TStringCellEditor class derived from TCustomMaskEdit. This editor is specialized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns, if the programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that has a field of type boolean, it behaves as if cbsCheckBoxColumn editor style was specified. This is the recommended value for Custom Cell Editors. TODO: related OnEditingDone.
815815
816816- **cbsEllipsis**
817817
818818
819819This editor style is the most generic one. When used, a button appears in the editing cell and programmers could use the OnEditButtonClick grid event to detect when the user has pressed the button and take any action programmed for such a cell. For example a programmer could use this editor style to pop up a calendar dialog to allow the user easily to select a specific date. Other possibilities could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc.
820820
821821
822822OnEditButtonClick is just a notification, to find out in which cell a button has been clicked by taking a look at the grid.Row and grid.Col properties.
823823
824824
825825A DBGrid has specific properties to retrieve the active column or field and because this event occurs in the active record, it could update the information in the active field.
826826
827827
828828This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
829829
830830- **cbsNone**
831831
832832
833833This editor style instructs the grid not to use any editor for a specific cell or column; it behaves then, as if the grid were readonly for such a cell or column.
834834
835835- **cbsPickList**
836836
837837
838838Used to present the user with a list of values that can be entered. This editor style is implemented using TPickListCellEditor, a component derived from TCustomComboBox. The list of values that are shown is filled in one of two ways depending on the method used to select the editor style.
839839
8408401. When using custom columns, programmers can enter a list of values using the column's PickList property. \[FOR BEGINNERS: TODO: exact procedure to edit the list\]
8418412. In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList). See here for an example
842842
843843The value in a TStringGrid grid will automatically reflect the value selected. If necessary the programmer could detect the moment the value is selected by writing an event handler for the grid's OnPickListSelect event, so additional steps can be taken (for example, to process the new value). TODO: related OnEditingDone.
844844
845845- **cbsCheckboxColumn**
846846
847847
848848It can be useful when the data content associated with the column is restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of data in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the data of a column by using a checkbox representation that the user can toggle by using a mouse click or pressing the SPACE key (if the column, containing the checkbox is selected and if the StringGrid is editable).
849849
850850Getting or setting the value of the checkbox in a cell is done the following way:
851851
852852``` pascal
853853StringGrid1.Cell[x,y]:='Z';
854854```
855855
856856
857857where Z shall be replaced by *0* for Unchecked, *1* for Checked and an empty string for Grayed. Note that any value (string) different from *0* and *1* will be displayed as a grayed checkbox.
858858
859859
860860If a columns' ButtonStyle property is set to cbsAuto and DBGrid detects that the field associated with the column is a boolean field, then the grid uses this editor style automatically. This automatic selection can be disabled or enabled using DBGrid's OptionsExtra property; setting dgeCheckboxColumn element to false disables this feature.
861861
862862
863863The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
864864
865865
866866At any moment, the field value can be in one to three states: Unchecked, Checked or Grayed. Internally these states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.
867867
868868
869869This editor style doesn't use real TCheckbox components to handle user interaction: the visual representation is given by three built-in bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap; the handler of this event gets the state of the checkbox in the parameter CheckedState of type TDBGridCheckboxState and a bitmap parameter that the programmer could use to specify custom bitmaps.
870870
871871- **cbsButtonColumn**
872872
873873
874874This editor style is used to show a button on every cell on column. Like in the case of cbsCheckboxColumn this editor do not use real buttons, the appearance is defined by current widgetset theme.
875875
876876
877877The user knows what particular button was pressed by handling the grid's OnEditButtonClick and checking grid's col and row. Note that in this particular case, grid's col and row do not identify the currently selected cell, but the cell of the clicked button. Once the OnEditButtonClick event has been handled, and if the user has not modified the grid's col or row in this handler, the grid automatically resets the col and row to reflect the currently selected cell. While handling the OnEditButtonClick the current grid selection is available in grid.Selection which is a TRect property, left and right represent Column indexes, Top and Bottom are row indexes.
878878
879879
880880The button's caption is the corresponding cell string.
881881
882882### Editing grids
883883
884884The effect of editing is different depending on what kind of grid is used, for example, TStringGrid stores the edited text internally and TDBGrid affects records in a dataset. TDrawGrid doesn't know what to do with the edited text, if the programmer do not take control on it, it's simply discarded.\
885885For TDrawGrid (although this should work for all grid classes) in order to access the edited text, the programmer can use the event **OnSetEditText** which is triggered every time the user modify something in the editor, the *aCol* and *aRow* parameters of this event identify the cell being edited, the parameter *value* holds the text, this is the recommended method. Another way of access the edited text is taking it directly from the editor once the editing process has ended by using the event **OnEditingDone**. This could be accomplished by accessing the internal editor used for editing, in this case, the default "String Cell Editor". For this, there are two methods: The first is by using the event **OnSelectEditor** where the parameter *Editor* is the instance which will be used to edit a cell, you have to store this instance in a variable, for example TheEditor (of type *TStringCellEditor*), for later use in **OnEditingDone**. The second alternative is using the grid's method **EditorByStyle**, this method accepts an "editor style" as parameter and it returns the instace of the editor corresponding to that style. In our case knowing that the style cbsAuto returns the default cell editor, in the **OnEditingDone** event handler we can use directly TheEditor := Grid.EditorByStyle(cbsAuto). You can then get the text with *TheEditor.Text*. If you use this method note that "with great power, comes great responsibility".
886886
887887#### Options and properties that affect editing
888888
889889The editing behavior is controlled by a number of properties and options, virtually any adjustment or options that affects editing requires an editable grid or such adjustment might be ignored.\
890890At start the editable state of the grids is different, for TDrawGrid and TStringGrid editing is disabled, for TDbGrid is enabled by default.\
891891The **Options** property has several items that deal with editing, they are described below:\
892892***goEditing, dgEditing (in DbGrid)**. This option changes the editable state of the grid, can be changed at runtime because it is checked when editing is about to be started.
893893
894894- **goAlwaysShowEditor**. Normally the editor is hidden and it becomes visible only when it's needed. With this option, the editor will be visible all the time, if grid is not editable, this option is ignored and editor is always hidden.
895895
896896### Cell hints
897897
898898In addition to the standard hint of most controls, the descendants of TCustomGrid can display a special hint for the cell over which the mouse hovers. This feature is controlled by the grid options **goCellHints** and **goTruncCellHints** (or **dgCellHints** and **gdTruncCellHints** for DBGrid, respectively):
899899
900900- **goCellHints** evaluates the event **OnGetCellHint** to retrieve the displayed text for the cell specified by the passed parameters. In case of the TDrawGrid and the TStringGrid, column and row indexes are given, while in case of the TDBGrid the cell text can be extracted from the Field assigned to the specified column object.
901901- **goTruncCellHints** displays a hint only if the text does not fit into a cell and is truncated; the hint text is the untruncated cell text. In Lazarus 1.9+, this text is passed to the event **OnGetCellHint** for further refinement (e.g. display the entire text of a memo in the hint). In case of the truncated cell texts, it is recommended to also activate the option **goCellEllipsis** (or **dgCellEllipsis** for DBGrid) which adds a '...' to the truncated cell text.
902902
903903If the grid has a value in its standard property **Hint** then the property **HintPriority** can be used to control how all these hint texts can be combined into a single popup window:
904904
905905- **chpAll** puts the standard Hint into the first line, the hint determined by the OnGetCellHint event into the second line, and the hint for the truncated cells into the third line
906906- **chpAllNoDefault** displays only the cell hints given by the OnGetCellHint event and by the truncated cells.
907907- **chpTruncOnly** displays only hints for the truncated cells; this is the default settings.
908908
909909In addition, the standard property **ShowHint** of the grid must be set to true in order to show cell hints.
910910
911911## Grids Howto and Examples
912912
913913### Focusing a cell
914914
915915Focusing a cell in TStringGrid is easy. Note that counting starts from zero not 1. So to focus the cell in the 10th row and the 9th column, do:
916916
917917``` Pascal
918918StringGrid1.Row := 9;
919919StringGrid1.Col := 8;
920920```
921921
922922### Example: How to set a custom cell editor
923923
924924See lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (from laz 1.2)
925925
926926### Example: How to add a button editor
927927
928928``` pascal
929929// Conditionally show button editor in column index 1 or 2 if
930930// cell in column index 1 is empty
931931procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
932932 var Editor: TWinControl);
933933begin
934934 if (aCol = 1) or (aCol = 2) then
935935 if StringGrid1.Cells[1,aRow] = '' then
936936 begin
937937 Editor := StringGrid1.EditorByStyle(cbsEllipsis);
938938 end;
939939 end;
940940
941941// Triggering Action ...
942942procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
943943begin
944944 if StringGrid1.Col = 1 then Showmessage('column 1 editor clicked');
945945 if StringGrid1.Col = 2 then Showmessage('column 2 editor clicked');
946946end;
947947```
948948
949949### Example: Working with Picklist, How to make it read only and How to fill it at run time
950950
951951Using grid's event OnSelectEditor one can customize how PickList editor (see cbsPickList button style) behaves. In next example the picklist editor from column 1 is modified so on odd rows the user can enter values by typing, on even rows the values are limited to the ones contained in its list. Also, this example show how to fill the list with different values depending on the row being processed.
952952
953953``` pascal
954954procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
955955 var Editor: TWinControl);
956956begin
957957 if aCol=1 then begin
958958 if (Editor is TCustomComboBox) then
959959 with Editor as TCustomComboBox do begin
960960 if (aRow mod 2=0) then
961961 Style := csDropDown
962962 else
963963 Style := csDropDownList;
964964 case aRow of
965965 1:
966966 Items.CommaText := 'ONE,TWO,THREE,FOUR';
967967 2:
968968 Items.CommaText := 'A,B,C,D,E';
969969 3:
970970 Items.CommaText := 'MX,ES,NL,UK';
971971 4:
972972 Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
973973 end;
974974 end;
975975 end;
976976end;
977977```
978978
979979### Aligning text in StringGrids
980980
981981This code shows how to use different text alignments in columns 2 and 3.
982982
983983``` pascal
984984procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
985985 aState: TGridDrawState);
986986var
987987 MyTextStyle: TTextStyle;
988988begin
989989 if (aCol=2) or (aCol=3) then
990990 begin
991991 MyTextStyle := StringGrid1.Canvas.TextStyle;
992992 if aCol=2 then
993993 MyTextStyle.Alignment := taRightJustify
994994 else
995995 if aCol=3 then
996996 MyTextStyle.Alignment := taCenter;
997997 StringGrid1.Canvas.TextStyle := MyTextStyle;
998998 end;
999999end;
10001000```
10011001
10021002### Multilines in Grids
10031003
10041004This sample shows how to make multilined text in cell \[3,2\]. It works the same for DBGrid where OnPrepareCanvas have parameters for dealing with TColumns and from there with TFields.
10051005
10061006``` pascal
10071007procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
10081008 aState: TGridDrawState);
10091009var
10101010 MyTextStyle: TTextStyle;
10111011begin
10121012 if (aRow=2) or (aCol=3) then
10131013 begin
10141014 MyTextStyle := StringGrid1.Canvas.TextStyle;
10151015 MyTextStyle.SingleLine := false;
10161016 StringGrid1.Canvas.TextStyle := MyTextStyle;
10171017 end;
10181018end;
10191019```
10201020
10211021### Validating Entered Values
10221022
10231023Lazarus version 0.9.29 introduces the StringGrid OnValidateEntry event of type TValidateEntryEvent which has the following declaration:
10241024
10251025``` pascal
10261026type
10271027 TValidateEntryEvent = procedure(sender: TObject; aCol, aRow: Integer; const OldValue: string; var NewValue: string) of object;
10281028```
10291029
10301030`aCol,aRow are the cell coordinates of cell being validated.`\
10311031`OldValue is the value that was in cells[aCol,aRow] before editing started.`\
10321032`NewValue is the value that will be finally inserted in cells[aCol,aRow].`
10331033
10341034Because of the way StringGrid works by setting the cell value while user is editing (see grid's OnSetEditText event and SetEditText method), when the OnValidateEntry event triggers, the cell already contains the entered value (valid or not); using the event arguments OldValue and NewValue the cell value can be validated and changed if needed.
10351035
10361036Usually validation occurs when the user has moved to another cell. If validation then fails, it is desirable to keep the cell editor visible/focused so the entered value can be corrected by user. To let the grid know that validation has failed, an exception needs to be raised. The grid will handle the exception to Application.HandleException and any movement is cancelled.
10371037
10381038For example, suppose that cell\[1,1\] should hold only values 'A' or 'B', validation could be made with:
10391039
10401040``` pascal
10411041procedure TForm1.GridValidateEntry(sender: TObject; aCol,
10421042 aRow: Integer; const OldValue: string; var NewValue: String);
10431043begin
10441044 if (aCol=1) and (aRow=1) then begin
10451045 if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
10461046 // set a new valid value so user can just press RETURN to continue for example.
10471047 NewValue := 'A';
10481048 // another option is reverting to previous cell value (which is assumed to be valid)
10491049 // NewValue := OldValue;
10501050 // Use EAbort rather than another Exception type to avoid spurious error
10511051 // dialog boxes and backtraces
10521052 raise EAbort.Create('Only A or B are allowed here');
10531053 end else begin
10541054 // if no exception is raised, it is assumed the value is valid, yet if necessary
10551055 // the final value can still be changed by filling NewValue with a different value
10561056
10571057 // computer knows better :)
10581058 if grid.Cells[1,1]='A' then
10591059 NewValue := 'B'
10601060 else
10611061 NewValue := 'A';
10621062 end;
10631063 end;
10641064end;
10651065```
10661066
10671067### Sorting Columns or Rows
10681068
10691069Property ColumnClickSorts allows grid to be sorted automatically when user clicks a column header. Clicking the same column many times switches the sort order. Default column sort images are shown to indicate which column was clicked.
10701070
10711071In code you can use SortColRow() method.
10721072
10731073The first parameter, IsColumn, is a boolean value which indicates:
10741074
10751075- True if a column is to be sorted
10761076- False if a row is to be sorted.
10771077
10781078The second parameter, index, is an integer value:
10791079
10801080- Index Indicates Column or Row to be sorted.
10811081
10821082The last two parameters are optional, defines subrange of rows or columns to be sorted.
10831083
10841084- FromIndex (From column or row)
10851085- ToIndex (To column or row)
10861086
10871087If the last two parameters are not specified, the whole column or row is sorted.
10881088
10891089Sorting uses QuickSort algorithm, it could be changed if a descendant grid overrides the sort() method and calls doCompareCells for cell compare.
10901090
10911091By default it sorts cell content as strings either in ascending or descending order which is selectable with property SortOrder. By default it uses ascending order.
10921092
10931093``` pascal
10941094// Sort column 3 in ascending order
10951095grid.SortColRow(true, 3);
10961096
10971097// Sort column 3 in descending order, skip fixed rows a top
10981098grid.SortOrder := soDescending; // or soAscending
10991099grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);
11001100```
11011101
11021102For custom sorting of numbers, dates, states, etc: StringGrid has the OnCompareCells event which users can handle for example this way:
11031103
11041104``` pascal
11051105procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
11061106begin
11071107 // Result will be either <0, =0, or >0 for normal order.
11081108 result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
11091109 // For inverse order, just negate the result (eg. based on grid's SortOrder).
11101110 if StringGrid1.SortOrder = soDescending then
11111111 result := -result;
11121112end;
11131113```
11141114
11151115You can use OnCompareCells also when automatic column sorting is enabled through ColumnClickSorts property.
11161116
11171117### Override automatic sort (ColumnClickSorts set to False). Avoid sort of some columns. Show sort arrows in column header
11181118
11191119(ColumnClickSorts set to False will NOT show sort arrows in column header, so we have to set the arrows.)
11201120
11211121``` pascal
11221122procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
11231123 Index: Integer);
11241124begin
11251125 if isColumn then with StringGrid1 do
11261126 begin
11271127 col:=Index; // Set column to sort
11281128 if Tag = 0 then Tag :=-1 // Suppose data in column is Ascending
11291129 else Tag := Tag * -1; // Using StringGrid1.Tag to hold sortorder
11301130 // First click of header will sort descending
11311131
11321132 // Activate CompareCells (ativate the sort)
11331133 case Index of
11341134 0: col:=-1; // Do not sort column 0 and do not show sort arrow
11351135 1: SortColRow(True, Index); // The sort will take place in CompareCells as soon as statement is executed
11361136 2: SortColRow(True, Index);
11371137 3: SortColRow(True, Index);
11381138 4: SortColRow(True, Index);
11391139 5: col:=-1; // Do not sort column 5 and do not show sort arrow
11401140 end;
11411141 end;
11421142end;
11431143
11441144// Compare returns one of the following values:
11451145// if A<B return: -1 ( Ascending )
11461146// if A=B return: 0
11471147// if A>B return: 1 ( Descending)
11481148
11491149// How to sort: Text, Integer, Float, DateTime:
11501150
11511151procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
11521152 BRow: Integer; var Result: integer);
11531153
11541154 function CompareTextAsText: Integer; // Text
11551155 begin
11561156 CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
11571157 end;
11581158
11591159 function CompareTextAsInteger: Integer;
11601160 var i1,i2:Integer;
11611161 begin
11621162 if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and // Integer
11631163 TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
11641164 Result := CompareValue(i1, i2)
11651165 else Exit(0);
11661166 end;
11671167
11681168 function CompareTextAsFloat: Integer; // Float
11691169 var f1,f2:Extended;
11701170 begin
11711171 if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
11721172 TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
11731173 Result := CompareValue(f1, f2)
11741174 else Exit(0);
11751175 end;
11761176
11771177 function CompareTextAsDateTime: Integer; // DateTime
11781178 var
11791179 D1, D2 : TDateTime;
11801180 begin
11811181 if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
11821182 begin
11831183 if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
11841184 if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
11851185 end
11861186 else Exit(0);
11871187
11881188 if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
11891189 TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
11901190 Result := CompareDateTime(D1, D2)
11911191 else Exit(0);
11921192 end;
11931193
11941194begin // Don't do anything else here, sort might be done incorrect
11951195 With StringGrid1 do
11961196 begin
11971197 case ACol of
11981198 0 : ; // Do not sort column 0
11991199 1 : Result := CompareTextAsText; // Text
12001200 2 : Result := CompareTextAsInteger; // Integer
12011201 3 : Result := CompareTextAsFloat; // Float
12021202 4 : Result := CompareTextAsDateTime; // DateTime
12031203 end; {Case}
12041204 end;
12051205 if StringGrid1.Tag = -1 then Result := -Result; // Switch the sort order
12061206end;
12071207
12081208// DrawCell does the trick, previous arrow images are removed:
12091209procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
12101210 aRect: TRect; aState: TGridDrawState);
12111211
12121212 procedure DrawArrow(ImgIndex:Integer); // Need Up/Down arrows in a ImageList
12131213 begin
12141214 with StringGrid1 do
12151215 TitleImageList.Draw(Canvas, // TitleImageList of StringGrid
12161216 ARect.Right - TitleImageList.Width,
12171217 (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2, // Position of arrow, may be adjusted.
12181218 ImgIndex );
12191219 end;
12201220begin
12211221 if Col > 0 then // In this demo no arrows in col 0
12221222 if (ARow = 0) and // Suppose Row 0 as TitleRow
12231223 Assigned(TitleImageList) then // Need Up/Down arrows in a ImageList
12241224 case Columns.Items[aCol].Tag of // Use Tag of Column as index of Ascending/Descending arrow
12251225 -1: DrawArrow(1); // Replace with your Index of down arrow
12261226 0: DrawArrow(-1); // Remove previous image
12271227 1: DrawArrow(2); // Replace with your Index of Up Arrow
12281228 end;
12291229end;
12301230```
12311231
12321232### Highlighting the selected cell column and row
12331233
12341234In Lazarus revision 40276 an option (**gdRowHighlight**) has been added, it works similar to **goRowSelect** but it uses a lighter color for selection and the focus rect is only selected cell and not in the whole row. This works fine for rows, but how about columns?.
12351235
12361236This section presents a way to highlight columns, rows or both (an example that highlights only column and row headers can be found in lazarus/examples/gridexamples/spreadsheet, this howto is really an extension of that example). This uses two grid events: **OnBeforeSelection** and **OnPrepareCanvas**, drawing is not necessary.
12371237
12381238The event **OnBeforeSelection** is triggered when the selection is about the change, in this event we can know what cell is currently selected and what cell will be selected next. We use this information to invalidate the whole row or column of both the old and the new cells. When the next paint cycle starts, the grid will be instructed to paint the cells that belong to the invalidated areas. One of the first steps for painting is calling the **OnPrepareCanvas** event (if it exists) to setup default canvas properties. We use this event to set up the highlighted row or column:
12391239
12401240``` pascal
12411241procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
12421242begin
12431243 // we can decide here if we want highlight columns, rows or both
12441244 // in this example we highlight both
12451245 if Grid.Col<>aCol then
12461246 begin
12471247 // a change on current column is detected
12481248 grid.InvalidateCol(aCol); // invalidate the new selected cell column
12491249 grid.InvalidateCol(grid.Col); // invalidate the current (it will be the 'old') selected column
12501250 end;
12511251 if Grid.Row<>aRow then
12521252 begin
12531253 grid.InvalidateRow(aRow); // invalidate the new selected cell row
12541254 grid.InvalidateRow(grid.Row); // invalidate the current (it will be the 'old') selected row
12551255 end;
12561256end;
12571257
12581258procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
12591259 aState: TGridDrawState);
12601260begin
12611261 if gdFixed in aState then
12621262 begin
12631263 if (aCol=grid.Col) or (aRow=grid.Row) then
12641264 grid.Canvas.Brush.Color := clInactiveCaption; // this would highlight also column or row headers
12651265 end else
12661266 if gdFocused in aState then begin
12671267 // we leave alone the current selected/focused cell
12681268 end else
12691269 if (aCol=Grid.Col) or (aRow=grid.Row) then
12701270 grid.Canvas.Brush.Color := clSkyBlue; // highlight rows and columns with clSkyBlue color
12711271end;
12721272```
12731273
12741274### Avoiding the empty space at the right of the last column
12751275
12761276The grid must be in column mode to achieve this. When the grid property `AutoFillColumns` is `true` the empty space is divided by all columns which have a non-zero `SizePriority`. Therefore, you should follow these steps to fill up all horizontal space of a grid:
12771277
12781278- Set `AutoFillColumns` to `true`.
12791279- Using the Columns editor, add as many columns as you need.
12801280- Select each column in the object tree above the object inspector and set its `SizePriority` to `0`.
12811281- Now decide while column is widened such that all empty space is used up. Set the `SizePriority` of this column to `1`.
12821282
12831283### How to scroll a Grid in "realtime" (moving the scrollbar via mouse)
12841284
12851285If you scroll a Grid by moving the scrollbar with the mouse then the Grid per default is not moved "in realtime". Only after you release the mouse, the Grid "jumps" to its new position. You can enable a scrolling in "realtime" by adding Option `goThumbTracking`.
12861286
12871287Example:
12881288
12891289``` pascal
12901290 StringGrid1.Options:=StringGrid1.Options + [goThumbTracking];
12911291```
12921292
12931293Unfortunately this Option is neither documented in `<lazarus-installdir>\lcl\grids.pas` (ver 2.0.6) nor in <https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tgridoptions.html>
12941294
12951295### How to resize a row based on cells content
12961296
12971297Lacak in bug report [\#38473](https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/38473) contributed the following code:
12981298
12991299``` pascal
13001300
13011301type
13021302{ TStringGridHelper }
13031303 TStringGridHelper = class helper for TStringGrid
13041304 protected
13051305 procedure AutoSizeRow(aRow: integer);
13061306 end;
13071307
13081308{ TStringGridHelper }
13091309procedure TStringGridHelper.AutoSizeRow(aRow: integer);
13101310var
13111311 aCanvas: TCanvas;
13121312 aCol, maxRowHeight:integer;
13131313 aText: String;
13141314 textRect: TRect;
13151315begin
13161316 aCanvas := GetWorkingCanvas(Canvas);
13171317 maxRowHeight := DefaultRowHeight;
13181318 for aCol:=0 to ColCount-1 do begin
13191319 aText := Cells[aCol,aRow];
13201320 textRect := Rect(0, 0, ColWidths[aCol], MaxInt);
13211321 DrawText(aCanvas.Handle, PChar(aText), Length(aText), textRect, DT_CALCRECT or DT_WORDBREAK);
13221322 if maxRowHeight < textRect.Height then
13231323 maxRowHeight := textRect.Height
13241324 end;
13251325 if aCanvas<>Canvas then FreeWorkingCanvas(aCanvas);
13261326 RowHeights[aRow] := maxRowHeight+2;
13271327end;
13281328```
13291329
13301330Add that somewhere in your source code and then you can resize a row as if the AutoSizeRow procedure is part of the stringgrid, example:
13311331
13321332 // Resize the row index 2
13331333 grid.AutoSizeRow(2);
13341334
13351335## DBGrids Howto and Examples
13361336
13371337### Example: How to set a memo editor for dbgrids
13381338
13391339You can, of course, use another control instead of a TMemo. Adjust to taste. Adapted from [1](http://forum.lazarus.freepascal.org/index.php?topic=3640.0) (simplified to use the SelectedFieldRect property, see [2](http://forum.lazarus.freepascal.org/index.php/topic,8713.msg43288.html#msg43288))
13401340
13411341- Place a memo control (or whatever control you want) on your form, set whatever properties you want and set visible to false. This will be used when editing a grid cell. We'll use GridCellMemo in this example.
13421342- In the OnSelectEditor event put the following code - adapt if you don't want to edit logical column 3, physical column 4:
13431343
13441344``` pascal
13451345 if (Column.DesignIndex = 3) then
13461346 begin
13471347 GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
13481348 GridCellMemo.Text:=Column.Field.AsString;
13491349 Editor := GridCellMemo;
13501350 end;
13511351```
13521352
13531353- Suppose your datasource is called Datasource1 and DBGrid is called ResultsGrid. Then, in the OnEditingDone event for the GridCellMemo put the following:
13541354
13551355(The original forum post used the OnChange event, which would fire each time the content is changed. Using OnEditingDone only fires after the user is finished with his edits.)
13561356
13571357``` pascal
13581358 if not(Datasource1.State in [dsEdit, dsInsert]) then
13591359 Datasource1.Edit;
13601360
13611361 Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
13621362```
13631363
13641364**Beware** Using a normal control as a custom editor has many drawbacks: it won't resize or reposition itself when resizing the columns or scrolling through the grid, you have to take care yourself of modifying the data in the grid (as explained above), it doesn't work properly with DbGrids and interferes with normal grid navigation using the keyboard. The grid interacts with the editor using some special grid messages, so it's better to subclass a control in order to manage those messages, as explained in chapter five of [this document](http://www.freepascal.org/~michael/articles/grids/grids.pdf).
13651365
13661366**Beware** If you have more than one grid in the same form use a different custom editor for each one: if you use the same control bad things will happen since both grids will try to use it at once.
13671367
13681368### Example: Avoid displaying text fields as "(Memo)" for DBGrids
13691369
1370-<span style="color: red;">now (08/2023) exist the dbgrid option dgDisplayMemoText in object inspector - option</span>
1370+now (08/2023) exist the dbgrid option dgDisplayMemoText in object inspector - option
13711371
13721372When you use SQL statements like "Select * from A" you might see "(Memo)" instead of your content in a grid cell. There are many ways to fix this, but maybe the easiest is to change your statement "Select * from A" to "Select Cast(Column as TEXT) as Column from A". This is a common problem when you use a DBGrid component.
13731373
13741374``` pascal
13751375 var
13761376 sql : UTF8String;
13771377 Queryu : TSQLQuery;
13781378.....
13791379 sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
13801380 Queryu.SQL.Text:=sql;
13811381```
13821382
13831383
13841384for SQLITE3, to avoid cast function usage, just give len for fields in your sqlite database :\
13851385::- when you import from csv or create table : create table mytable (myfield TEXT (10))\
13861386::- or using sqlstudio or other to give len to fields
13871387
13881388### Sorting columns or rows in DBGrid with sort arrows in column header
13891389
13901390Here is an example that will sort a **DBgrid** using the **OnTitleClick** event and a **TSQLQuery** and indexes. This should also work for any compatible data set such as **TbufDataset**. The function uses the **column.tag** property to store the sort state for each column you click on, so when you go back to one you have already sorted it will pick the correct sort arrow to display.
13911391
13921392**Prerequisites:**
13931393
13941394- You will need an imagelist to store the up/down sort arrows. Assign your imagelist to the dbgrid's **TitleImageList** property.
13951395- Ensure the **[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]** is large enough to hold the new indexes you will be creating. For this example I had it set to 100.
13961396- You will also need a private var to store the last column used to sort, for this example I have called it **FLastColumn**.
13971397
13981398For this example the **TSQLQuery** used is called OpenQuery.
13991399
14001400> **Note:** As of March 21st 2013, **TSQLQuery** has no way to clear indexes, but as a work around you can set **unidirectional** to true, then set it to false and this will clear the indexes.
14011401
14021402In order to reuse the **TSQLQuery** component with another SQL statement, the indexes must be cleared after sorting or an exception will be raised when you open the **TSQLQuery** with a different SQL statement.
14031403
14041404``` pascal
14051405FLastColumn: TColumn; //store last grid column we sorted on
14061406
14071407procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
14081408const
14091409 ImageArrowUp=0; //should match image in imagelist
14101410 ImageArrowDown=1; //should match image in imagelist
14111411var
14121412 ASC_IndexName, DESC_IndexName:string;
14131413 procedure UpdateIndexes;
14141414 begin
14151415 // Ensure index defs are up to date
14161416 OpenQuery.IndexDefs.Updated:=false; {<<<--This line is critical. IndexDefs.Update will not
14171417 update if already true, which will happen on the first column sorted.}
14181418 Openquery.IndexDefs.Update;
14191419 end;
14201420begin
14211421 ASC_IndexName:='ASC_'+Column.FieldName;
14221422 DESC_IndexName:='DESC_'+Column.FieldName;
14231423 // indexes can't sort binary types such as ftMemo, ftBLOB
14241424 if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
14251425 exit;
14261426 // check if an ascending index already exists for this column.
14271427 // if not, create one
14281428 if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
14291429 begin
14301430 OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
14311431 UpdateIndexes; //ensure index defs are up to date
14321432 end;
14331433 // Check if a descending index already exists for this column
14341434 // if not, create one
14351435 if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
14361436 begin
14371437 OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
14381438 UpdateIndexes; //ensure index defs are up to date
14391439 end;
14401440
14411441 // Use the column tag to toggle ASC/DESC
14421442 column.tag := not column.tag;
14431443 if boolean(column.tag) then
14441444 begin
14451445 Column.Title.ImageIndex:=ImageArrowUp;
14461446 Openquery.IndexName:=ASC_IndexName;
14471447 end
14481448 else
14491449 begin
14501450 Column.Title.ImageIndex:=ImageArrowDown;
14511451 OpenQuery.IndexName:=DESC_IndexName;
14521452 end;
14531453 // Remove the sort arrow from the previous column we sorted
14541454 if (FLastColumn <> nil) and (FlastColumn <> Column) then
14551455 FLastColumn.Title.ImageIndex:=-1;
14561456 FLastColumn:=column;
14571457end;
14581458```
14591459
14601460### Selecting Records in a DBGrid using checkboxes
14611461
14621462The objective is to be able to select arbitrary records in a dbgrid using checkboxes, the grid has the ability to show checkboxes automatically when it detects there are boolean fields, for other field types the user can manually choose the cbsCheckboxColumn ButtonStyle for the column. For this kind of columns the user just click the checkbox and the field content is modified accordingly.
14631463
14641464But what happen if there is no such available field in our dataset? or we don't want the grid enter edit state when checking the checkbox?. Adding a fieldless column with ButtonStyle=cbsCheckboxColumn will show all checkboxes grayed and disabled because there is no field linked to this column and so nothing to modify. As we want to handle the checkbox state ourselves we need to store the state somewhere for each record. For this we can use the class TBookmarklist (defined in dbgrids.pas unit) where property CurrentRowSelected can tell if the current record is selected or not. By using dbgrid events OnCellClick and OnUserCheckboxState we can track the checkbox state.
14651465
14661466Note this technique needs Lazarus r31148 or later which implements event OnUserCheckboxState.
14671467
14681468``` pascal
14691469...
14701470uses ..., dbgrids, stdctrls, ...
14711471
14721472type
14731473
14741474 { TForm1 }
14751475
14761476 TForm1 = class(TForm)
14771477 ...
14781478 procedure DBGrid1CellClick(Column: TColumn);
14791479 procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
14801480 procedure FormCreate(Sender: TObject);
14811481 procedure FormDestroy(Sender: TObject);
14821482 ...
14831483 private
14841484 RecList: TBookmarklist;
14851485 ...
14861486 end;
14871487
14881488procedure TForm1.DBGrid1CellClick(Column: TColumn);
14891489begin
14901490 if Column.Index=1 then
14911491 RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
14921492end;
14931493
14941494procedure TForm1.FormCreate(Sender: TObject);
14951495begin
14961496 RecList := TBookmarkList.Create(DbGrid1);
14971497end;
14981498
14991499procedure TForm1.FormDestroy(Sender: TObject);
15001500begin
15011501 RecList.Free;
15021502end;
15031503
15041504procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
15051505begin
15061506 if RecList.CurrentRowSelected then
15071507 AState := cbChecked
15081508 else
15091509 AState := cbUnchecked;
15101510end;
15111511```
15121512
15131513### Example: How to export a DBGrid to a text file
15141514
15151515There are several ways to create a text file from the content of a DBGrid, normally people will tell you that instead of thinking of a DBGrid as a data container one should look a the linked dataset instead, but what happen when the text representation you want must match the grids appearance of the columns visibility, width or order? one solution is to use the LazReport suite that has a component specifically for producing a report from a DbGrid, this report can then be exported to several other formats including a text format. Here another simple solution is presented which can also be used as the basis for other text format exporters, for example, a simple html exporter.
15161516
15171517``` pascal
15181518// aGrid: The grid to be exported
15191519// lpp: Lines per page. Not including the text header
15201520// pageBreak: insert a page break that some printers could use for starting a new page
15211521// require: uses LazUTF8
15221522function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
15231523const
15241524 PX_PER_CHAR = 7;
15251525
15261526var
15271527 Ds: TDataset;
15281528 book: TBookMark;
15291529 recCount: Integer;
15301530 line: string;
15311531 column: TColumn;
15321532
15331533 function WidthToChar(aWidth: Integer): integer;
15341534 begin
15351535 result := trunc(aWidth/PX_PER_CHAR);
15361536 end;
15371537
15381538 procedure AddNext(theText: string; alignment: TAlignment);
15391539 var
15401540 width: Integer;
15411541 begin
15421542 if (line<>'') and (line<>#12) then
15431543 line := line + ' ';
15441544
15451545 width := WidthToChar(Column.Width);
15461546
15471547 case Alignment of
15481548 taRightJustify: line := line + UTF8PadLeft(theText, width);
15491549 taCenter: line := line + UTF8PadCenter(theText, width);
15501550 else line := line + UTF8PadRight(theText, width);
15511551 end;
15521552 end;
15531553
15541554 procedure CollectHeader;
15551555 begin
15561556 AddNext(Column.Title.Caption, Column.Title.Alignment);
15571557 end;
15581558
15591559 procedure CollectField;
15601560 var
15611561 field: TField;
15621562 begin
15631563 field := Column.Field;
15641564
15651565 if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
15661566 AddNext(field.AsString, Column.Alignment)
15671567 else if Field.DataType<>ftBlob then
15681568 AddNext(field.DisplayText, Column.Alignment)
15691569 else
15701570 AddNext('(blob)', Column.Alignment);
15711571 end;
15721572
15731573 procedure LoopColumns(printingRecord: boolean);
15741574 var
15751575 c: TCollectionItem;
15761576 begin
15771577 if (not printingRecord) and pageBreak and (result.count>0) then
15781578 line := #12
15791579 else
15801580 line := '';
15811581 for c in aGrid.Columns do begin
15821582 Column := TColumn(c);
15831583 if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
15841584 if printingRecord then CollectField
15851585 else CollectHeader;
15861586 end;
15871587 end;
15881588 result.add(line);
15891589 end;
15901590
15911591begin
15921592 result := TStringList.create;
15931593 Ds := aGrid.DataSource.DataSet;
15941594 Ds.DisableControls;
15951595 book := Ds.GetBookmark;
15961596 try
15971597 Ds.First;
15981598 recCount := 0;
15991599 while not DS.EOF do begin
16001600 if RecCount mod lpp = 0 then
16011601 LoopColumns(false);
16021602 LoopColumns(true);
16031603 inc(recCount);
16041604 Ds.Next;
16051605 end;
16061606 finally
16071607 Ds.GotoBookmark(book);
16081608 Ds.FreeBookmark(book);
16091609 Ds.EnableControls;
16101610 end;
16111611end;
16121612```
16131613
16141614To use it one simply does:
16151615
16161616``` pascal
16171617var
16181618 L: TStringList;
16191619begin
16201620 L := GridTextExporter(grid, 80, true);
16211621 L.SaveToFile('grid.txt');
16221622 L.Free;
16231623end;
16241624```
16251625
16261626---
16271627*From the Free Pascal wiki, page "Grids Reference Page", as it stood on 2023-12-06 after a change by Wp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history