Sign in Join
Wiki copytemporary

Grids Reference Page: what changed

Revision 3 to 4. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
1-## Contents
2-
3- * 1 Objective
4- * 2 Overview
5- * 3 Inheritance Tree
6- * 4 A Starting Example
7- * 5 Differences between Lazarus and Delphi grids
8- * 5.1 Differences
9- * 5.2 New Functionality
10- * 5.3 Ways in which you can make a Lazarus grid more Delphi-compatible
11- * 6 Grids Reference
12- * 6.1 Information
13- * 6.2 TCustomGrid
14- * 6.2.1 property AllowOutboundEvents
15- * 6.2.2 property Columns
16- * 6.2.3 property AutoFillColumns
17- * 6.2.4 procedure SaveToFile(AFileName: String);
18- * 6.2.5 procedure LoadFromFile(AFileName: String);
19- * 6.3 TCustomDBGrid
20- * 6.3.1 procedure AutoAdjustColumns
21- * 6.3.2 procedure InplaceEditor
22- * 6.4 TCustomStringGrid
23- * 6.4.1 procedure AutoSizeColumn(aCol: Integer);
24- * 6.4.2 procedure AutoAdjustColumns or AutoSizeColumns
25- * 6.4.3 procedure Clean; overload;
26- * 6.4.4 procedure Clean(CleanOptions: TGridZoneSet); overload;
27- * 6.4.5 procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload;
28- * 6.4.6 procedure Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;
29- * 6.4.7 procedure SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);
30- * 6.4.8 procedure LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);
31- * 6.4.9 property Cols[index: Integer]: TStrings read GetCols write SetCols;
32- * 6.4.9.1 Examples
33- * 6.4.9.2 Notes.
34- * 6.4.10 property Rows[index: Integer]: TStrings read GetRows write SetRows;
35- * 6.4.10.1 Notes.
36- * 6.4.10.2 Examples
37- * 6.4.11 property UseXORFeatures;
38- * 6.5 TValueListEditor
39- * 6.5.1 Property DisplayOptions
40- * 6.5.2 Property TitleCaptions
41- * 6.5.3 Property Strings
42- * 6.5.4 Property ItemProps
43- * 6.5.5 Property KeyOptions
44- * 6.5.6 Property DropDownRows
45- * 6.5.7 Function DeleteRow
46- * 6.5.8 Function InsertRow
47- * 6.5.9 Function IsEmptyRow
48- * 6.5.10 Function FindRow
49- * 6.5.11 Function RestoreCurrentRow
50- * 6.5.12 Altered behaviour of some properties derived from TCustomStringGrid
51- * 6.5.12.1 Property Options
52- * 6.5.12.2 Property FixedRows
53- * 6.5.12.3 Property ColCount
54- * 6.5.12.4 Property Objects
55- * 6.5.13 General comments on the use of TValueListEditor
56- * 6.5.14 Saving and loading the contenst of a TValueListEditor
57- * 6.5.14.1 Using the Strings property
58- * 6.5.14.2 Using TValueList.SaveToFile and TValueList.LoadFromFile
59- * 6.5.14.3 Using SaveToCSVFile and LoadFromCSV methods
60- * 7 Working with grids
61- * 7.1 Customizing grids
62- * 7.2 Properties and Events for customizing grids
63- * 7.3 Description of grid's drawing process
64- * 7.3.1 Differences with Delphi
65- * 7.4 Grid's cell selection
66- * 7.4.1 Differences with Delphi
67- * 7.5 When built-in properties are not enough: derived grids
68- * 7.6 What happens in the TCustomGrid.Paint method?
69- * 7.6.1 Additional methods to draw in TCustomGrid
70- * 7.6.2 Drawing methods introduced by TCustomDrawGrid
71- * 7.7 Save and Retrieve Grid Content
72- * 7.8 Grid Cell Editors
73- * 7.8.1 Builtin cell editors
74- * 7.8.2 Using cell editors
75- * 7.8.3 Description of editor styles
76- * 7.9 Editing grids
77- * 7.9.1 Options and properties that affect editing
78- * 7.10 Cell hints
79- * 8 Grids Howto and Examples
80- * 8.1 Focusing a cell
81- * 8.2 Example: How to set a custom cell editor
82- * 8.3 Example: How to add a button editor
83- * 8.4 Example: Working with Picklist, How to make it read only and How to fill it at run time
84- * 8.5 Aligning text in StringGrids
85- * 8.6 Multilines in Grids
86- * 8.7 Validating Entered Values
87- * 8.8 Sorting Columns or Rows
88- * 8.9 Override automatic sort (ColumnClickSorts set to False). Avoid sort of some columns. Show sort arrows in column header
89- * 8.10 Highlighting the selected cell column and row
90- * 8.11 Avoiding the empty space at the right of the last column
91- * 8.12 How to scroll a Grid in "realtime" (moving the scrollbar via mouse)
92- * 8.13 How to resize a row based on cells content
93- * 9 DBGrids Howto and Examples
94- * 9.1 Example: How to set a memo editor for dbgrids
95- * 9.2 Example: Avoid displaying text fields as "(Memo)" for DBGrids
96- * 9.3 Sorting columns or rows in DBGrid with sort arrows in column header
97- * 9.4 Selecting Records in a DBGrid using checkboxes
98- * 9.5 Example: How to export a DBGrid to a text file
99-
1001## Objective
1012
1023This 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:
1034
1045 1. To introduce the grids components to people with little or no previous Delphi experience.
1056 2. To document the differences with respect to Delphi grids components.
1067 3. To document the new functionality in Lazarus grids.
1078 4. Create reference and examples for the components.
1089
10910## Overview
11011
11112A 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.
11213
11314The 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.
11415
11516Given 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>)
11617
11718## Inheritance Tree
11819```
119-[TCustomControl](</index.php?title=TCustomControl&action=edit&redlink=1> "TCustomControl \(page does not exist\)")
20+[[TCustomControl]]
12021 |
12122 |
12223 TCustomGrid
12324 |
12425 +-------------+------------+
12526 | |
12627 TCustomDrawGrid TCustomDbGrid
12728 | |
12829 +--------+--------+ |
12930 | | [[TDBGrid]]
13031 [[TDrawGrid]] TCustomStringGrid
13132 |
13233 ---------+----------
13334 | |
13435 [[TStringGrid]] [[TValueListEditor]]
13536```
13637
13738## A Starting Example
13839
13940As 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.
14041
14142 1. Create a new application.
14243 * From the main menu select: project->New Project
14344 * In the Create New Project dialog press the "Create Button"
14445 * A new empty form will be shown.
14546 2. Place a grid on the form
14647 * From the component palette select the "additional" tab
14748 * Click over the TStringGrid icon [![tstringgrid.png](/wiki-images/c/c4/tstringgrid.png)](</File:tstringgrid.png>)
14849 * Click over the form, near to the top left corner. A new empty grid appears.
14950 3. Place a button on the form
15051 * From the component palette select the "Standard" tab
15152 * Click over the TButton icon [![tbutton.png](/wiki-images/b/b2/tbutton.png)](</File:tbutton.png>)
15253 * Click over a empty area of the form. A new button appears.
15354 4. Doubleclick the button from step 3, and write down the following code in the click button handler:
15455 *
15556```
15657StringGrid1.Cells[1,1] := 'Hi World!';
15758```
15859
15960 5. Run the program by clicking the play icon [![menu run.png](/wiki-images/0/0d/menu_run.png)](</File:menu_run.png>)
16061 * 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)
16162
16263## Differences between Lazarus and Delphi grids
16364
16465The 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.
16566
16667At 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.
16768
16869### Differences
16970
17071Known differences are listed below, in no special order.
17172
17273 * Cell Editors
17374 * Designtime Behaviour
17475 * Cell Drawing has some differences, see customizing grid section.
17576
17677### New Functionality
17778
17879 * Custom Columns
17980 * Events
18081 * Grid Editor
18182
18283### Ways in which you can make a Lazarus grid more Delphi-compatible
18384
18485You 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.
18586
18687 * [Design] TitleStyle := tsStandard;
18788 * [Design] DefaultRowHeight := 24;
18889 * [Code] EditorBorderStyle := bsNone; // this might work only on Windows.
18990 * [Code] UseXORFeatures := true;
19091 * [Code] AllowOutBoundEvents := False; // SVN revision 10992 or later
19192 * [Code] FastEditing := False; // supported in dbgrid. StringGrid requires SVN revision 10992 or later
19293 * [Design] AutoAdvance := aaNone;
19394
19495## Grids Reference
19596
19697### Information
19798
19899The 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>)
199100
200101For TCustomDBGrid and TDBgrid it is the [unit DBGrids.pas reference](<http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html>)
201102
202103In 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.
203104
204105TODO: 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>)
205106
206107### TCustomGrid
207108
208109See the full [TCustomGrid Reference](<http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomgrid.html> "doc:lcl/grids/tcustomgrid.html")
209110
210111#### property AllowOutboundEvents
211112
212113Protected 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.
213114
214115#### property Columns
215116
216117Lazarus 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.
217118
218119Custom 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**.
219120
220121For example, if to a base grid with ColCount := 5 and FixedCols := 2 we:
221122
222123 * Add 3 custom columns, the base grid will be exactly as before, 2 fixed cols and 3 normal cols.
223124 * Add 1 custom column, the base grid will have ColCount := 3, that is 2 fixed cols and 1 normal cols.
224125 * Add 4 custom columns, the base grid will have ColCount := 6, that is 2 fixed cols and 4 normal cols.
225126
226127From this we can conclude that:
227128
228129 * Fixed column properties or count are not enhanced or modified respectively by custom columns.
229130 * **grid.ColCount** is usually different from **grid.Columns.Count** (**grid.ColCount** =**grid.Columns.Count** only when **FixedCols** =0)
230131
231132At 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.
232133
233134At runtime, columns can be modified with code like this:
234135```
235136var
236137 c: TGridColumn;
237138 begin
238139 // add a custom column a grid
239140 c := Grid.Columns.Add;
240141 // modify
241142 c.title.caption := 'Price'; // Set columns caption
242143 c.align := taRightJustify; // Align column content to the right
243144 c.color := clMoneyGreen; // Change default color to clMoneyGreen
244145 c.Index := 0; // Make it the first column
245146 // access an existing column
246147 grid.columns[0].Width := 60; // Change column 0 width to 60 pixels
247148 // delete an existing column
248149 grid.columns.delete(0); // Delete column 0
249150 ....
250151 end;
251152```
252153
253154Additionally, 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.
254155
255156Currently there are no plans to make the grids use only custom columns.
256157
257158#### property AutoFillColumns
258159
259160Often 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.
260161
261162For 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`.
262163
263164#### procedure SaveToFile(AFileName: String);
264165
265166Saves the grid to an XML file. The property `SaveOptions` determines what exactly is to be saved:
266167```
267168type
268169 TGridSaveOptions = (
269170 soDesign, // Save grid structure (col/row count and Options)
270171 soAttributes, // Save grid attributes (Font, Brush, TextStyle)
271172 soContent, // Save Grid Content (Text in StringGrid)
272173 soPosition // Save Grid cursor and selection position
273174 );
274175 TSaveOptions = set of TGridSaveOptions;
275176```
276177
277178#### procedure LoadFromFile(AFileName: String);
278179
279180Loads the XML file created by `SaveToFile`. Again, the `SaveOptions` define what exactly is to be loaded from the file.
280181
281182### TCustomDBGrid
282183
283184TCustomDBGrid is the base for TDBGrid.
284185
285186They do not expose Col and Row properties. To go to a certain column, use e.g. the SelectedIndex property.
286187
287188An interesting public method is AutoAdjustColumns.
288189
289190#### procedure AutoAdjustColumns
290191
291192This 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.
292193
293194However, contrary to TCustomStringGrid.AutoAdjustColumns (see below), this will set your columns very wide unless you have the property dgAutoSizeColumns enabled.
294195
295196#### procedure InplaceEditor
296197
297198See example from bug 23103 - _and insert explanation of what it does/why it is needed. Validate input values? Change what is shown?_
298199```
299200procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
300201 var
301202 S: String;
302203 begin
303204 if (Key in [',','.']) then
304205 begin
305206 //unlike Delphi not all InPlaceEditors are editors for string type, so check!
306207 if (DBGrid1.InplaceEditor is TStringCellEditor) then
307208 begin
308209 S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
309210 if Pos(',',S) > 0 then
310211 Key := #0
311212 else
312213 Key := ',';
313214 end;
314215 end;
315216 end;
316217```
317218
318219### TCustomStringGrid
319220
320-TCustomStringGrid serves as the base for TStringGrid. It can be used for derived TStringGrid components that want to hide published properties. See [new intermediate grids](</index.php?title=new_intermediate_grids&action=edit&redlink=1> "new intermediate grids \(page does not exist\)") for more information.
221+TCustomStringGrid 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.
321222
322223The following properties or methods are public and are also available to TStringGrid.
323224
324225See the full [TCustomStringGrid Reference](<http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomstringgrid.html> "doc:lcl/grids/tcustomstringgrid.html")
325226
326227#### procedure AutoSizeColumn(aCol: Integer);
327228
328229This 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.
329230
330231#### procedure AutoAdjustColumns or AutoSizeColumns
331232
332233Automatically 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.
333234
334235#### procedure Clean; overload;
335236
336237Cleans all cells in the grid, fixed or not.
337238
338239#### procedure Clean(CleanOptions: TGridZoneSet); overload;
339240
340241Cleans all cells in the grid subject to the given CleanOptions. See [TGridZoneSet](<http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tgridzoneset.html> "doc:lcl/grids/tgridzoneset.html") for more information. Some examples:
341242
342243 * Clean all cells: grid.Clean([]); (the same as grid.clean)
343244 * Clean all non fixed cells: grid.Clean([gzNormal]);
344245 * Clean all cells but don't touch grid column headers: Grid.Clean([gzNormal, gzFixedRows]);
345246
346247#### procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload;
347248
348249does the same as Clean(CleanOptions:TGridZoneSet) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:
349250
350251 * 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.
351252
352253#### procedure Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;
353254
354255The 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,[]);
355256
356257#### procedure SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);
357258
358259Save grid content to a comma separated values format (CSV) file (added in Lazarus r32179).
359260
360261The 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.
361262
362263ADelimiter (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.
363264
364265The 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.
365266
366267If 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.
367268
368269Normal CSV data output will start at the first non-fixed row in the grid.
369270
370271#### procedure LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);
371272
372273Loads grid content from a comma separated values format (CSV) file (added in Lazarus r32179).
373274
374275Columns 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.
375276
376277The _AFileName_ argument specifies the name of the source file with the CSV content.
377278
378279_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 ;
379280
380281The _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.
381282
382283If 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.
383284
384285#### property Cols[index: Integer]: TStrings read GetCols write SetCols;
385286
386287Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1.
387288
388289##### Examples
389290
390291 * Set Example: Set the content of the third column in the grid from a ListBox:
391292
392293```
393294Grid.Cols[2] := ListBox1.Items;
394295```
395296
396297 * Get Example: Set the content of a Listbox from the grid's column index 4:
397298
398299```
399300procedure TForm1.FillListBox1;
400301 var
401302 StrTempList: TStringList;
402303 begin
403304 StrTempList := TStringList(Grid.Cols[4]);
404305 if StrTempList<>nil then begin
405306 ListBox1.Items.Assign(StrTempList);
406307 StrTempList.Free;
407308 end;
408309 end;
409310```
410311
411312##### Notes.
412313
413314This 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.
414315
415316This means also that changes in the returned list will not affect the grids content or layout.
416317
417318See the Get Example.
418319
419320#### property Rows[index: Integer]: TStrings read GetRows write SetRows;
420321
421322Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1.
422323
423324##### Notes.
424325
425326This 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.
426327
427328This means also that changes in the returned list will not affect the grid's content or layout.
428329
429330##### Examples
430331
431332 * Set Example: Set the content of the third row in the grid from a ListBox:
432333
433334```
434335Grid.Rows[2] := ListBox1.Items;
435336```
436337
437338 * Get Example: Set the content of a Listbox from the grid's row index 4:
438339
439340```
440341procedure TForm1.FillListBox1;
441342 var
442343 StrTempList: TStringList;
443344 begin
444345 StrTempList := TStringList(Grid.Rows[4]);
445346 if StrTempList<>nil then begin
446347 ListBox1.Items.Assign(StrTempList);
447348 StrTempList.Free;
448349 end;
449350 end;
450351```
451352
452353 * An Example that doesn't work, and its Fix: Retrieved string list is read only
453354
454355```
455356// this will not work and will cause memory leak
456357 // because returned StringList is not being freed
457358 Grid.Rows[1].CommaText := '1,2,3,4,5';
458359 Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g';
459360
460361 // fixing the first case
461362 Lst:=TStringList.Create;
462363 Lst.CommaText := '1,2,3,4,5';
463364 Grid.Rows[1] := Lst;
464365 Lst.Free;
465366```
466367
467368#### property UseXORFeatures;
468369
469370Boolean property, default value: False;
470371
471-This 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](</index.php?title=FocusColor_property&action=edit&redlink=1> "FocusColor property \(page does not exist\)")
372+This 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]]
472373
473374It 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.
474375
475376### TValueListEditor
476377
477378[[TValueListEditor]] is a control derived from TCustomStringGrid for editing Key-Value pairs.
478379
479380#### Property DisplayOptions
480381
481382Controls various aspects of the TValueListEditor's appearance.
482383
483384#### Property TitleCaptions
484385
485386Sets the values of the title captions (if doColumnTitles is in DisplayOptions). If DisplayOptions lacks the value doColumnTitles then default captions are used.
486387
487388#### Property Strings
488389
489390Provides access to the list of strings that hold the Key-Value pairs.
490391Key-Value pairs must be in the form:
491392'KeyName=Value'
492393
493394#### Property ItemProps
494395
495396You can use this property to control how the items in the "Value" columns can be edited.
496397This is controlled by setting the ItemProp's EditStyle and ReadOnly properties.
497398
498399#### Property KeyOptions
499400
500401KeyOptions is a set of TKeyOptions controlling whether the user can modify the contents of the "Key" column.
501402
502403 * KeyEdit: the user can edit the name of the Key
503404 * KeyAdd: the user can add keys (by pressing Insert in the grid). KeyAdd requires KeyEdit.
504405 * KeyDelete: the user can delete Key-Value pairs (by pressing Ctrl+Delete).
505406 * KeyUnique: if set, then Keys must have unique names. Attempting to enter a dulpicate Key will raise an exception.
506407
507408#### Property DropDownRows
508409
509410If the cell editor is a picklist (ValueEdit1.ItemProps['key1'].EditStyle=esPickList) this property sets the DropDownCount of the displayed list. The default is 8.
510411
511412#### Function DeleteRow
512413
513414Deletes the Key-Value pair of the indexed row removing the row entirely.
514415
515416#### Function InsertRow
516417
517418Inserts a row in the grid and sets the Key-Value pair. Returns the index of the newly inserted row.
518419
519420#### Function IsEmptyRow
520421
521422Returns true if the indexed row's cells are empty (Keys[aRow]=_; Values[aRow]=_).
522423
523424#### Function FindRow
524425
525426Returns the row that has the specified key name.
526427
527428#### Function RestoreCurrentRow
528429
529430Undoes the editing in the current row (if the editor is still focused). Happens when the user presses the Escape key.
530431
531432#### Altered behaviour of some properties derived from TCustomStringGrid
532433
533434##### Property Options
534435
535436Due to the nature of TValueListEditor its Options property has certain restrictions
536437
537438 * goColMoving is not allowed in Options (you cannot set it).
538439 * goAutoAddRows can only be set if KeyAdd is in KeyOptions. Setting KeyAdd will automatically set goAutoAddRows.
539440 * goAutoAddRowsSkipContentCheck is not allowed (for the time being, it causes a crash in TValueListeditor: needs fixing).
540441
541442##### Property FixedRows
542443
543444Can only be 1 (show column titles) or 0 (don't show column titles).
544445
545446##### Property ColCount
546447
547448Is always 2.
548449
549450##### Property Objects
550451
551452It is not recommended to use this property at all.
552453When some (internal) code manipulates the Strings property, changes are Objects will be lost.
553454
554455#### General comments on the use of TValueListEditor
555456
556457When manipulating the contents of the ValueListEditor (the grid), you can manipulate the underlying Strings property directly.
557458Be 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.
558459
559460If 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.
560461
561462Trying to use ancestor's methods to manipulate rows or columns (e.g. Columns.Add) might result in a crash.
562463
563464#### Saving and loading the contenst of a TValueListEditor
564465
565466##### Using the Strings property
566467
567468If 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.
568469In case of LoadFromFile the `RowCount` property will be autmatically adjusted.
569470No sanity checks are performed upon LoadFromFile (so lines that do not represent a Key/Value pair will end up as Keys without Value).
570471
571472##### Using TValueList.SaveToFile and TValueList.LoadFromFile
572473
573474When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.
574475SaveToFile will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.
575476
576477In 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.
577478If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.
578479
579480TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions <= 2.1 r62044.
580481
581482##### Using SaveToCSVFile and LoadFromCSV methods
582483
583484These methods should **not** be used at the moment, since for a TValueListEditor they are flawed.
584485
585486## Working with grids
586487
587488### Customizing grids
588489
589490Grid 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).
590491
591492### Properties and Events for customizing grids
592493
593494Some 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:
594495
595496 * **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.
596497 * **Color.** This sets the primary color used to draw non fixed cells background.
597498 * **FixedColor.** This is the color used to draw fixed cells background.
598499 * **Flat.** This eliminates the 3d look of fixed cells.
599500
600501 * **TitleFont.** Font used to draw the text in fixed cells.
601502
602503 * **TitleStyle.** This property changes the 3D look of fixed cells, there are 3 settings:
603504 * _tsLazarus._ This is the default look
604505 * _tsNative._ This tries to set a look that is conforms with the current widgetset theme.
605506 * _tsStandard._ This style is a more contrasted look, like Delphi grids.
606507 * **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.
607508 * **BorderColor.** This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;
608509 * **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.
609510
610511 * **FocusColor.** The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.
611512 * **FocusRectVisible.** Turns on/off the drawing of focused cell.
612513 * **GridLineColor.** Color of grid lines in non fixed area.
613514 * **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_.
614515 * **SelectedColor.** Color used to draw cell background on selected cells.
615516 * **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.
616517 * **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.
617518 * **AutoAdvance.** Where the cell cursor will go when pressing Enter, or after editing.
618519 * **TabAdvance.** Where the cell cursor will go when pressing Tab or Shift-Tab.
619520 * **ExtendedColSizing.** If true user can resize columns not just at the headers but along the columns height.
620521
621522Other properties that also affect the grids look.
622523
623524**Options**.
624525
625- Options 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.
526+Options 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.
626527
627528 * **goFixedVertLine, goFixedHorzLine** it draws a vertical or horizontal line respectively delimiting cells or columns in fixed area, active by default.
628529 * **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.
629530 * **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)
630531 * **goRowSelect** select the full row instead of individual cells
631532 * **goFixedRowNumbering** if set, grid will do numbering of rows in first fixed column
632533 * **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.
633534 * **goHeaderPushedLook** if set, this element enables a pushed look when clicking any fixed cell. The zone of "pushable" cells is enabled using HeaderPusedZones property.
634535
635536(write more)
636537
637538### Description of grid's drawing process
638539
639540Like 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.
640541
641542The process is as follow:
642543
643544 * 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.
644545 * 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.
645546 * 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:
646547 * _gdSelected_ The cell will have a selected look.
647548 * _gdFocused_ The cell will have a focused look.
648549 * _gdFixed_ Cell have to be painted with fixed cell look.
649550 * _gdHot_ the mouse is over this cell, so paint it with hot tracking look
650551 * _gdPushed_ the cell is being clicked, paint it with pushed look
651552 * **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:
652553 * **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.
653554 * **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?...
654555 * **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.
655556 * **Text.** At this point (only for TStringGrid) if DefaultDrawing property is true, the cell's text content is painted.
656557 * **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).
657558 * **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.
658559
659560#### Differences with Delphi
660561
661562 * In Lazarus TCustomGrid.DrawCell method is not abstract and its default implementation does basic cell background filling.
662563 * 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>)).
663564
664565### Grid's cell selection
665566
666567The 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.
667568
668569The 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.
669570
670571Once 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.
671572
672573After 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).
673574
674575Note 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.
675576
676577#### Differences with Delphi
677578
678579 * 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.
679580
680581### When built-in properties are not enough: derived grids
681582
682583Derived grids usually have to override the following methods:
683584DrawAllRows: Draws all visible rows.
684585DrawRow: Draws all cells in a row.
685586DrawRow draws all cells in the row by first checking if cell is within clipping region, and only draws the cell if it is.
686587DrawCell:
687588DrawCellGrid:
688589DrawCellText:
689590DrawFocusRect:
690591(write me).
691592
692593### What happens in the `TCustomGrid.Paint` method?
693594
694595The 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`.
695596
696597 * **`DrawEdges`** : Draws the grid's outer border.
697598 * **`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`_.
698599 * **`DrawRow`** (virtual): Is called for each row inside the current viewport.
699600 * **`DrawCell`** (virtual): Is first called for each 'normal' (i.e. not fixed) cell in the row.
700601 * **`PrepareCanvas`** (virtual): Sets the canvas's drawing styles according to the current cell's visual properties.
701602 * **`DrawFillRect`** : Draws the cell's background with the styles set in `PrepareCanvas`.
702603 * **`DrawCellGrid`** (virtual): Draws the cell's border lines.
703604 * **`DrawFocusRect`** (virtual): In `TCustomGrid` this method does nothing. In descendent grids this method is used to draw the focus rectangle inside the active cell.
704605 * **`DrawCell`** (virtual): (see above) Is called for each fixed cell in the row's visible viewport.
705606 * **`DrawColRowMoving`** : only active while moving a column or row. This method draws the line that indicates the row's/column's new position.
706607 * **`DrawBorder`** : If needed (`Flat=TRUE` and `BorderStyle=bsSingle`), this draws an inner border line.
707608
708609#### Additional methods to draw in `TCustomGrid`
709610
710611These 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.
711612
712613 * **`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`).
713614 * **`DrawThemedCell`** (virtual): Is only used for fixed cells and if `TitleStyle=tsNative`. It draws the cell's background using `ThemeServices`.
714615 * **`DrawColumnText`** (virtual): Is only used for column header cells.
715616 * **`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.
716617 * **`DrawCellText`** : (see above)
717618 * **`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).
718619
719620#### Drawing methods introduced by `TCustomDrawGrid`
720621
721622As 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:
722623
723624 * **`DrawCell`** (override):
724625 * **`PrepareCanvas`** : calls the inherited method from **`TCustomGrid`**.
725626 * **`DefaultDrawCell`** (virtual): This method is only called if the event handler for `OnDrawCell` is NOT assigned.
726627 * **`DrawFillRect / DrawThemedCell`** (von `TCustomGrid`): Draws the cell's background. If `TitleStyle=tsNative`, the background of fixed cells is drawn using `DrawThemedCell`.
727628 * **`DrawCellAutonumbering`** (virtual): If `goFixedAutonumbering in Options`, row numbers are drawn into the first fixed column using `TCustomGrid.DrawCellText`.
728629 * **`DrawColumnText`** (from `TCustomGrid`): Is only called for column header cells (see _Additional methods to draw in` TCustomGrid`_).
729630 * **`DrawTextInCell`** (from `TCustomGrid`): Is only called for 'normal' (i.e. not fixed) cells (see _Additional methods to draw in` TCustomGrid`_).
730631 * **`DrawCellGrid`** (virtual): Draws the cell's borders
731632
732633### Save and Retrieve Grid Content
733634
734635The **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).
735636
736637The 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:
737638```
738639soDesign: Save & load ColCount,RowCount,FixedCols,FixedRows,
739640 ColWidths, RowHeights and Options (TCustomGrid)
740641 soPosition: Save & load Scroll position, Row, Col and Selection (TCustomGrid)
741642 soAttributes: Save & load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)
742643 soContent: Save & load Text (TCustomStringGrid)
743644 soAll: set of all options (soAll:=[soDesign,soPosition,soAttributes,soContent];)
744645```
745646
746647Not 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.
747648
748649The soAttributes option is not used in Lazarus standard grids, is there for derived grids.
749650
750651When 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.
751652
752653For a TStringGrid the default SaveOptions value is [soContent], for other kind of grids, SaveOptions is the empty set.
753654
754655**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:
755656
756657* * *
757658
758659**Example:**
759660
760661 1. First, go to menu "File -> New -> Application";
761662 2. Put an empty TStringGrid on the form;
762663 3. Put a TButton and TOpenDialog on the form;
763664 4. Add the event OnCreate for the form;
764665 5. Add the event OnClick for the button.
765666
766667```
767668unit Unit1;
768669
769670 {$mode objfpc}{$H+}
770671
771672 interface
772673
773674 uses
774675 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,
775676 Buttons, StdCtrls, XMLCfg;
776677
777678 type
778679
779680 { TForm1 }
780681 TForm1 = class(TForm)
781682 StringGrid1: TStringGrid;
782683 Button1: TButton;
783684 OpenDialog1: TOpenDialog;
784685 procedure Button1Click(Sender: TObject);
785686 procedure Form1Create(Sender: TObject);
786687 private
787688 { private declarations }
788689 public
789690 { public declarations }
790691 end;
791692
792693 var
793694 Form1: TForm1;
794695
795696 implementation
796697
797698 { TForm1 }
798699
799700 procedure TForm1.Form1Create(Sender: TObject);
800701 begin
801702 //sets the SaveOptions at creation time of the form
802703 stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
803704 end;
804705
805706
806707 procedure TForm1.Button1Click(Sender: TObject);
807708 begin
808709 //Ask if thew Execute method of the OpenDialog was launched
809710 //when this occurs, the user selects an XML file to Load
810711 //wich name was stored in the FileName prop.
811712
812713 if opendialog1.Execute then
813714 begin
814715 //Clear the grid
815716 StringGrid1.Clear;
816717 //Load the XML
817718 StringGrid1.LoadFromFile(OpenDialog1.FileName);
818719 //Refresh the Grid
819720 StringGrid1.Refresh;
820721 end;
821722 end;
822723
823724 initialization
824725 {$I unit1.lrs}
825726
826727 end.
827728```
828729
829730* * *
830731
831732**The sample xml file:** (Copy the text below into a txt file. Don't forget put the xml header :-))
832733```
833734<?xml version="1.0"?>
834735 <CONFIG>
835736 <grid version="3">
836737 <saveoptions create="True" position="True" content="True"/>
837738 <design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20">
838739 <options>
839740 <goFixedVertLine value="True"/>
840741 <goFixedHorzLine value="True"/>
841742 <goVertLine value="True"/>
842743 <goHorzLine value="True"/>
843744 <goRangeSelect value="True"/>
844745 <goDrawFocusSelected value="False"/>
845746 <goRowSizing value="False"/>
846747 <goColSizing value="False"/>
847748 <goRowMoving value="False"/>
848749 <goColMoving value="False"/>
849750 <goEditing value="False"/>
850751 <goTabs value="False"/>
851752 <goRowSelect value="False"/>
852753 <goAlwaysShowEditor value="False"/>
853754 <goThumbTracking value="False"/>
854755 <goColSpanning value="False"/>
855756 <goRelaxedRowSelect value="False"/>
856757 <goDblClickAutoSize value="False"/>
857758 <goSmoothScroll value="True"/>
858759 </options>
859760 </design>
860761 <position topleftcol="1" topleftrow="1" col="1" row="1">
861762 <selection left="1" top="1" right="1" bottom="1"/>
862763 </position>
863764 <content>
864765 <cells cellcount="10">
865766 <cell1 column="0" row="0" text="Title Col1"/>
866767 <cell2 column="0" row="1" text="value(1.1)"/>
867768 <cell3 column="0" row="2" text="value(2.1)"/>
868769 <cell4 column="0" row="3" text="value(3.1)"/>
869770 <cell5 column="0" row="4" text="value(4.1)"/>
870771 <cell6 column="1" row="0" text="Title Col2"/>
871772 <cell7 column="1" row="1" text="value(1.2)"/>
872773 <cell8 column="1" row="2" text="value(2.2)"/>
873774 <cell9 column="1" row="3" text="value(3.2)"/>
874775 <cell10 column="1" row="4" text="value(4.2)"/>
875776 </cells>
876777 </content>
877778 </grid>
878779 </CONFIG>
879780```
880781
881782### Grid Cell Editors
882783
883784The grid uses cell editors to change the content of cells.
884785
885786For 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:
886787
887788 1. show the open file dialog to find the location of a file so the user doesn't have to type the full path manually
888789 2. if the text in the cell represents a date, popup a calendar so we can choose a specific date easily.
889790
890791Sometimes 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.
891792
892793This 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.
893794
894795#### Builtin cell editors
895796
896797The 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.
897798
898799The built-in cell editors are Button, Edit, and Picklist.
899800
900801#### Using cell editors
901802
902803Users can specify what editor will be used for a cell using one of two methods.
903804
904805 1. **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.
905806 2. **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).
906807
907808Setting 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:
908809```
909810if ColCB< StringGrid1.Columns.Count
910811 then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;
911812```
912813
913814#### Description of editor styles
914815
915816The 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.
916817
917818 * **cbsAuto**
918819
919- This 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.
820+This 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.
920821
921822 * **cbsEllipsis**
922823
923- This 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.
824+This 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.
924825
925- OnEditButtonClick 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.
826+OnEditButtonClick 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.
926827
927- A 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.
828+A 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.
928829
929- This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
830+This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
930831
931832 * **cbsNone**
932833
933- This 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.
834+This 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.
934835
935836 * **cbsPickList**
936837
937- Used 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.
838+Used 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.
938839
939840 1. 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]
940841 2. In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList). See here for an example
941842
942- The 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.
843+The 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.
943844
944845 * **cbsCheckboxColumn**
945846
946- It 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).
947- Getting or setting the value of the checkbox in a cell is done the following way:
847+It 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).
848+
849+Getting or setting the value of the checkbox in a cell is done the following way:
948850```
949851StringGrid1.Cell[x,y]:='Z';
950852```
951853
952- where 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.
854+where 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.
953855
954- If 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.
856+If 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.
955857
956- The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
858+The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
957859
958- At 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.
860+At 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.
959861
960- This 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.
862+This 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.
961863
962864 * **cbsButtonColumn**
963865
964- This 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.
866+This 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.
965867
966- The 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.
868+The 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.
967869
968- The button's caption is the corresponding cell string.
870+The button's caption is the corresponding cell string.
969871
970872### Editing grids
971873
972874The 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.
973875For 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".
974876
975877#### Options and properties that affect editing
976878
977879The 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.
978880At start the editable state of the grids is different, for TDrawGrid and TStringGrid editing is disabled, for TDbGrid is enabled by default.
979881The **Options** property has several items that deal with editing, they are described below:
980882
981883 * **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.
982884 * **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.
983885
984886### Cell hints
985887
986888In 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):
987889
988890 * **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.
989891 * **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.
990892
991893If 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:
992894
993895 * **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
994896 * **chpAllNoDefault** displays only the cell hints given by the OnGetCellHint event and by the truncated cells.
995897 * **chpTruncOnly** displays only hints for the truncated cells; this is the default settings.
996898
997899In addition, the standard property **ShowHint** of the grid must be set to true in order to show cell hints.
998900
999901## Grids Howto and Examples
1000902
1001903### Focusing a cell
1002904
1003905Focusing 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:
1004906```
1005907StringGrid1.Row := 9;
1006908 StringGrid1.Col := 8;
1007909```
1008910
1009911### Example: How to set a custom cell editor
1010912
1011913See lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (from laz 1.2)
1012914
1013915### Example: How to add a button editor
1014916```
1015917// Conditionally show button editor in column index 1 or 2 if
1016918 // cell in column index 1 is empty
1017919 procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
1018920 var Editor: TWinControl);
1019921 begin
1020922 if (aCol = 1) or (aCol = 2) then
1021923 if StringGrid1.Cells[1,aRow] = '' then
1022924 begin
1023925 Editor := StringGrid1.EditorByStyle(cbsEllipsis);
1024926 end;
1025927 end;
1026928
1027929 // Triggering Action ...
1028930 procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
1029931 begin
1030932 if StringGrid1.Col = 1 then Showmessage('column 1 editor clicked');
1031933 if StringGrid1.Col = 2 then Showmessage('column 2 editor clicked');
1032934 end;
1033935```
1034936
1035937### Example: Working with Picklist, How to make it read only and How to fill it at run time
1036938
1037939Using 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.
1038940```
1039941procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
1040942 var Editor: TWinControl);
1041943 begin
1042944 if aCol=1 then begin
1043945 Editor := StringGrid1.EditorByStyle(cbsPickList);
1044946 if (Editor is TCustomComboBox) then
1045947 with Editor as TCustomComboBox do begin
1046948 if (aRow mod 2=0) then
1047949 Style := csDropDown
1048950 else
1049951 Style := csDropDownList;
1050952 case aRow of
1051953 1:
1052954 Items.CommaText := 'ONE,TWO,THREE,FOUR';
1053955 2:
1054956 Items.CommaText := 'A,B,C,D,E';
1055957 3:
1056958 Items.CommaText := 'MX,ES,NL,UK';
1057959 4:
1058960 Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
1059961 end;
1060962 end;
1061963 end;
1062964 end;
1063965```
1064966
1065967### Aligning text in StringGrids
1066968
1067969This code shows how to use different text alignments in columns 2 and 3.
1068970```
1069971procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
1070972 aState: TGridDrawState);
1071973 var
1072974 MyTextStyle: TTextStyle;
1073975 begin
1074976 if (aCol=2) or (aCol=3) then
1075977 begin
1076978 MyTextStyle := StringGrid1.Canvas.TextStyle;
1077979 if aCol=2 then
1078980 MyTextStyle.Alignment := taRightJustify
1079981 else
1080982 if aCol=3 then
1081983 MyTextStyle.Alignment := taCenter;
1082984 StringGrid1.Canvas.TextStyle := MyTextStyle;
1083985 end;
1084986 end;
1085987```
1086988
1087989### Multilines in Grids
1088990
1089991This 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.
1090992```
1091993procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
1092994 aState: TGridDrawState);
1093995 var
1094996 MyTextStyle: TTextStyle;
1095997 begin
1096998 if (aRow=2) or (aCol=3) then
1097999 begin
10981000 MyTextStyle := StringGrid1.Canvas.TextStyle;
10991001 MyTextStyle.SingleLine := false;
11001002 StringGrid1.Canvas.TextStyle := MyTextStyle;
11011003 end;
11021004 end;
11031005```
11041006
11051007### Validating Entered Values
11061008
11071009Lazarus version 0.9.29 introduces the StringGrid OnValidateEntry event of type TValidateEntryEvent which has the following declaration:
11081010```
11091011type
11101012 TValidateEntryEvent = procedure(sender: TObject; aCol, aRow: Integer; const OldValue: string; var NewValue: string) of object;
11111013```
11121014```
11131015aCol,aRow are the cell coordinates of cell being validated.
11141016 OldValue is the value that was in cells[aCol,aRow] before editing started.
11151017 NewValue is the value that will be finally inserted in cells[aCol,aRow].
11161018```
11171019
11181020Because 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.
11191021
11201022Usually 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.
11211023
11221024For example, suppose that cell[1,1] should hold only values 'A' or 'B', validation could be made with:
11231025```
11241026procedure TForm1.GridValidateEntry(sender: TObject; aCol,
11251027 aRow: Integer; const OldValue: string; var NewValue: String);
11261028 begin
11271029 if (aCol=1) and (aRow=1) then begin
11281030 if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
11291031 // set a new valid value so user can just press RETURN to continue for example.
11301032 NewValue := 'A';
11311033 // another option is reverting to previous cell value (which is assumed to be valid)
11321034 // NewValue := OldValue;
11331035 // Use EAbort rather than another Exception type to avoid spurious error
11341036 // dialog boxes and backtraces
11351037 raise EAbort.Create('Only A or B are allowed here');
11361038 end else begin
11371039 // if no exception is raised, it is assumed the value is valid, yet if necessary
11381040 // the final value can still be changed by filling NewValue with a different value
11391041
11401042 // computer knows better :)
11411043 if grid.Cells[1,1]='A' then
11421044 NewValue := 'B'
11431045 else
11441046 NewValue := 'A';
11451047 end;
11461048 end;
11471049 end;
11481050```
11491051
11501052### Sorting Columns or Rows
11511053
11521054Property 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.
11531055
11541056In code you can use SortColRow() method.
11551057
11561058The first parameter, IsColumn, is a boolean value which indicates:
11571059
11581060 * True if a column is to be sorted
11591061 * False if a row is to be sorted.
11601062
11611063The second parameter, index, is an integer value:
11621064
11631065 * Index Indicates Column or Row to be sorted.
11641066
11651067The last two parameters are optional, defines subrange of rows or columns to be sorted.
11661068
11671069 * FromIndex (From column or row)
11681070 * ToIndex (To column or row)
11691071
11701072If the last two parameters are not specified, the whole column or row is sorted.
11711073
11721074Sorting uses QuickSort algorithm, it could be changed if a descendant grid overrides the sort() method and calls doCompareCells for cell compare.
11731075
11741076By 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.
11751077```
11761078// Sort column 3 in ascending order
11771079 grid.SortColRow(true, 3);
11781080
11791081 // Sort column 3 in descending order, skip fixed rows a top
11801082 grid.SortOrder := soDescending; // or soAscending
11811083 grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);
11821084```
11831085
11841086For custom sorting of numbers, dates, states, etc: StringGrid has the OnCompareCells event which users can handle for example this way:
11851087```
11861088procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
11871089 begin
11881090 // Result will be either <0, =0, or >0 for normal order.
11891091 result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
11901092 // For inverse order, just negate the result (eg. based on grid's SortOrder).
11911093 if StringGrid1.SortOrder = soDescending then
11921094 result := -result;
11931095 end;
11941096```
11951097
11961098You can use OnCompareCells also when automatic column sorting is enabled through ColumnClickSorts property.
11971099
11981100### Override automatic sort (ColumnClickSorts set to False). Avoid sort of some columns. Show sort arrows in column header
11991101
12001102(ColumnClickSorts set to False will NOT show sort arrows in column header, so we have to set the arrows.)
12011103```
12021104procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
12031105 Index: Integer);
12041106 begin
12051107 if isColumn then with StringGrid1 do
12061108 begin
12071109 col:=Index; // Set column to sort
12081110 if Tag = 0 then Tag :=-1 // Suppose data in column is Ascending
12091111 else Tag := Tag * -1; // Using StringGrid1.Tag to hold sortorder
12101112 // First click of header will sort descending
12111113
12121114 // Activate CompareCells (ativate the sort)
12131115 case Index of
12141116 0: col:=-1; // Do not sort column 0 and do not show sort arrow
12151117 1: SortColRow(True, Index); // The sort will take place in CompareCells as soon as statement is executed
12161118 2: SortColRow(True, Index);
12171119 3: SortColRow(True, Index);
12181120 4: SortColRow(True, Index);
12191121 5: col:=-1; // Do not sort column 5 and do not show sort arrow
12201122 end;
12211123 end;
12221124 end;
12231125
12241126 // Compare returns one of the following values:
12251127 // if A<B return: -1 ( Ascending )
12261128 // if A=B return: 0
12271129 // if A>B return: 1 ( Descending)
12281130
12291131 // How to sort: Text, Integer, Float, DateTime:
12301132
12311133 procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
12321134 BRow: Integer; var Result: integer);
12331135
12341136 function CompareTextAsText: Integer; // Text
12351137 begin
12361138 CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
12371139 end;
12381140
12391141 function CompareTextAsInteger: Integer;
12401142 var i1,i2:Integer;
12411143 begin
12421144 if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and // Integer
12431145 TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
12441146 Result := CompareValue(i1, i2)
12451147 else Exit(0);
12461148 end;
12471149
12481150 function CompareTextAsFloat: Integer; // Float
12491151 var f1,f2:Extended;
12501152 begin
12511153 if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
12521154 TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
12531155 Result := CompareValue(f1, f2)
12541156 else Exit(0);
12551157 end;
12561158
12571159 function CompareTextAsDateTime: Integer; // DateTime
12581160 var
12591161 D1, D2 : TDateTime;
12601162 begin
12611163 if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
12621164 begin
12631165 if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
12641166 if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
12651167 end
12661168 else Exit(0);
12671169
12681170 if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
12691171 TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
12701172 Result := CompareDateTime(D1, D2)
12711173 else Exit(0);
12721174 end;
12731175
12741176 begin // Don't do anything else here, sort might be done incorrect
12751177 With StringGrid1 do
12761178 begin
12771179 case ACol of
12781180 0 : ; // Do not sort column 0
12791181 1 : Result := CompareTextAsText; // Text
12801182 2 : Result := CompareTextAsInteger; // Integer
12811183 3 : Result := CompareTextAsFloat; // Float
12821184 4 : Result := CompareTextAsDateTime; // DateTime
12831185 end; {Case}
12841186 end;
12851187 if StringGrid1.Tag = -1 then Result := -Result; // Switch the sort order
12861188 end;
12871189
12881190 // DrawCell does the trick, previous arrow images are removed:
12891191 procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
12901192 aRect: TRect; aState: TGridDrawState);
12911193
12921194 procedure DrawArrow(ImgIndex:Integer); // Need Up/Down arrows in a ImageList
12931195 begin
12941196 with StringGrid1 do
12951197 TitleImageList.Draw(Canvas, // TitleImageList of StringGrid
12961198 ARect.Right - TitleImageList.Width,
12971199 (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2, // Position of arrow, may be adjusted.
12981200 ImgIndex );
12991201 end;
13001202 begin
13011203 if Col > 0 then // In this demo no arrows in col 0
13021204 if (ARow = 0) and // Suppose Row 0 as TitleRow
13031205 Assigned(TitleImageList) then // Need Up/Down arrows in a ImageList
13041206 case Columns.Items[aCol].Tag of // Use Tag of Column as index of Ascending/Descending arrow
13051207 -1: DrawArrow(1); // Replace with your Index of down arrow
13061208 0: DrawArrow(-1); // Remove previous image
13071209 1: DrawArrow(2); // Replace with your Index of Up Arrow
13081210 end;
13091211 end;
13101212```
13111213
13121214### Highlighting the selected cell column and row
13131215
13141216In 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?.
13151217
13161218This 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.
13171219
13181220The 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:
13191221```
13201222procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
13211223 begin
13221224 // we can decide here if we want highlight columns, rows or both
13231225 // in this example we highlight both
13241226 if Grid.Col<>aCol then
13251227 begin
13261228 // a change on current column is detected
13271229 grid.InvalidateCol(aCol); // invalidate the new selected cell column
13281230 grid.InvalidateCol(grid.Col); // invalidate the current (it will be the 'old') selected column
13291231 end;
13301232 if Grid.Row<>aRow then
13311233 begin
13321234 grid.InvalidateRow(aRow); // invalidate the new selected cell row
13331235 grid.InvalidateRow(grid.Row); // invalidate the current (it will be the 'old') selected row
13341236 end;
13351237 end;
13361238
13371239 procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
13381240 aState: TGridDrawState);
13391241 begin
13401242 if gdFixed in aState then
13411243 begin
13421244 if (aCol=grid.Col) or (aRow=grid.Row) then
13431245 grid.Canvas.Brush.Color := clInactiveCaption; // this would highlight also column or row headers
13441246 end else
13451247 if gdFocused in aState then begin
13461248 // we leave alone the current selected/focused cell
13471249 end else
13481250 if (aCol=Grid.Col) or (aRow=grid.Row) then
13491251 grid.Canvas.Brush.Color := clSkyBlue; // highlight rows and columns with clSkyBlue color
13501252 end;
13511253```
13521254
13531255### Avoiding the empty space at the right of the last column
13541256
13551257The 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:
13561258
13571259 * Set `AutoFillColumns` to `true`.
13581260 * Using the Columns editor, add as many columns as you need.
13591261 * Select each column in the object tree above the object inspector and set its `SizePriority` to `0`.
13601262 * Now decide while column is widened such that all empty space is used up. Set the `SizePriority` of this column to `1`.
13611263
13621264### How to scroll a Grid in "realtime" (moving the scrollbar via mouse)
13631265
13641266If 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`.
13651267
13661268Example:
13671269```
13681270StringGrid1.Options:=StringGrid1.Options + [goThumbTracking];
13691271```
13701272
13711273Unfortunately 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>
13721274
13731275### How to resize a row based on cells content
13741276
13751277Lacak in bug report [#38473](<https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/38473>) contributed the following code:
13761278```
13771279type
13781280 { TStringGridHelper }
13791281 TStringGridHelper = class helper for TStringGrid
13801282 protected
13811283 procedure AutoSizeRow(aRow: integer);
13821284 end;
13831285
13841286 { TStringGridHelper }
13851287 procedure TStringGridHelper.AutoSizeRow(aRow: integer);
13861288 var
13871289 aCanvas: TCanvas;
13881290 aCol, maxRowHeight:integer;
13891291 aText: String;
13901292 textRect: TRect;
13911293 begin
13921294 aCanvas := GetWorkingCanvas(Canvas);
13931295 maxRowHeight := DefaultRowHeight;
13941296 for aCol:=0 to ColCount-1 do begin
13951297 aText := Cells[aCol,aRow];
13961298 textRect := Rect(0, 0, ColWidths[aCol], MaxInt);
13971299 DrawText(aCanvas.Handle, PChar(aText), Length(aText), textRect, DT_CALCRECT or DT_WORDBREAK);
13981300 if maxRowHeight < textRect.Height then
13991301 maxRowHeight := textRect.Height
14001302 end;
14011303 if aCanvas<>Canvas then FreeWorkingCanvas(aCanvas);
14021304 RowHeights[aRow] := maxRowHeight+2;
14031305 end;
14041306```
14051307
14061308Add that somewhere in your source code and then you can resize a row as if the AutoSizeRow procedure is part of the stringgrid, example:
14071309```
14081310// Resize the row index 2
14091311 grid.AutoSizeRow(2);
14101312```
14111313
14121314## DBGrids Howto and Examples
14131315
14141316### Example: How to set a memo editor for dbgrids
14151317
14161318You 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>))
14171319
14181320 * 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.
14191321 * In the OnSelectEditor event put the following code - adapt if you don't want to edit logical column 3, physical column 4:
14201322
14211323```
14221324if (Column.DesignIndex = 3) then
14231325 begin
14241326 GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
14251327 GridCellMemo.Text:=Column.Field.AsString;
14261328 Editor := GridCellMemo;
14271329 end;
14281330```
14291331
14301332 * Suppose your datasource is called Datasource1 and DBGrid is called ResultsGrid. Then, in the OnEditingDone event for the GridCellMemo put the following:
14311333
14321334(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.)
14331335```
14341336if not(Datasource1.State in [dsEdit, dsInsert]) then
14351337 Datasource1.Edit;
14361338
14371339 Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
14381340```
14391341
14401342**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>).
14411343
14421344**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.
14431345
14441346### Example: Avoid displaying text fields as "(Memo)" for DBGrids
14451347
14461348now (08/2023) exist the dbgrid option dgDisplayMemoText in object inspector - option
14471349
14481350
14491351When 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.
14501352```
14511353var
14521354 sql : UTF8String;
14531355 Queryu : TSQLQuery;
14541356 .....
14551357 sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
14561358 Queryu.SQL.Text:=sql;
14571359```
14581360
14591361
14601362
1461- for SQLITE3, to avoid cast function usage, just give len for fields in your sqlite database :
1462-
1463-
1363+for SQLITE3, to avoid cast function usage, just give len for fields in your sqlite database :
14641364
1465- \- when you import from csv or create table : create table mytable (myfield TEXT (10))
1365+\- when you import from csv or create table : create table mytable (myfield TEXT (10))
14661366
1467- \- or using sqlstudio or other to give len to fields
1367+\- or using sqlstudio or other to give len to fields
14681368
14691369### Sorting columns or rows in DBGrid with sort arrows in column header
14701370
14711371Here 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.
14721372
14731373**Prerequisites:**
14741374
14751375 * You will need an imagelist to store the up/down sort arrows. Assign your imagelist to the dbgrid's **TitleImageList** property.
14761376 * Ensure the **[TSQLQuery.MaxIndexesCount](<http://lazarus-ccr.sourceforge.net/docs/fcl/sqldb/tsqlquery.maxindexescount.html> "doc:fcl/sqldb/tsqlquery.maxindexescount.html")** is large enough to hold the new indexes you will be creating. For this example I had it set to 100.
14771377 * You will also need a private var to store the last column used to sort, for this example I have called it **FLastColumn**.
14781378
14791379For this example the **TSQLQuery** used is called OpenQuery.
14801380
14811381![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **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.
14821382
14831383In 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.
14841384```
14851385FLastColumn: TColumn; //store last grid column we sorted on
14861386
14871387 procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
14881388 const
14891389 ImageArrowUp=0; //should match image in imagelist
14901390 ImageArrowDown=1; //should match image in imagelist
14911391 var
14921392 ASC_IndexName, DESC_IndexName:string;
14931393 procedure UpdateIndexes;
14941394 begin
14951395 // Ensure index defs are up to date
14961396 OpenQuery.IndexDefs.Updated:=false; {<<<--This line is critical. IndexDefs.Update will not
14971397 update if already true, which will happen on the first column sorted.}
14981398 Openquery.IndexDefs.Update;
14991399 end;
15001400 begin
15011401 ASC_IndexName:='ASC_'+Column.FieldName;
15021402 DESC_IndexName:='DESC_'+Column.FieldName;
15031403 // indexes can't sort binary types such as ftMemo, ftBLOB
15041404 if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
15051405 exit;
15061406 // check if an ascending index already exists for this column.
15071407 // if not, create one
15081408 if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
15091409 begin
15101410 OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
15111411 UpdateIndexes; //ensure index defs are up to date
15121412 end;
15131413 // Check if a descending index already exists for this column
15141414 // if not, create one
15151415 if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
15161416 begin
15171417 OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
15181418 UpdateIndexes; //ensure index defs are up to date
15191419 end;
15201420
15211421 // Use the column tag to toggle ASC/DESC
15221422 column.tag := not column.tag;
15231423 if boolean(column.tag) then
15241424 begin
15251425 Column.Title.ImageIndex:=ImageArrowUp;
15261426 Openquery.IndexName:=ASC_IndexName;
15271427 end
15281428 else
15291429 begin
15301430 Column.Title.ImageIndex:=ImageArrowDown;
15311431 OpenQuery.IndexName:=DESC_IndexName;
15321432 end;
15331433 // Remove the sort arrow from the previous column we sorted
15341434 if (FLastColumn <> nil) and (FlastColumn <> Column) then
15351435 FLastColumn.Title.ImageIndex:=-1;
15361436 FLastColumn:=column;
15371437 end;
15381438```
15391439
15401440### Selecting Records in a DBGrid using checkboxes
15411441
15421442The 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.
15431443
15441444But 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.
15451445
15461446Note this technique needs Lazarus r31148 or later which implements event OnUserCheckboxState.
15471447```
15481448...
15491449 uses ..., dbgrids, stdctrls, ...
15501450
15511451 type
15521452
15531453 { TForm1 }
15541454
15551455 TForm1 = class(TForm)
15561456 ...
15571457 procedure DBGrid1CellClick(Column: TColumn);
15581458 procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
15591459 procedure FormCreate(Sender: TObject);
15601460 procedure FormDestroy(Sender: TObject);
15611461 ...
15621462 private
15631463 RecList: TBookmarklist;
15641464 ...
15651465 end;
15661466
15671467 procedure TForm1.DBGrid1CellClick(Column: TColumn);
15681468 begin
15691469 if Column.Index=1 then
15701470 RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
15711471 end;
15721472
15731473 procedure TForm1.FormCreate(Sender: TObject);
15741474 begin
15751475 RecList := TBookmarkList.Create(DbGrid1);
15761476 end;
15771477
15781478 procedure TForm1.FormDestroy(Sender: TObject);
15791479 begin
15801480 RecList.Free;
15811481 end;
15821482
15831483 procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
15841484 begin
15851485 if RecList.CurrentRowSelected then
15861486 AState := cbChecked
15871487 else
15881488 AState := cbUnchecked;
15891489 end;
15901490```
15911491
15921492### Example: How to export a DBGrid to a text file
15931493
15941494There 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.
15951495```
15961496// aGrid: The grid to be exported
15971497 // lpp: Lines per page. Not including the text header
15981498 // pageBreak: insert a page break that some printers could use for starting a new page
15991499 // require: uses LazUTF8
16001500 function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
16011501 const
16021502 PX_PER_CHAR = 7;
16031503
16041504 var
16051505 Ds: TDataset;
16061506 book: TBookMark;
16071507 recCount: Integer;
16081508 line: string;
16091509 column: TColumn;
16101510
16111511 function WidthToChar(aWidth: Integer): integer;
16121512 begin
16131513 result := trunc(aWidth/PX_PER_CHAR);
16141514 end;
16151515
16161516 procedure AddNext(theText: string; alignment: TAlignment);
16171517 var
16181518 width: Integer;
16191519 begin
16201520 if (line<>'') and (line<>#12) then
16211521 line := line + ' ';
16221522
16231523 width := WidthToChar(Column.Width);
16241524
16251525 case Alignment of
16261526 taRightJustify: line := line + UTF8PadLeft(theText, width);
16271527 taCenter: line := line + UTF8PadCenter(theText, width);
16281528 else line := line + UTF8PadRight(theText, width);
16291529 end;
16301530 end;
16311531
16321532 procedure CollectHeader;
16331533 begin
16341534 AddNext(Column.Title.Caption, Column.Title.Alignment);
16351535 end;
16361536
16371537 procedure CollectField;
16381538 var
16391539 field: TField;
16401540 begin
16411541 field := Column.Field;
16421542
16431543 if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
16441544 AddNext(field.AsString, Column.Alignment)
16451545 else if Field.DataType<>ftBlob then
16461546 AddNext(field.DisplayText, Column.Alignment)
16471547 else
16481548 AddNext('(blob)', Column.Alignment);
16491549 end;
16501550
16511551 procedure LoopColumns(printingRecord: boolean);
16521552 var
16531553 c: TCollectionItem;
16541554 begin
16551555 if (not printingRecord) and pageBreak and (result.count>0) then
16561556 line := #12
16571557 else
16581558 line := '';
16591559 for c in aGrid.Columns do begin
16601560 Column := TColumn(c);
16611561 if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
16621562 if printingRecord then CollectField
16631563 else CollectHeader;
16641564 end;
16651565 end;
16661566 result.add(line);
16671567 end;
16681568
16691569 begin
16701570 result := TStringList.create;
16711571 Ds := aGrid.DataSource.DataSet;
16721572 Ds.DisableControls;
16731573 book := Ds.GetBookmark;
16741574 try
16751575 Ds.First;
16761576 recCount := 0;
16771577 while not DS.EOF do begin
16781578 if RecCount mod lpp = 0 then
16791579 LoopColumns(false);
16801580 LoopColumns(true);
16811581 inc(recCount);
16821582 Ds.Next;
16831583 end;
16841584 finally
16851585 Ds.GotoBookmark(book);
16861586 Ds.FreeBookmark(book);
16871587 Ds.EnableControls;
16881588 end;
16891589 end;
16901590```
16911591
16921592To use it one simply does:
16931593```
16941594var
16951595 L: TStringList;
16961596 begin
16971597 L := GridTextExporter(grid, 80, true);
16981598 L.SaveToFile('grid.txt');
16991599 L.Free;
17001600 end;
17011601```
17021602
17031603---
17041604*From the Free Pascal wiki, page "Grids Reference Page", as the Internet Archive kept it on 2024-12-09, last changed on 2024-07-10. 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