Sign in Join
Wiki copytemporary

Grids Reference Page: what changed

Revision 2 to 3. 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+
1100## Objective
2101
3-This 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:
102+This 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:
4103
5-1. To introduce the grids components to people with little or no previous Delphi experience.
6-2. To document the differences with respect to Delphi grids components.
7-3. To document the new functionality in Lazarus grids.
8-4. Create reference and examples for the components.
104+ 1. To introduce the grids components to people with little or no previous Delphi experience.
105+ 2. To document the differences with respect to Delphi grids components.
106+ 3. To document the new functionality in Lazarus grids.
107+ 4. Create reference and examples for the components.
9108
10109## Overview
11110
12-A 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.
111+A 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.
13112
14-The 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.
113+The 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.
15114
16-Given 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)
115+Given 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>)
17116
18117## Inheritance Tree
19-
20-`                     `[[TCustomControl]]`           `\
21-`                             |                    `\
22-`                             |                    `\
23-`                        TCustomGrid               `\
24-`                             |                    `\
25-`               +-------------+------------+       `\
26-`               |                          |       `\
27-`         TCustomDrawGrid             TCustomDbGrid`\
28-`               |                          |       `\
29-`      +--------+--------+                 |       `\
30-`      |                 |              `[[TDBGrid]]`    `\
31-`  `[[TDrawGrid]]`      TCustomStringGrid                `\
32-`                        | `\
33-`               ---------+----------`\
34-`               |                  |    `\
35-`         `[[TStringGrid]]`      `[[TValueListEditor]]
118+```
119+[TCustomControl](</index.php?title=TCustomControl&action=edit&redlink=1> "TCustomControl \(page does not exist\)")
120+ |
121+ |
122+ TCustomGrid
123+ |
124+ +-------------+------------+
125+ | |
126+ TCustomDrawGrid TCustomDbGrid
127+ | |
128+ +--------+--------+ |
129+ | | [[TDBGrid]]
130+ [[TDrawGrid]] TCustomStringGrid
131+ |
132+ ---------+----------
133+ | |
134+ [[TStringGrid]] [[TValueListEditor]]
135+```
36136
37137## A Starting Example
38138
39-As 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.
139+As 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.
40140
41-1. Create a new application.
42- - From the main menu select: project-\>New Project
43- - In the Create New Project dialog press the "Create Button"
44- - A new empty form will be shown.
45-2. Place a grid on the form
46- - From the component palette select the "additional" tab
47- - Click over the TStringGrid icon ![](/wiki-images/6/67/Tstringgrid.png)
48- - Click over the form, near to the top left corner. A new empty grid appears.
49-3. Place a button on the form
50- - From the component palette select the "Standard" tab
51- - Click over the TButton icon ![](/wiki-images/b/b2/Tbutton.png)
52- - Click over a empty area of the form. A new button appears.
53-4. Doubleclick the button from step 3, and write down the following code in the click button handler:
54- - ``` Pascal
55- StringGrid1.Cells[1,1] := 'Hi World!';
56- ```
57-5. Run the program by clicking the play icon ![](/wiki-images/8/85/Menu_run.png)
58- - 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)
141+ 1. Create a new application.
142+ * From the main menu select: project->New Project
143+ * In the Create New Project dialog press the "Create Button"
144+ * A new empty form will be shown.
145+ 2. Place a grid on the form
146+ * From the component palette select the "additional" tab
147+ * Click over the TStringGrid icon [![tstringgrid.png](/wiki-images/c/c4/tstringgrid.png)](</File:tstringgrid.png>)
148+ * Click over the form, near to the top left corner. A new empty grid appears.
149+ 3. Place a button on the form
150+ * From the component palette select the "Standard" tab
151+ * Click over the TButton icon [![tbutton.png](/wiki-images/b/b2/tbutton.png)](</File:tbutton.png>)
152+ * Click over a empty area of the form. A new button appears.
153+ 4. Doubleclick the button from step 3, and write down the following code in the click button handler:
154+ *
155+```
156+StringGrid1.Cells[1,1] := 'Hi World!';
157+```
59158
159+ 5. Run the program by clicking the play icon [![menu run.png](/wiki-images/0/0d/menu_run.png)](</File:menu_run.png>)
160+ * 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)
161+
60162## Differences between Lazarus and Delphi grids
61163
62-The 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.
164+The 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.
63165
64-At 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.
166+At 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.
65167
66168### Differences
67169
68-Known differences are listed below, in no special order.
170+Known differences are listed below, in no special order.
69171
70-- Cell Editors
71-- Designtime Behaviour
72-- Cell Drawing has some differences, see customizing grid section.
172+ * Cell Editors
173+ * Designtime Behaviour
174+ * Cell Drawing has some differences, see customizing grid section.
73175
74176### New Functionality
75177
76-- Custom Columns
77-- Events
78-- Grid Editor
178+ * Custom Columns
179+ * Events
180+ * Grid Editor
79181
80182### Ways in which you can make a Lazarus grid more Delphi-compatible
81183
82-You 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.
184+You 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.
83185
84-- \[Design\] TitleStyle := tsStandard;
85-- \[Design\] DefaultRowHeight := 24;
86-- \[Code\] EditorBorderStyle := bsNone; // this might work only on Windows.
87-- \[Code\] UseXORFeatures := true;
88-- \[Code\] AllowOutBoundEvents := False; // SVN revision 10992 or later
89-- \[Code\] FastEditing := False; // supported in dbgrid. StringGrid requires SVN revision 10992 or later
90-- \[Design\] AutoAdvance := aaNone;
186+ * [Design] TitleStyle := tsStandard;
187+ * [Design] DefaultRowHeight := 24;
188+ * [Code] EditorBorderStyle := bsNone; // this might work only on Windows.
189+ * [Code] UseXORFeatures := true;
190+ * [Code] AllowOutBoundEvents := False; // SVN revision 10992 or later
191+ * [Code] FastEditing := False; // supported in dbgrid. StringGrid requires SVN revision 10992 or later
192+ * [Design] AutoAdvance := aaNone;
91193
92194## Grids Reference
93195
94196### Information
95197
96-The 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)
198+The 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>)
97199
98-For TCustomDBGrid and TDBgrid it is the [unit DBGrids.pas reference](http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html)
200+For TCustomDBGrid and TDBgrid it is the [unit DBGrids.pas reference](<http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html>)
99201
100-In 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.
202+In 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.
101203
102-TODO: 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)
204+TODO: 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>)
103205
104206### TCustomGrid
105207
106-See the full [[doc:lcl/grids/tcustomgrid.html|TCustomGrid Reference]]
208+See the full [TCustomGrid Reference](<http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomgrid.html> "doc:lcl/grids/tcustomgrid.html")
107209
108210#### property AllowOutboundEvents
109211
110-Protected 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.
212+Protected 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.
111213
112214#### property Columns
113215
114-Lazarus 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.
115-
116-Custom 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**.
216+Lazarus 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.
117217
118-For example, if to a base grid with ColCount := 5 and FixedCols := 2 we:
218+Custom 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**.
119219
120-- Add 3 custom columns, the base grid will be exactly as before, 2 fixed cols and 3 normal cols.
121-- Add 1 custom column, the base grid will have ColCount := 3, that is 2 fixed cols and 1 normal cols.
122-- Add 4 custom columns, the base grid will have ColCount := 6, that is 2 fixed cols and 4 normal cols.
220+For example, if to a base grid with ColCount := 5 and FixedCols := 2 we:
123221
124-From this we can conclude that:
222+ * Add 3 custom columns, the base grid will be exactly as before, 2 fixed cols and 3 normal cols.
223+ * Add 1 custom column, the base grid will have ColCount := 3, that is 2 fixed cols and 1 normal cols.
224+ * Add 4 custom columns, the base grid will have ColCount := 6, that is 2 fixed cols and 4 normal cols.
125225
126-- Fixed column properties or count are not enhanced or modified respectively by custom columns.
127-- **grid.ColCount** is usually different from **grid.Columns.Count** (**grid.ColCount**=**grid.Columns.Count** only when **FixedCols**=0)
226+From this we can conclude that:
128227
129-At 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.
228+ * Fixed column properties or count are not enhanced or modified respectively by custom columns.
229+ * **grid.ColCount** is usually different from **grid.Columns.Count** (**grid.ColCount** =**grid.Columns.Count** only when **FixedCols** =0)
130230
131-At runtime, columns can be modified with code like this:
231+At 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.
132232
133-``` pascal
134- var
135- c: TGridColumn;
136- begin
137- // add a custom column a grid
138- c := Grid.Columns.Add;
139- // modify
140- c.title.caption := 'Price'; // Set columns caption
141- c.align := taRightJustify; // Align column content to the right
142- c.color := clMoneyGreen; // Change default color to clMoneyGreen
143- c.Index := 0; // Make it the first column
144- // access an existing column
145- grid.columns[0].Width := 60; // Change column 0 width to 60 pixels
146- // delete an existing column
147- grid.columns.delete(0); // Delete column 0
148- ....
149- end;
233+At runtime, columns can be modified with code like this:
150234```
235+var
236+ c: TGridColumn;
237+ begin
238+ // add a custom column a grid
239+ c := Grid.Columns.Add;
240+ // modify
241+ c.title.caption := 'Price'; // Set columns caption
242+ c.align := taRightJustify; // Align column content to the right
243+ c.color := clMoneyGreen; // Change default color to clMoneyGreen
244+ c.Index := 0; // Make it the first column
245+ // access an existing column
246+ grid.columns[0].Width := 60; // Change column 0 width to 60 pixels
247+ // delete an existing column
248+ grid.columns.delete(0); // Delete column 0
249+ ....
250+ end;
251+```
151252
152-Additionally, 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.
253+Additionally, 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.
153254
154-Currently there are no plans to make the grids use only custom columns.
255+Currently there are no plans to make the grids use only custom columns.
155256
156257#### property AutoFillColumns
157258
158-Often 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.
259+Often 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.
159260
160-For 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`.
261+For 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`.
161262
162263#### procedure SaveToFile(AFileName: String);
163264
164-Saves the grid to an XML file. The property `SaveOptions` determines what exactly is to be saved:
165-
166-``` Pascal
265+Saves the grid to an XML file. The property `SaveOptions` determines what exactly is to be saved:
266+```
167267type
168- TGridSaveOptions = (
169- soDesign, // Save grid structure (col/row count and Options)
170- soAttributes, // Save grid attributes (Font, Brush, TextStyle)
171- soContent, // Save Grid Content (Text in StringGrid)
172- soPosition // Save Grid cursor and selection position
173- );
174- TSaveOptions = set of TGridSaveOptions;
268+ TGridSaveOptions = (
269+ soDesign, // Save grid structure (col/row count and Options)
270+ soAttributes, // Save grid attributes (Font, Brush, TextStyle)
271+ soContent, // Save Grid Content (Text in StringGrid)
272+ soPosition // Save Grid cursor and selection position
273+ );
274+ TSaveOptions = set of TGridSaveOptions;
175275```
176276
177277#### procedure LoadFromFile(AFileName: String);
178278
179-Loads the XML file created by `SaveToFile`. Again, the `SaveOptions` define what exactly is to be loaded from the file.
279+Loads the XML file created by `SaveToFile`. Again, the `SaveOptions` define what exactly is to be loaded from the file.
180280
181281### TCustomDBGrid
182282
183-TCustomDBGrid is the base for TDBGrid.
283+TCustomDBGrid is the base for TDBGrid.
184284
185-They do not expose Col and Row properties. To go to a certain column, use e.g. the SelectedIndex property.
285+They do not expose Col and Row properties. To go to a certain column, use e.g. the SelectedIndex property.
186286
187-An interesting public method is AutoAdjustColumns.
287+An interesting public method is AutoAdjustColumns.
188288
189289#### procedure AutoAdjustColumns
190290
191-This 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.
291+This 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.
192292
193-However, contrary to TCustomStringGrid.AutoAdjustColumns (see below), this will set your columns very wide unless you have the property dgAutoSizeColumns enabled.
293+However, contrary to TCustomStringGrid.AutoAdjustColumns (see below), this will set your columns very wide unless you have the property dgAutoSizeColumns enabled.
194294
195295#### procedure InplaceEditor
196296
197-See example from bug 23103 - *and insert explanation of what it does/why it is needed. Validate input values? Change what is shown?*
198-
199-``` pascal
200-
297+See example from bug 23103 - _and insert explanation of what it does/why it is needed. Validate input values? Change what is shown?_
298+```
201299procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
202-var
203- S: String;
204-begin
205- if (Key in [',','.']) then
206- begin
207- //unlike Delphi not all InPlaceEditors are editors for string type, so check!
208- if (DBGrid1.InplaceEditor is TStringCellEditor) then
300+ var
301+ S: String;
209302 begin
210- S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
211- if Pos(',',S) > 0 then
212- Key := #0
213- else
214- Key := ',';
303+ if (Key in [',','.']) then
304+ begin
305+ //unlike Delphi not all InPlaceEditors are editors for string type, so check!
306+ if (DBGrid1.InplaceEditor is TStringCellEditor) then
307+ begin
308+ S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
309+ if Pos(',',S) > 0 then
310+ Key := #0
311+ else
312+ Key := ',';
313+ end;
314+ end;
215315 end;
216- end;
217-end;
218316```
219317
220318### TCustomStringGrid
221319
222-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.
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.
223321
224-The following properties or methods are public and are also available to TStringGrid.
322+The following properties or methods are public and are also available to TStringGrid.
225323
226-See the full [[doc:lcl/grids/tcustomstringgrid.html|TCustomStringGrid Reference]]
324+See the full [TCustomStringGrid Reference](<http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomstringgrid.html> "doc:lcl/grids/tcustomstringgrid.html")
227325
228326#### procedure AutoSizeColumn(aCol: Integer);
229327
230-This 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.
328+This 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.
231329
232330#### procedure AutoAdjustColumns or AutoSizeColumns
233331
234-Automatically 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.
332+Automatically 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.
235333
236334#### procedure Clean; overload;
237335
238-Cleans all cells in the grid, fixed or not.
336+Cleans all cells in the grid, fixed or not.
239337
240338#### procedure Clean(CleanOptions: TGridZoneSet); overload;
241339
242-Cleans all cells in the grid subject to the given CleanOptions. See [[doc:lcl/grids/tgridzoneset.html|TGridZoneSet]] for more information. Some examples:
340+Cleans 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:
243341
244-- Clean all cells: grid.Clean(\[\]); (the same as grid.clean)
245-- Clean all non fixed cells: grid.Clean(\[gzNormal\]);
246-- Clean all cells but don't touch grid column headers: Grid.Clean(\[gzNormal, gzFixedRows\]);
342+ * Clean all cells: grid.Clean([]); (the same as grid.clean)
343+ * Clean all non fixed cells: grid.Clean([gzNormal]);
344+ * Clean all cells but don't touch grid column headers: Grid.Clean([gzNormal, gzFixedRows]);
247345
248346#### procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload;
249347
250-does the same as Clean(CleanOptions:TGridZoneSet) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:
348+does the same as Clean(CleanOptions:TGridZoneSet) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:
251349
252-- 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.
350+ * 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.
253351
254352#### procedure Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;
255353
256-The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,\[\]); ==== procedure SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ==== Save grid content to a comma separated values format (CSV) file (added in Lazarus r32179).
354+The 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,[]);
257355
258-The 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.
356+#### procedure SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);
259357
260-ADelimiter (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.
358+Save grid content to a comma separated values format (CSV) file (added in Lazarus r32179).
261359
262-The 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.
360+The 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.
263361
264-If 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.
362+ADelimiter (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.
265363
266-Normal CSV data output will start at the first non-fixed row in the grid.
364+The 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.
267365
268-==== procedure LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ==== Loads grid content from a comma separated values format (CSV) file (added in Lazarus r32179).
366+If 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.
269367
270-Columns 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.
368+Normal CSV data output will start at the first non-fixed row in the grid.
271369
272-The *AFileName* argument specifies the name of the source file with the CSV content.
370+#### procedure LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);
273371
274-*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 ;
372+Loads grid content from a comma separated values format (CSV) file (added in Lazarus r32179).
275373
276-The *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.
374+Columns 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.
277375
278-If 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.
376+The _AFileName_ argument specifies the name of the source file with the CSV content.
279377
280-#### property Cols\[index: Integer\]: TStrings read GetCols write SetCols;
378+_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 ;
281379
282-Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1.
380+The _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.
283381
382+If 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.
383+
384+#### property Cols[index: Integer]: TStrings read GetCols write SetCols;
385+
386+Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1.
387+
284388##### Examples
285389
286-- Set Example: Set the content of the third column in the grid from a ListBox:
390+ * Set Example: Set the content of the third column in the grid from a ListBox:
287391
288-``` pascal
392+```
289393Grid.Cols[2] := ListBox1.Items;
290394```
291395
292-- Get Example: Set the content of a Listbox from the grid's column index 4:
396+ * Get Example: Set the content of a Listbox from the grid's column index 4:
293397
294-``` pascal
398+```
295399procedure TForm1.FillListBox1;
296-var
297- StrTempList: TStringList;
298-begin
299- StrTempList := TStringList(Grid.Cols[4]);
300- if StrTempList<>nil then begin
301- ListBox1.Items.Assign(StrTempList);
302- StrTempList.Free;
303- end;
304-end;
400+ var
401+ StrTempList: TStringList;
402+ begin
403+ StrTempList := TStringList(Grid.Cols[4]);
404+ if StrTempList<>nil then begin
405+ ListBox1.Items.Assign(StrTempList);
406+ StrTempList.Free;
407+ end;
408+ end;
305409```
306410
307411##### Notes.
308412
309-This 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.
413+This 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.
310414
311-This means also that changes in the returned list will not affect the grids content or layout.
415+This means also that changes in the returned list will not affect the grids content or layout.
312416
313-See the Get Example.
417+See the Get Example.
314418
315-#### property Rows\[index: Integer\]: TStrings read GetRows write SetRows;
419+#### property Rows[index: Integer]: TStrings read GetRows write SetRows;
316420
317-Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1.
421+Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1.
318422
319423##### Notes.
320424
321-This 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.
425+This 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.
322426
323-This means also that changes in the returned list will not affect the grid's content or layout.
427+This means also that changes in the returned list will not affect the grid's content or layout.
324428
325429##### Examples
326430
327-- Set Example: Set the content of the third row in the grid from a ListBox:
431+ * Set Example: Set the content of the third row in the grid from a ListBox:
328432
329-``` Pascal
433+```
330434Grid.Rows[2] := ListBox1.Items;
331435```
332436
333-- Get Example: Set the content of a Listbox from the grid's row index 4:
437+ * Get Example: Set the content of a Listbox from the grid's row index 4:
334438
335-``` pascal
439+```
336440procedure TForm1.FillListBox1;
337-var
338- StrTempList: TStringList;
339-begin
340- StrTempList := TStringList(Grid.Rows[4]);
341- if StrTempList<>nil then begin
342- ListBox1.Items.Assign(StrTempList);
343- StrTempList.Free;
344- end;
345-end;
441+ var
442+ StrTempList: TStringList;
443+ begin
444+ StrTempList := TStringList(Grid.Rows[4]);
445+ if StrTempList<>nil then begin
446+ ListBox1.Items.Assign(StrTempList);
447+ StrTempList.Free;
448+ end;
449+ end;
346450```
347451
348-- An Example that doesn't work, and its Fix: Retrieved string list is read only
452+ * An Example that doesn't work, and its Fix: Retrieved string list is read only
349453
350-``` pascal
454+```
351455// this will not work and will cause memory leak
352-// because returned StringList is not being freed
353-Grid.Rows[1].CommaText := '1,2,3,4,5';
354-Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g';
355-
356-// fixing the first case
357-Lst:=TStringList.Create;
358-Lst.CommaText := '1,2,3,4,5';
359-Grid.Rows[1] := Lst;
360-Lst.Free;
456+ // because returned StringList is not being freed
457+ Grid.Rows[1].CommaText := '1,2,3,4,5';
458+ Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g';
459+
460+ // fixing the first case
461+ Lst:=TStringList.Create;
462+ Lst.CommaText := '1,2,3,4,5';
463+ Grid.Rows[1] := Lst;
464+ Lst.Free;
361465```
362466
363467#### property UseXORFeatures;
364468
365-Boolean property, default value: False;
469+Boolean property, default value: False;
366470
367-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]]
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\)")
368472
369-It 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.
473+It 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.
370474
371475### TValueListEditor
372476
373-[[TValueListEditor]] is a control derived from TCustomStringGrid for editing Key-Value pairs.
477+[[TValueListEditor]] is a control derived from TCustomStringGrid for editing Key-Value pairs.
374478
375479#### Property DisplayOptions
376480
377-Controls various aspects of the TValueListEditor's appearance.
481+Controls various aspects of the TValueListEditor's appearance.
378482
379483#### Property TitleCaptions
380484
381-Sets the values of the title captions (if doColumnTitles is in DisplayOptions). If DisplayOptions lacks the value doColumnTitles then default captions are used.
485+Sets the values of the title captions (if doColumnTitles is in DisplayOptions). If DisplayOptions lacks the value doColumnTitles then default captions are used.
382486
383487#### Property Strings
384488
385-Provides access to the list of strings that hold the Key-Value pairs.\
386-Key-Value pairs must be in the form:\
387-'KeyName=Value'
489+Provides access to the list of strings that hold the Key-Value pairs.
490+Key-Value pairs must be in the form:
491+'KeyName=Value'
388492
389493#### Property ItemProps
390494
391-You can use this property to control how the items in the "Value" columns can be edited.\
392-This is controlled by setting the ItemProp's EditStyle and ReadOnly properties.
495+You can use this property to control how the items in the "Value" columns can be edited.
496+This is controlled by setting the ItemProp's EditStyle and ReadOnly properties.
393497
394498#### Property KeyOptions
395499
396-KeyOptions is a set of TKeyOptions controlling whether the user can modify the contents of the "Key" column.
500+KeyOptions is a set of TKeyOptions controlling whether the user can modify the contents of the "Key" column.
397501
398-- KeyEdit: the user can edit the name of the Key
399-- KeyAdd: the user can add keys (by pressing Insert in the grid). KeyAdd requires KeyEdit.
400-- KeyDelete: the user can delete Key-Value pairs (by pressing Ctrl+Delete).
401-- KeyUnique: if set, then Keys must have unique names. Attempting to enter a dulpicate Key will raise an exception.
502+ * KeyEdit: the user can edit the name of the Key
503+ * KeyAdd: the user can add keys (by pressing Insert in the grid). KeyAdd requires KeyEdit.
504+ * KeyDelete: the user can delete Key-Value pairs (by pressing Ctrl+Delete).
505+ * KeyUnique: if set, then Keys must have unique names. Attempting to enter a dulpicate Key will raise an exception.
402506
403507#### Property DropDownRows
404508
405-If the cell editor is a picklist (ValueEdit1.ItemProps\['key1'\].EditStyle=esPickList) this property sets the DropDownCount of the displayed list. The default is 8.
509+If the cell editor is a picklist (ValueEdit1.ItemProps['key1'].EditStyle=esPickList) this property sets the DropDownCount of the displayed list. The default is 8.
406510
407511#### Function DeleteRow
408512
409-Deletes the Key-Value pair of the indexed row removing the row entirely.
513+Deletes the Key-Value pair of the indexed row removing the row entirely.
410514
411515#### Function InsertRow
412516
413-Inserts a row in the grid and sets the Key-Value pair. Returns the index of the newly inserted row.
517+Inserts a row in the grid and sets the Key-Value pair. Returns the index of the newly inserted row.
414518
415519#### Function IsEmptyRow
416520
417-Returns true if the indexed row's cells are empty (Keys\[aRow\]=*; Values\[aRow\]=*).
521+Returns true if the indexed row's cells are empty (Keys[aRow]=_; Values[aRow]=_).
418522
419523#### Function FindRow
420524
421-Returns the row that has the specified key name.
525+Returns the row that has the specified key name.
422526
423527#### Function RestoreCurrentRow
424528
425-Undoes the editing in the current row (if the editor is still focused). Happens when the user presses the Escape key.
529+Undoes the editing in the current row (if the editor is still focused). Happens when the user presses the Escape key.
426530
427531#### Altered behaviour of some properties derived from TCustomStringGrid
428532
429533##### Property Options
430534
431-Due to the nature of TValueListEditor its Options property has certain restrictions
535+Due to the nature of TValueListEditor its Options property has certain restrictions
432536
433-- goColMoving is not allowed in Options (you cannot set it).
434-- goAutoAddRows can only be set if KeyAdd is in KeyOptions. Setting KeyAdd will automatically set goAutoAddRows.
435-- goAutoAddRowsSkipContentCheck is not allowed (for the time being, it causes a crash in TValueListeditor: needs fixing).
537+ * goColMoving is not allowed in Options (you cannot set it).
538+ * goAutoAddRows can only be set if KeyAdd is in KeyOptions. Setting KeyAdd will automatically set goAutoAddRows.
539+ * goAutoAddRowsSkipContentCheck is not allowed (for the time being, it causes a crash in TValueListeditor: needs fixing).
436540
437541##### Property FixedRows
438542
439-Can only be 1 (show column titles) or 0 (don't show column titles).
543+Can only be 1 (show column titles) or 0 (don't show column titles).
440544
441545##### Property ColCount
442546
443-Is always 2.
547+Is always 2.
444548
445549##### Property Objects
446550
447-It is not recommended to use this property at all.\
448-When some (internal) code manipulates the Strings property, changes are Objects will be lost.
551+It is not recommended to use this property at all.
552+When some (internal) code manipulates the Strings property, changes are Objects will be lost.
449553
450554#### General comments on the use of TValueListEditor
451555
452-When manipulating the contents of the ValueListEditor (the grid), you can manipulate the underlying Strings property directly.\
453-Be aware however, that is you do this, you must hide the editor, otherwise the Strings may get out of sync with the contents of the grid you will see on the screen.\
454-
455-If you want to insert or delete rows etc. then the preferred way is to use the public methods from TValueListEditor: DeleteRow(), InsertRow(), MoveRow(), ExchangeRow() or the various Sort() methods.\
456-
457-Trying to use ancestor's methods to manipulate rows or columns (e.g. Columns.Add) might result in a crash.
556+When manipulating the contents of the ValueListEditor (the grid), you can manipulate the underlying Strings property directly.
557+Be aware however, that is you do this, you **must** hide the editor, otherwise the Strings may get out of sync with the contents of the grid you will see on the screen.
558+
559+If 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.
560+
561+Trying to use ancestor's methods to manipulate rows or columns (e.g. Columns.Add) might result in a crash.
458562
459563#### Saving and loading the contenst of a TValueListEditor
460564
461565##### Using the Strings property
462566
463-If 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.\
464-In case of LoadFromFile the `RowCount` property will be autmatically adjusted.\
465-No sanity checks are performed upon LoadFromFile (so lines that do not represent a Key/Value pair will end up as Keys without Value).
567+If 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.
568+In case of LoadFromFile the `RowCount` property will be autmatically adjusted.
569+No sanity checks are performed upon LoadFromFile (so lines that do not represent a Key/Value pair will end up as Keys without Value).
466570
467571##### Using TValueList.SaveToFile and TValueList.LoadFromFile
468572
469-When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.\
470-SaveToFile will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.\
471-
472-In 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.\
473-If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.\
474-
475-TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions \<= 2.1 r62044.
573+When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.
574+SaveToFile will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.
575+
576+In 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.
577+If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.
578+
579+TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions <= 2.1 r62044.
476580
477581##### Using SaveToCSVFile and LoadFromCSV methods
478582
479-These methods should not be used at the moment, since for a TValueListEditor they are flawed.
583+These methods should **not** be used at the moment, since for a TValueListEditor they are flawed.
480584
481585## Working with grids
482586
483587### Customizing grids
484588
485-Grid 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).
589+Grid 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).
486590
487591### Properties and Events for customizing grids
488592
489-Some 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:
593+Some 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:
490594
491-- **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.
492-- **Color.** This sets the primary color used to draw non fixed cells background.
493-- **FixedColor.** This is the color used to draw fixed cells background.
494-- **Flat.** This eliminates the 3d look of fixed cells.\
495-- **TitleFont.** Font used to draw the text in fixed cells.\
496-- **TitleStyle.** This property changes the 3D look of fixed cells, there are 3 settings:
497- - *tsLazarus.* This is the default look
498- - *tsNative.* This tries to set a look that is conforms with the current widgetset theme.
499- - *tsStandard.* This style is a more contrasted look, like Delphi grids.
500-- **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.
501-- **BorderColor.** This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;
502-- **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.\
503-- **FocusColor.** The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.
504-- **FocusRectVisible.** Turns on/off the drawing of focused cell.
505-- **GridLineColor.** Color of grid lines in non fixed area.
506-- **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*.
507-- **SelectedColor.** Color used to draw cell background on selected cells.
508-- **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.
509-- **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.
510-- **AutoAdvance.** Where the cell cursor will go when pressing Enter, or after editing.
511-- **TabAdvance.** Where the cell cursor will go when pressing Tab or Shift-Tab.
512-- **ExtendedColSizing.** If true user can resize columns not just at the headers but along the columns height.
595+ * **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.
596+ * **Color.** This sets the primary color used to draw non fixed cells background.
597+ * **FixedColor.** This is the color used to draw fixed cells background.
598+ * **Flat.** This eliminates the 3d look of fixed cells.
513599
514-Other properties that also affect the grids look.
600+ * **TitleFont.** Font used to draw the text in fixed cells.
515601
516-**Options**.
602+ * **TitleStyle.** This property changes the 3D look of fixed cells, there are 3 settings:
603+ * _tsLazarus._ This is the default look
604+ * _tsNative._ This tries to set a look that is conforms with the current widgetset theme.
605+ * _tsStandard._ This style is a more contrasted look, like Delphi grids.
606+ * **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.
607+ * **BorderColor.** This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;
608+ * **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.
517609
518-
519-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.
610+ * **FocusColor.** The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.
611+ * **FocusRectVisible.** Turns on/off the drawing of focused cell.
612+ * **GridLineColor.** Color of grid lines in non fixed area.
613+ * **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_.
614+ * **SelectedColor.** Color used to draw cell background on selected cells.
615+ * **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.
616+ * **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.
617+ * **AutoAdvance.** Where the cell cursor will go when pressing Enter, or after editing.
618+ * **TabAdvance.** Where the cell cursor will go when pressing Tab or Shift-Tab.
619+ * **ExtendedColSizing.** If true user can resize columns not just at the headers but along the columns height.
520620
521-- **goFixedVertLine, goFixedHorzLine** it draws a vertical or horizontal line respectively delimiting cells or columns in fixed area, active by default.
522-- **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.
523-- **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)
524-- **goRowSelect** select the full row instead of individual cells
525-- **goFixedRowNumbering** if set, grid will do numbering of rows in first fixed column
526-- **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.
527-- **goHeaderPushedLook** if set, this element enables a pushed look when clicking any fixed cell. The zone of "pushable" cells is enabled using HeaderPusedZones property.
621+Other properties that also affect the grids look.
528622
529-(write more)
623+**Options**.
530624
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.
626+
627+ * **goFixedVertLine, goFixedHorzLine** it draws a vertical or horizontal line respectively delimiting cells or columns in fixed area, active by default.
628+ * **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.
629+ * **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)
630+ * **goRowSelect** select the full row instead of individual cells
631+ * **goFixedRowNumbering** if set, grid will do numbering of rows in first fixed column
632+ * **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.
633+ * **goHeaderPushedLook** if set, this element enables a pushed look when clicking any fixed cell. The zone of "pushable" cells is enabled using HeaderPusedZones property.
634+
635+(write more)
636+
531637### Description of grid's drawing process
532638
533-Like 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.
639+Like 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.
534640
535-The process is as follow:
641+The process is as follow:
536642
537-- 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.
538-- 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.
539-- 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:
540- - *gdSelected* The cell will have a selected look.
541- - *gdFocused* The cell will have a focused look.
542- - *gdFixed* Cell have to be painted with fixed cell look.
543- - *gdHot* the mouse is over this cell, so paint it with hot tracking look
544- - *gdPushed* the cell is being clicked, paint it with pushed look
545-- **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:
546-- **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.
547-- **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?...
548-- **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.
549-- **Text.** At this point (only for TStringGrid) if DefaultDrawing property is true, the cell's text content is painted.
550-- **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).
551-- **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.
643+ * 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.
644+ * 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.
645+ * 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:
646+ * _gdSelected_ The cell will have a selected look.
647+ * _gdFocused_ The cell will have a focused look.
648+ * _gdFixed_ Cell have to be painted with fixed cell look.
649+ * _gdHot_ the mouse is over this cell, so paint it with hot tracking look
650+ * _gdPushed_ the cell is being clicked, paint it with pushed look
651+ * **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:
652+ * **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.
653+ * **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?...
654+ * **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.
655+ * **Text.** At this point (only for TStringGrid) if DefaultDrawing property is true, the cell's text content is painted.
656+ * **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).
657+ * **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.
552658
553659#### Differences with Delphi
554660
555-- In Lazarus TCustomGrid.DrawCell method is not abstract and its default implementation does basic cell background filling.
556-- 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)).
661+ * In Lazarus TCustomGrid.DrawCell method is not abstract and its default implementation does basic cell background filling.
662+ * 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>)).
557663
558664### Grid's cell selection
559665
560-The 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.
666+The 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.
561667
562-The 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.
668+The 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.
563669
564-Once 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.
670+Once 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.
565671
566-After 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).
672+After 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).
567673
568-Note 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.
674+Note 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.
569675
570676#### Differences with Delphi
571677
572-- 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.
678+ * 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.
573679
574680### When built-in properties are not enough: derived grids
575681
576-Derived grids usually have to override the following methods:\
577-DrawAllRows: Draws all visible rows.\
578-DrawRow: Draws all cells in a row.\
579-DrawRow draws all cells in the row by first checking if cell is within clipping region, and only draws the cell if it is.\
580-DrawCell:\
581-DrawCellGrid:\
582-DrawCellText:\
583-DrawFocusRect:\
584-(write me).\
682+Derived grids usually have to override the following methods:
683+DrawAllRows: Draws all visible rows.
684+DrawRow: Draws all cells in a row.
685+DrawRow draws all cells in the row by first checking if cell is within clipping region, and only draws the cell if it is.
686+DrawCell:
687+DrawCellGrid:
688+DrawCellText:
689+DrawFocusRect:
690+(write me).
585691
586692### What happens in the `TCustomGrid.Paint` method?
587693
588-The 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`.
694+The 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`.
589695
590-- **`DrawEdges`**: Draws the grid's outer border.
591-- **`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`*.
592- - **`DrawRow`** (virtual): Is called for each row inside the current viewport.
593- - **`DrawCell`** (virtual): Is first called for each 'normal' (i.e. not fixed) cell in the row.
594- - **`PrepareCanvas`** (virtual): Sets the canvas's drawing styles according to the current cell's visual properties.
595- - **`DrawFillRect`**: Draws the cell's background with the styles set in `PrepareCanvas`.
596- - **`DrawCellGrid`** (virtual): Draws the cell's border lines.
597- - **`DrawFocusRect`** (virtual): In `TCustomGrid` this method does nothing. In descendent grids this method is used to draw the focus rectangle inside the active cell.
598- - **`DrawCell`** (virtual): (see above) Is called for each fixed cell in the row's visible viewport.
599-- **`DrawColRowMoving`**: only active while moving a column or row. This method draws the line that indicates the row's/column's new position.
600-- **`DrawBorder`**: If needed (`Flat=TRUE` and `BorderStyle=bsSingle`), this draws an inner border line.
696+ * **`DrawEdges`** : Draws the grid's outer border.
697+ * **`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`_.
698+ * **`DrawRow`** (virtual): Is called for each row inside the current viewport.
699+ * **`DrawCell`** (virtual): Is first called for each 'normal' (i.e. not fixed) cell in the row.
700+ * **`PrepareCanvas`** (virtual): Sets the canvas's drawing styles according to the current cell's visual properties.
701+ * **`DrawFillRect`** : Draws the cell's background with the styles set in `PrepareCanvas`.
702+ * **`DrawCellGrid`** (virtual): Draws the cell's border lines.
703+ * **`DrawFocusRect`** (virtual): In `TCustomGrid` this method does nothing. In descendent grids this method is used to draw the focus rectangle inside the active cell.
704+ * **`DrawCell`** (virtual): (see above) Is called for each fixed cell in the row's visible viewport.
705+ * **`DrawColRowMoving`** : only active while moving a column or row. This method draws the line that indicates the row's/column's new position.
706+ * **`DrawBorder`** : If needed (`Flat=TRUE` and `BorderStyle=bsSingle`), this draws an inner border line.
601707
602708#### Additional methods to draw in `TCustomGrid`
603709
604-These 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.
710+These 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.
605711
606-- **`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`).
607-- **`DrawThemedCell`** (virtual): Is only used for fixed cells and if `TitleStyle=tsNative`. It draws the cell's background using `ThemeServices`.
608-- **`DrawColumnText`** (virtual): Is only used for column header cells.
609- - **`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.
610- - **`DrawCellText`** : (see above)
611-- **`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).
712+ * **`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`).
713+ * **`DrawThemedCell`** (virtual): Is only used for fixed cells and if `TitleStyle=tsNative`. It draws the cell's background using `ThemeServices`.
714+ * **`DrawColumnText`** (virtual): Is only used for column header cells.
715+ * **`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.
716+ * **`DrawCellText`** : (see above)
717+ * **`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).
612718
613719#### Drawing methods introduced by `TCustomDrawGrid`
614720
615-As 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:
721+As 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:
616722
617-- **`DrawCell`** (override):
618- - **`PrepareCanvas`**: calls the inherited method from **`TCustomGrid`**.
619- - **`DefaultDrawCell`** (virtual): This method is only called if the event handler for `OnDrawCell` is NOT assigned.
620- - **`DrawFillRect / DrawThemedCell`** (von `TCustomGrid`): Draws the cell's background. If `TitleStyle=tsNative`, the background of fixed cells is drawn using `DrawThemedCell`.
621- - **`DrawCellAutonumbering`** (virtual): If `goFixedAutonumbering in Options`, row numbers are drawn into the first fixed column using `TCustomGrid.DrawCellText`.
622- - **`DrawColumnText`** (from `TCustomGrid`): Is only called for column header cells (see *Additional methods to draw in `TCustomGrid`*).
623- - **`DrawTextInCell`** (from `TCustomGrid`): Is only called for 'normal' (i.e. not fixed) cells (see *Additional methods to draw in `TCustomGrid`*).
624- - **`DrawCellGrid`** (virtual): Draws the cell's borders
723+ * **`DrawCell`** (override):
724+ * **`PrepareCanvas`** : calls the inherited method from **`TCustomGrid`**.
725+ * **`DefaultDrawCell`** (virtual): This method is only called if the event handler for `OnDrawCell` is NOT assigned.
726+ * **`DrawFillRect / DrawThemedCell`** (von `TCustomGrid`): Draws the cell's background. If `TitleStyle=tsNative`, the background of fixed cells is drawn using `DrawThemedCell`.
727+ * **`DrawCellAutonumbering`** (virtual): If `goFixedAutonumbering in Options`, row numbers are drawn into the first fixed column using `TCustomGrid.DrawCellText`.
728+ * **`DrawColumnText`** (from `TCustomGrid`): Is only called for column header cells (see _Additional methods to draw in` TCustomGrid`_).
729+ * **`DrawTextInCell`** (from `TCustomGrid`): Is only called for 'normal' (i.e. not fixed) cells (see _Additional methods to draw in` TCustomGrid`_).
730+ * **`DrawCellGrid`** (virtual): Draws the cell's borders
625731
626732### Save and Retrieve Grid Content
627733
628-The **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).
629-
630-The 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:
734+The **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).
631735
632-`soDesign:     Save & load ColCount,RowCount,FixedCols,FixedRows,`\
633-`              ColWidths, RowHeights and Options (TCustomGrid)`\
634-`soPosition:   Save & load Scroll position, Row, Col and Selection (TCustomGrid)`\
635-`soAttributes: Save & load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)`\
636-`soContent:    Save & load Text (TCustomStringGrid)`\
637-`soAll:        set of all options (soAll:=[soDesign,soPosition,soAttributes,soContent];)`
736+The 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:
737+```
738+soDesign: Save & load ColCount,RowCount,FixedCols,FixedRows,
739+ ColWidths, RowHeights and Options (TCustomGrid)
740+ soPosition: Save & load Scroll position, Row, Col and Selection (TCustomGrid)
741+ soAttributes: Save & load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)
742+ soContent: Save & load Text (TCustomStringGrid)
743+ soAll: set of all options (soAll:=[soDesign,soPosition,soAttributes,soContent];)
744+```
638745
639-Not 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.
746+Not 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.
640747
641-The soAttributes option is not used in Lazarus standard grids, is there for derived grids.
748+The soAttributes option is not used in Lazarus standard grids, is there for derived grids.
642749
643-When 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.
750+When 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.
644751
645-For a TStringGrid the default SaveOptions value is \[soContent\], for other kind of grids, SaveOptions is the empty set.
752+For a TStringGrid the default SaveOptions value is [soContent], for other kind of grids, SaveOptions is the empty set.
646753
647-**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:
754+**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:
648755
649-------------------------------------------------------------------------
756+* * *
650757
651758**Example:**
652759
653-1. First, go to menu "File -\> New -\> Application";
654-2. Put an empty TStringGrid on the form;
655-3. Put a TButton and TOpenDialog on the form;
656-4. Add the event OnCreate for the form;
657-5. Add the event OnClick for the button.
760+ 1. First, go to menu "File -> New -> Application";
761+ 2. Put an empty TStringGrid on the form;
762+ 3. Put a TButton and TOpenDialog on the form;
763+ 4. Add the event OnCreate for the form;
764+ 5. Add the event OnClick for the button.
658765
659-``` pascal
766+```
660767unit Unit1;
661-
662-{$mode objfpc}{$H+}
663-
664-interface
665-
666-uses
667- Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,
668- Buttons, StdCtrls, XMLCfg;
669-
670-type
671-
672- { TForm1 }
673- TForm1 = class(TForm)
674- StringGrid1: TStringGrid;
675- Button1: TButton;
676- OpenDialog1: TOpenDialog;
677- procedure Button1Click(Sender: TObject);
678- procedure Form1Create(Sender: TObject);
679- private
680- { private declarations }
681- public
682- { public declarations }
683- end;
684-
685-var
686- Form1: TForm1;
687-
688-implementation
689-
690-{ TForm1 }
691-
692-procedure TForm1.Form1Create(Sender: TObject);
693-begin
694- //sets the SaveOptions at creation time of the form
695- stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
696-end;
697-
698-procedure TForm1.Button1Click(Sender: TObject);
699-begin
700- //Ask if thew Execute method of the OpenDialog was launched
701- //when this occurs, the user selects an XML file to Load
702- //wich name was stored in the FileName prop.
703-
704- if opendialog1.Execute then
705- begin
706- //Clear the grid
707- StringGrid1.Clear;
708- //Load the XML
709- StringGrid1.LoadFromFile(OpenDialog1.FileName);
710- //Refresh the Grid
711- StringGrid1.Refresh;
712- end;
713-end;
714-
715-initialization
716- {$I unit1.lrs}
717-
718-end.
768+
769+ {$mode objfpc}{$H+}
770+
771+ interface
772+
773+ uses
774+ Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,
775+ Buttons, StdCtrls, XMLCfg;
776+
777+ type
778+
779+ { TForm1 }
780+ TForm1 = class(TForm)
781+ StringGrid1: TStringGrid;
782+ Button1: TButton;
783+ OpenDialog1: TOpenDialog;
784+ procedure Button1Click(Sender: TObject);
785+ procedure Form1Create(Sender: TObject);
786+ private
787+ { private declarations }
788+ public
789+ { public declarations }
790+ end;
791+
792+ var
793+ Form1: TForm1;
794+
795+ implementation
796+
797+ { TForm1 }
798+
799+ procedure TForm1.Form1Create(Sender: TObject);
800+ begin
801+ //sets the SaveOptions at creation time of the form
802+ stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
803+ end;
804+
805+
806+ procedure TForm1.Button1Click(Sender: TObject);
807+ begin
808+ //Ask if thew Execute method of the OpenDialog was launched
809+ //when this occurs, the user selects an XML file to Load
810+ //wich name was stored in the FileName prop.
811+
812+ if opendialog1.Execute then
813+ begin
814+ //Clear the grid
815+ StringGrid1.Clear;
816+ //Load the XML
817+ StringGrid1.LoadFromFile(OpenDialog1.FileName);
818+ //Refresh the Grid
819+ StringGrid1.Refresh;
820+ end;
821+ end;
822+
823+ initialization
824+ {$I unit1.lrs}
825+
826+ end.
719827```
720828
721-------------------------------------------------------------------------
722-
723-**The sample xml file:** (Copy the text below into a txt file. Don't forget put the xml header :-))
829+* * *
724830
725-``` xml
831+**The sample xml file:** (Copy the text below into a txt file. Don't forget put the xml header :-))
832+```
726833<?xml version="1.0"?>
727-<CONFIG>
728- <grid version="3">
729- <saveoptions create="True" position="True" content="True"/>
730- <design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20">
731- <options>
732- <goFixedVertLine value="True"/>
733- <goFixedHorzLine value="True"/>
734- <goVertLine value="True"/>
735- <goHorzLine value="True"/>
736- <goRangeSelect value="True"/>
737- <goDrawFocusSelected value="False"/>
738- <goRowSizing value="False"/>
739- <goColSizing value="False"/>
740- <goRowMoving value="False"/>
741- <goColMoving value="False"/>
742- <goEditing value="False"/>
743- <goTabs value="False"/>
744- <goRowSelect value="False"/>
745- <goAlwaysShowEditor value="False"/>
746- <goThumbTracking value="False"/>
747- <goColSpanning value="False"/>
748- <goRelaxedRowSelect value="False"/>
749- <goDblClickAutoSize value="False"/>
750- <goSmoothScroll value="True"/>
751- </options>
752- </design>
753- <position topleftcol="1" topleftrow="1" col="1" row="1">
754- <selection left="1" top="1" right="1" bottom="1"/>
755- </position>
756- <content>
757- <cells cellcount="10">
758- <cell1 column="0" row="0" text="Title Col1"/>
759- <cell2 column="0" row="1" text="value(1.1)"/>
760- <cell3 column="0" row="2" text="value(2.1)"/>
761- <cell4 column="0" row="3" text="value(3.1)"/>
762- <cell5 column="0" row="4" text="value(4.1)"/>
763- <cell6 column="1" row="0" text="Title Col2"/>
764- <cell7 column="1" row="1" text="value(1.2)"/>
765- <cell8 column="1" row="2" text="value(2.2)"/>
766- <cell9 column="1" row="3" text="value(3.2)"/>
767- <cell10 column="1" row="4" text="value(4.2)"/>
768- </cells>
769- </content>
770- </grid>
771-</CONFIG>
834+ <CONFIG>
835+ <grid version="3">
836+ <saveoptions create="True" position="True" content="True"/>
837+ <design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20">
838+ <options>
839+ <goFixedVertLine value="True"/>
840+ <goFixedHorzLine value="True"/>
841+ <goVertLine value="True"/>
842+ <goHorzLine value="True"/>
843+ <goRangeSelect value="True"/>
844+ <goDrawFocusSelected value="False"/>
845+ <goRowSizing value="False"/>
846+ <goColSizing value="False"/>
847+ <goRowMoving value="False"/>
848+ <goColMoving value="False"/>
849+ <goEditing value="False"/>
850+ <goTabs value="False"/>
851+ <goRowSelect value="False"/>
852+ <goAlwaysShowEditor value="False"/>
853+ <goThumbTracking value="False"/>
854+ <goColSpanning value="False"/>
855+ <goRelaxedRowSelect value="False"/>
856+ <goDblClickAutoSize value="False"/>
857+ <goSmoothScroll value="True"/>
858+ </options>
859+ </design>
860+ <position topleftcol="1" topleftrow="1" col="1" row="1">
861+ <selection left="1" top="1" right="1" bottom="1"/>
862+ </position>
863+ <content>
864+ <cells cellcount="10">
865+ <cell1 column="0" row="0" text="Title Col1"/>
866+ <cell2 column="0" row="1" text="value(1.1)"/>
867+ <cell3 column="0" row="2" text="value(2.1)"/>
868+ <cell4 column="0" row="3" text="value(3.1)"/>
869+ <cell5 column="0" row="4" text="value(4.1)"/>
870+ <cell6 column="1" row="0" text="Title Col2"/>
871+ <cell7 column="1" row="1" text="value(1.2)"/>
872+ <cell8 column="1" row="2" text="value(2.2)"/>
873+ <cell9 column="1" row="3" text="value(3.2)"/>
874+ <cell10 column="1" row="4" text="value(4.2)"/>
875+ </cells>
876+ </content>
877+ </grid>
878+ </CONFIG>
772879```
773880
774881### Grid Cell Editors
775882
776-The grid uses cell editors to change the content of cells.
883+The grid uses cell editors to change the content of cells.
777884
778-For 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:
885+For 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:
779886
780-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
781-2. if the text in the cell represents a date, popup a calendar so we can choose a specific date easily.
887+ 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
888+ 2. if the text in the cell represents a date, popup a calendar so we can choose a specific date easily.
782889
783-Sometimes 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.
890+Sometimes 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.
784891
785-This 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.
892+This 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.
786893
787894#### Builtin cell editors
788895
789-The 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.
896+The 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.
790897
791-The built-in cell editors are Button, Edit, and Picklist.
898+The built-in cell editors are Button, Edit, and Picklist.
792899
793900#### Using cell editors
794901
795-Users can specify what editor will be used for a cell using one of two methods.
796-
797-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.
798-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).
902+Users can specify what editor will be used for a cell using one of two methods.
799903
800-Setting 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:
904+ 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.
905+ 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).
801906
802-``` pascal
907+Setting 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:
908+```
803909if ColCB< StringGrid1.Columns.Count
804- then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;
910+ then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;
805911```
806912
807913#### Description of editor styles
808914
809-The 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.
810-
811-- **cbsAuto**
812-
813-
814-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.
915+The 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.
815916
816-- **cbsEllipsis**
917+ * **cbsAuto**
817918
818-
819-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.
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.
820920
821-
822-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.
921+ * **cbsEllipsis**
823922
824-
825-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.
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.
826924
827-
828-This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
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.
829926
830-- **cbsNone**
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.
831928
832-
833-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.
929+ This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
834930
835-- **cbsPickList**
931+ * **cbsNone**
836932
837-
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.
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.
839934
840-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\]
841-2. In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList). See here for an example
935+ * **cbsPickList**
842936
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.
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.
844938
845-- **cbsCheckboxColumn**
939+ 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]
940+ 2. In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList). See here for an example
846941
847-
848-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).
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.
849943
850-Getting or setting the value of the checkbox in a cell is done the following way:
944+ * **cbsCheckboxColumn**
851945
852-``` pascal
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:
948+```
853949StringGrid1.Cell[x,y]:='Z';
854950```
855951
856-
857-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.
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.
858953
859-
860-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.
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.
861955
862-
863-The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
956+ The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
864957
865-
866-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.
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.
867959
868-
869-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.
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.
870961
871-- **cbsButtonColumn**
962+ * **cbsButtonColumn**
872963
873-
874-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.
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.
875965
876-
877-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.
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.
878967
879-
880-The button's caption is the corresponding cell string.
968+ The button's caption is the corresponding cell string.
881969
882970### Editing grids
883971
884-The 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.\
885-For 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".
972+The 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.
973+For 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".
886974
887975#### Options and properties that affect editing
888976
889-The 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.\
890-At start the editable state of the grids is different, for TDrawGrid and TStringGrid editing is disabled, for TDbGrid is enabled by default.\
891-The **Options** property has several items that deal with editing, they are described below:\
892-***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.
977+The 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.
978+At start the editable state of the grids is different, for TDrawGrid and TStringGrid editing is disabled, for TDbGrid is enabled by default.
979+The **Options** property has several items that deal with editing, they are described below:
893980
894-- **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.
981+ * **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.
982+ * **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.
895983
896984### Cell hints
897985
898-In 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):
986+In 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):
899987
900-- **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.
901-- **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.
988+ * **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.
989+ * **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.
902990
903-If 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:
991+If 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:
904992
905-- **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
906-- **chpAllNoDefault** displays only the cell hints given by the OnGetCellHint event and by the truncated cells.
907-- **chpTruncOnly** displays only hints for the truncated cells; this is the default settings.
993+ * **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
994+ * **chpAllNoDefault** displays only the cell hints given by the OnGetCellHint event and by the truncated cells.
995+ * **chpTruncOnly** displays only hints for the truncated cells; this is the default settings.
908996
909-In addition, the standard property **ShowHint** of the grid must be set to true in order to show cell hints.
997+In addition, the standard property **ShowHint** of the grid must be set to true in order to show cell hints.
910998
911999## Grids Howto and Examples
9121000
9131001### Focusing a cell
9141002
915-Focusing 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:
916-
917-``` Pascal
1003+Focusing 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:
1004+```
9181005StringGrid1.Row := 9;
919-StringGrid1.Col := 8;
1006+ StringGrid1.Col := 8;
9201007```
9211008
9221009### Example: How to set a custom cell editor
9231010
924-See lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (from laz 1.2)
1011+See lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (from laz 1.2)
9251012
9261013### Example: How to add a button editor
927-
928-``` pascal
1014+```
9291015// Conditionally show button editor in column index 1 or 2 if
930-// cell in column index 1 is empty
931-procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
932- var Editor: TWinControl);
933-begin
934- if (aCol = 1) or (aCol = 2) then
935- if StringGrid1.Cells[1,aRow] = '' then
1016+ // cell in column index 1 is empty
1017+ procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
1018+ var Editor: TWinControl);
9361019 begin
937- Editor := StringGrid1.EditorByStyle(cbsEllipsis);
1020+ if (aCol = 1) or (aCol = 2) then
1021+ if StringGrid1.Cells[1,aRow] = '' then
1022+ begin
1023+ Editor := StringGrid1.EditorByStyle(cbsEllipsis);
1024+ end;
1025+ end;
1026+
1027+ // Triggering Action ...
1028+ procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
1029+ begin
1030+ if StringGrid1.Col = 1 then Showmessage('column 1 editor clicked');
1031+ if StringGrid1.Col = 2 then Showmessage('column 2 editor clicked');
9381032 end;
939- end;
940-
941-// Triggering Action ...
942-procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
943-begin
944- if StringGrid1.Col = 1 then Showmessage('column 1 editor clicked');
945- if StringGrid1.Col = 2 then Showmessage('column 2 editor clicked');
946-end;
9471033```
9481034
9491035### Example: Working with Picklist, How to make it read only and How to fill it at run time
9501036
951-Using 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.
952-
953-``` pascal
1037+Using 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.
1038+```
9541039procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
955- var Editor: TWinControl);
956-begin
957- if aCol=1 then begin
958- if (Editor is TCustomComboBox) then
959- with Editor as TCustomComboBox do begin
960- if (aRow mod 2=0) then
961- Style := csDropDown
962- else
963- Style := csDropDownList;
964- case aRow of
965- 1:
966- Items.CommaText := 'ONE,TWO,THREE,FOUR';
967- 2:
968- Items.CommaText := 'A,B,C,D,E';
969- 3:
970- Items.CommaText := 'MX,ES,NL,UK';
971- 4:
972- Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
973- end;
1040+ var Editor: TWinControl);
1041+ begin
1042+ if aCol=1 then begin
1043+ Editor := StringGrid1.EditorByStyle(cbsPickList);
1044+ if (Editor is TCustomComboBox) then
1045+ with Editor as TCustomComboBox do begin
1046+ if (aRow mod 2=0) then
1047+ Style := csDropDown
1048+ else
1049+ Style := csDropDownList;
1050+ case aRow of
1051+ 1:
1052+ Items.CommaText := 'ONE,TWO,THREE,FOUR';
1053+ 2:
1054+ Items.CommaText := 'A,B,C,D,E';
1055+ 3:
1056+ Items.CommaText := 'MX,ES,NL,UK';
1057+ 4:
1058+ Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
1059+ end;
1060+ end;
9741061 end;
975- end;
976-end;
1062+ end;
9771063```
9781064
9791065### Aligning text in StringGrids
9801066
981-This code shows how to use different text alignments in columns 2 and 3.
982-
983-``` pascal
1067+This code shows how to use different text alignments in columns 2 and 3.
1068+```
9841069procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
985- aState: TGridDrawState);
986-var
987- MyTextStyle: TTextStyle;
988-begin
989- if (aCol=2) or (aCol=3) then
990- begin
991- MyTextStyle := StringGrid1.Canvas.TextStyle;
992- if aCol=2 then
993- MyTextStyle.Alignment := taRightJustify
994- else
995- if aCol=3 then
996- MyTextStyle.Alignment := taCenter;
997- StringGrid1.Canvas.TextStyle := MyTextStyle;
998- end;
999-end;
1070+ aState: TGridDrawState);
1071+ var
1072+ MyTextStyle: TTextStyle;
1073+ begin
1074+ if (aCol=2) or (aCol=3) then
1075+ begin
1076+ MyTextStyle := StringGrid1.Canvas.TextStyle;
1077+ if aCol=2 then
1078+ MyTextStyle.Alignment := taRightJustify
1079+ else
1080+ if aCol=3 then
1081+ MyTextStyle.Alignment := taCenter;
1082+ StringGrid1.Canvas.TextStyle := MyTextStyle;
1083+ end;
1084+ end;
10001085```
10011086
10021087### Multilines in Grids
10031088
1004-This 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.
1005-
1006-``` pascal
1089+This 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.
1090+```
10071091procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
1008- aState: TGridDrawState);
1009-var
1010- MyTextStyle: TTextStyle;
1011-begin
1012- if (aRow=2) or (aCol=3) then
1013- begin
1014- MyTextStyle := StringGrid1.Canvas.TextStyle;
1015- MyTextStyle.SingleLine := false;
1016- StringGrid1.Canvas.TextStyle := MyTextStyle;
1017- end;
1018-end;
1092+ aState: TGridDrawState);
1093+ var
1094+ MyTextStyle: TTextStyle;
1095+ begin
1096+ if (aRow=2) or (aCol=3) then
1097+ begin
1098+ MyTextStyle := StringGrid1.Canvas.TextStyle;
1099+ MyTextStyle.SingleLine := false;
1100+ StringGrid1.Canvas.TextStyle := MyTextStyle;
1101+ end;
1102+ end;
10191103```
10201104
10211105### Validating Entered Values
10221106
1023-Lazarus version 0.9.29 introduces the StringGrid OnValidateEntry event of type TValidateEntryEvent which has the following declaration:
1024-
1025-``` pascal
1107+Lazarus version 0.9.29 introduces the StringGrid OnValidateEntry event of type TValidateEntryEvent which has the following declaration:
1108+```
10261109type
1027- TValidateEntryEvent = procedure(sender: TObject; aCol, aRow: Integer; const OldValue: string; var NewValue: string) of object;
1110+ TValidateEntryEvent = procedure(sender: TObject; aCol, aRow: Integer; const OldValue: string; var NewValue: string) of object;
10281111```
1029-
1030-`aCol,aRow are the cell coordinates of cell being validated.`\
1031-`OldValue is the value that was in cells[aCol,aRow] before editing started.`\
1032-`NewValue is the value that will be finally inserted in cells[aCol,aRow].`
1033-
1034-Because 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.
1112+```
1113+aCol,aRow are the cell coordinates of cell being validated.
1114+ OldValue is the value that was in cells[aCol,aRow] before editing started.
1115+ NewValue is the value that will be finally inserted in cells[aCol,aRow].
1116+```
10351117
1036-Usually 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.
1118+Because 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.
10371119
1038-For example, suppose that cell\[1,1\] should hold only values 'A' or 'B', validation could be made with:
1120+Usually 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.
10391121
1040-``` pascal
1122+For example, suppose that cell[1,1] should hold only values 'A' or 'B', validation could be made with:
1123+```
10411124procedure TForm1.GridValidateEntry(sender: TObject; aCol,
1042- aRow: Integer; const OldValue: string; var NewValue: String);
1043-begin
1044- if (aCol=1) and (aRow=1) then begin
1045- if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
1046- // set a new valid value so user can just press RETURN to continue for example.
1047- NewValue := 'A';
1048- // another option is reverting to previous cell value (which is assumed to be valid)
1049- // NewValue := OldValue;
1050- // Use EAbort rather than another Exception type to avoid spurious error
1051- // dialog boxes and backtraces
1052- raise EAbort.Create('Only A or B are allowed here');
1053- end else begin
1054- // if no exception is raised, it is assumed the value is valid, yet if necessary
1055- // the final value can still be changed by filling NewValue with a different value
1056-
1057- // computer knows better :)
1058- if grid.Cells[1,1]='A' then
1059- NewValue := 'B'
1060- else
1061- NewValue := 'A';
1125+ aRow: Integer; const OldValue: string; var NewValue: String);
1126+ begin
1127+ if (aCol=1) and (aRow=1) then begin
1128+ if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
1129+ // set a new valid value so user can just press RETURN to continue for example.
1130+ NewValue := 'A';
1131+ // another option is reverting to previous cell value (which is assumed to be valid)
1132+ // NewValue := OldValue;
1133+ // Use EAbort rather than another Exception type to avoid spurious error
1134+ // dialog boxes and backtraces
1135+ raise EAbort.Create('Only A or B are allowed here');
1136+ end else begin
1137+ // if no exception is raised, it is assumed the value is valid, yet if necessary
1138+ // the final value can still be changed by filling NewValue with a different value
1139+
1140+ // computer knows better :)
1141+ if grid.Cells[1,1]='A' then
1142+ NewValue := 'B'
1143+ else
1144+ NewValue := 'A';
1145+ end;
1146+ end;
10621147 end;
1063- end;
1064-end;
10651148```
10661149
10671150### Sorting Columns or Rows
10681151
1069-Property 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.
1070-
1071-In code you can use SortColRow() method.
1152+Property 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.
10721153
1073-The first parameter, IsColumn, is a boolean value which indicates:
1154+In code you can use SortColRow() method.
10741155
1075-- True if a column is to be sorted
1076-- False if a row is to be sorted.
1156+The first parameter, IsColumn, is a boolean value which indicates:
10771157
1078-The second parameter, index, is an integer value:
1158+ * True if a column is to be sorted
1159+ * False if a row is to be sorted.
10791160
1080-- Index Indicates Column or Row to be sorted.
1161+The second parameter, index, is an integer value:
10811162
1082-The last two parameters are optional, defines subrange of rows or columns to be sorted.
1163+ * Index Indicates Column or Row to be sorted.
10831164
1084-- FromIndex (From column or row)
1085-- ToIndex (To column or row)
1165+The last two parameters are optional, defines subrange of rows or columns to be sorted.
10861166
1087-If the last two parameters are not specified, the whole column or row is sorted.
1167+ * FromIndex (From column or row)
1168+ * ToIndex (To column or row)
10881169
1089-Sorting uses QuickSort algorithm, it could be changed if a descendant grid overrides the sort() method and calls doCompareCells for cell compare.
1170+If the last two parameters are not specified, the whole column or row is sorted.
10901171
1091-By 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.
1172+Sorting uses QuickSort algorithm, it could be changed if a descendant grid overrides the sort() method and calls doCompareCells for cell compare.
10921173
1093-``` pascal
1174+By 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.
1175+```
10941176// Sort column 3 in ascending order
1095-grid.SortColRow(true, 3);
1096-
1097-// Sort column 3 in descending order, skip fixed rows a top
1098-grid.SortOrder := soDescending; // or soAscending
1099-grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);
1177+ grid.SortColRow(true, 3);
1178+
1179+ // Sort column 3 in descending order, skip fixed rows a top
1180+ grid.SortOrder := soDescending; // or soAscending
1181+ grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);
11001182```
11011183
1102-For custom sorting of numbers, dates, states, etc: StringGrid has the OnCompareCells event which users can handle for example this way:
1103-
1104-``` pascal
1184+For custom sorting of numbers, dates, states, etc: StringGrid has the OnCompareCells event which users can handle for example this way:
1185+```
11051186procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
1106-begin
1107- // Result will be either <0, =0, or >0 for normal order.
1108- result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
1109- // For inverse order, just negate the result (eg. based on grid's SortOrder).
1110- if StringGrid1.SortOrder = soDescending then
1111- result := -result;
1112-end;
1187+ begin
1188+ // Result will be either <0, =0, or >0 for normal order.
1189+ result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
1190+ // For inverse order, just negate the result (eg. based on grid's SortOrder).
1191+ if StringGrid1.SortOrder = soDescending then
1192+ result := -result;
1193+ end;
11131194```
11141195
1115-You can use OnCompareCells also when automatic column sorting is enabled through ColumnClickSorts property.
1196+You can use OnCompareCells also when automatic column sorting is enabled through ColumnClickSorts property.
11161197
11171198### Override automatic sort (ColumnClickSorts set to False). Avoid sort of some columns. Show sort arrows in column header
11181199
1119-(ColumnClickSorts set to False will NOT show sort arrows in column header, so we have to set the arrows.)
1120-
1121-``` pascal
1200+(ColumnClickSorts set to False will NOT show sort arrows in column header, so we have to set the arrows.)
1201+```
11221202procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
1123- Index: Integer);
1124-begin
1125- if isColumn then with StringGrid1 do
1126- begin
1127- col:=Index; // Set column to sort
1128- if Tag = 0 then Tag :=-1 // Suppose data in column is Ascending
1129- else Tag := Tag * -1; // Using StringGrid1.Tag to hold sortorder
1130- // First click of header will sort descending
1131-
1132- // Activate CompareCells (ativate the sort)
1133- case Index of
1134- 0: col:=-1; // Do not sort column 0 and do not show sort arrow
1135- 1: SortColRow(True, Index); // The sort will take place in CompareCells as soon as statement is executed
1136- 2: SortColRow(True, Index);
1137- 3: SortColRow(True, Index);
1138- 4: SortColRow(True, Index);
1139- 5: col:=-1; // Do not sort column 5 and do not show sort arrow
1203+ Index: Integer);
1204+ begin
1205+ if isColumn then with StringGrid1 do
1206+ begin
1207+ col:=Index; // Set column to sort
1208+ if Tag = 0 then Tag :=-1 // Suppose data in column is Ascending
1209+ else Tag := Tag * -1; // Using StringGrid1.Tag to hold sortorder
1210+ // First click of header will sort descending
1211+
1212+ // Activate CompareCells (ativate the sort)
1213+ case Index of
1214+ 0: col:=-1; // Do not sort column 0 and do not show sort arrow
1215+ 1: SortColRow(True, Index); // The sort will take place in CompareCells as soon as statement is executed
1216+ 2: SortColRow(True, Index);
1217+ 3: SortColRow(True, Index);
1218+ 4: SortColRow(True, Index);
1219+ 5: col:=-1; // Do not sort column 5 and do not show sort arrow
1220+ end;
1221+ end;
11401222 end;
1141- end;
1142-end;
1143-
1144-// Compare returns one of the following values:
1145-// if A<B return: -1 ( Ascending )
1146-// if A=B return: 0
1147-// if A>B return: 1 ( Descending)
1148-
1149-// How to sort: Text, Integer, Float, DateTime:
1150-
1151-procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
1152- BRow: Integer; var Result: integer);
1153-
1154- function CompareTextAsText: Integer; // Text
1155- begin
1156- CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
1157- end;
1158-
1159- function CompareTextAsInteger: Integer;
1160- var i1,i2:Integer;
1161- begin
1162- if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and // Integer
1163- TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
1164- Result := CompareValue(i1, i2)
1165- else Exit(0);
1166- end;
1167-
1168- function CompareTextAsFloat: Integer; // Float
1169- var f1,f2:Extended;
1170- begin
1171- if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
1172- TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
1173- Result := CompareValue(f1, f2)
1174- else Exit(0);
1175- end;
1176-
1177- function CompareTextAsDateTime: Integer; // DateTime
1178- var
1179- D1, D2 : TDateTime;
1180- begin
1181- if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
1223+
1224+ // Compare returns one of the following values:
1225+ // if A<B return: -1 ( Ascending )
1226+ // if A=B return: 0
1227+ // if A>B return: 1 ( Descending)
1228+
1229+ // How to sort: Text, Integer, Float, DateTime:
1230+
1231+ procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
1232+ BRow: Integer; var Result: integer);
1233+
1234+ function CompareTextAsText: Integer; // Text
1235+ begin
1236+ CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
1237+ end;
1238+
1239+ function CompareTextAsInteger: Integer;
1240+ var i1,i2:Integer;
1241+ begin
1242+ if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and // Integer
1243+ TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
1244+ Result := CompareValue(i1, i2)
1245+ else Exit(0);
1246+ end;
1247+
1248+ function CompareTextAsFloat: Integer; // Float
1249+ var f1,f2:Extended;
1250+ begin
1251+ if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
1252+ TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
1253+ Result := CompareValue(f1, f2)
1254+ else Exit(0);
1255+ end;
1256+
1257+ function CompareTextAsDateTime: Integer; // DateTime
1258+ var
1259+ D1, D2 : TDateTime;
1260+ begin
1261+ if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
1262+ begin
1263+ if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
1264+ if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
1265+ end
1266+ else Exit(0);
1267+
1268+ if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
1269+ TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
1270+ Result := CompareDateTime(D1, D2)
1271+ else Exit(0);
1272+ end;
1273+
1274+ begin // Don't do anything else here, sort might be done incorrect
1275+ With StringGrid1 do
1276+ begin
1277+ case ACol of
1278+ 0 : ; // Do not sort column 0
1279+ 1 : Result := CompareTextAsText; // Text
1280+ 2 : Result := CompareTextAsInteger; // Integer
1281+ 3 : Result := CompareTextAsFloat; // Float
1282+ 4 : Result := CompareTextAsDateTime; // DateTime
1283+ end; {Case}
1284+ end;
1285+ if StringGrid1.Tag = -1 then Result := -Result; // Switch the sort order
1286+ end;
1287+
1288+ // DrawCell does the trick, previous arrow images are removed:
1289+ procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
1290+ aRect: TRect; aState: TGridDrawState);
1291+
1292+ procedure DrawArrow(ImgIndex:Integer); // Need Up/Down arrows in a ImageList
1293+ begin
1294+ with StringGrid1 do
1295+ TitleImageList.Draw(Canvas, // TitleImageList of StringGrid
1296+ ARect.Right - TitleImageList.Width,
1297+ (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2, // Position of arrow, may be adjusted.
1298+ ImgIndex );
1299+ end;
11821300 begin
1183- if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
1184- if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
1185- end
1186- else Exit(0);
1187-
1188- if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
1189- TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
1190- Result := CompareDateTime(D1, D2)
1191- else Exit(0);
1192- end;
1193-
1194-begin // Don't do anything else here, sort might be done incorrect
1195- With StringGrid1 do
1196- begin
1197- case ACol of
1198- 0 : ; // Do not sort column 0
1199- 1 : Result := CompareTextAsText; // Text
1200- 2 : Result := CompareTextAsInteger; // Integer
1201- 3 : Result := CompareTextAsFloat; // Float
1202- 4 : Result := CompareTextAsDateTime; // DateTime
1203- end; {Case}
1204- end;
1205- if StringGrid1.Tag = -1 then Result := -Result; // Switch the sort order
1206-end;
1207-
1208-// DrawCell does the trick, previous arrow images are removed:
1209-procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
1210- aRect: TRect; aState: TGridDrawState);
1211-
1212- procedure DrawArrow(ImgIndex:Integer); // Need Up/Down arrows in a ImageList
1213- begin
1214- with StringGrid1 do
1215- TitleImageList.Draw(Canvas, // TitleImageList of StringGrid
1216- ARect.Right - TitleImageList.Width,
1217- (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2, // Position of arrow, may be adjusted.
1218- ImgIndex );
1219- end;
1220-begin
1221- if Col > 0 then // In this demo no arrows in col 0
1222- if (ARow = 0) and // Suppose Row 0 as TitleRow
1223- Assigned(TitleImageList) then // Need Up/Down arrows in a ImageList
1224- case Columns.Items[aCol].Tag of // Use Tag of Column as index of Ascending/Descending arrow
1225- -1: DrawArrow(1); // Replace with your Index of down arrow
1226- 0: DrawArrow(-1); // Remove previous image
1227- 1: DrawArrow(2); // Replace with your Index of Up Arrow
1228- end;
1229-end;
1301+ if Col > 0 then // In this demo no arrows in col 0
1302+ if (ARow = 0) and // Suppose Row 0 as TitleRow
1303+ Assigned(TitleImageList) then // Need Up/Down arrows in a ImageList
1304+ case Columns.Items[aCol].Tag of // Use Tag of Column as index of Ascending/Descending arrow
1305+ -1: DrawArrow(1); // Replace with your Index of down arrow
1306+ 0: DrawArrow(-1); // Remove previous image
1307+ 1: DrawArrow(2); // Replace with your Index of Up Arrow
1308+ end;
1309+ end;
12301310```
12311311
12321312### Highlighting the selected cell column and row
12331313
1234-In 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?.
1235-
1236-This 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.
1314+In 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?.
12371315
1238-The 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:
1316+This 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.
12391317
1240-``` pascal
1318+The 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:
1319+```
12411320procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
1242-begin
1243- // we can decide here if we want highlight columns, rows or both
1244- // in this example we highlight both
1245- if Grid.Col<>aCol then
1246- begin
1247- // a change on current column is detected
1248- grid.InvalidateCol(aCol); // invalidate the new selected cell column
1249- grid.InvalidateCol(grid.Col); // invalidate the current (it will be the 'old') selected column
1250- end;
1251- if Grid.Row<>aRow then
1252- begin
1253- grid.InvalidateRow(aRow); // invalidate the new selected cell row
1254- grid.InvalidateRow(grid.Row); // invalidate the current (it will be the 'old') selected row
1255- end;
1256-end;
1257-
1258-procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
1259- aState: TGridDrawState);
1260-begin
1261- if gdFixed in aState then
1262- begin
1263- if (aCol=grid.Col) or (aRow=grid.Row) then
1264- grid.Canvas.Brush.Color := clInactiveCaption; // this would highlight also column or row headers
1265- end else
1266- if gdFocused in aState then begin
1267- // we leave alone the current selected/focused cell
1268- end else
1269- if (aCol=Grid.Col) or (aRow=grid.Row) then
1270- grid.Canvas.Brush.Color := clSkyBlue; // highlight rows and columns with clSkyBlue color
1271-end;
1321+ begin
1322+ // we can decide here if we want highlight columns, rows or both
1323+ // in this example we highlight both
1324+ if Grid.Col<>aCol then
1325+ begin
1326+ // a change on current column is detected
1327+ grid.InvalidateCol(aCol); // invalidate the new selected cell column
1328+ grid.InvalidateCol(grid.Col); // invalidate the current (it will be the 'old') selected column
1329+ end;
1330+ if Grid.Row<>aRow then
1331+ begin
1332+ grid.InvalidateRow(aRow); // invalidate the new selected cell row
1333+ grid.InvalidateRow(grid.Row); // invalidate the current (it will be the 'old') selected row
1334+ end;
1335+ end;
1336+
1337+ procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
1338+ aState: TGridDrawState);
1339+ begin
1340+ if gdFixed in aState then
1341+ begin
1342+ if (aCol=grid.Col) or (aRow=grid.Row) then
1343+ grid.Canvas.Brush.Color := clInactiveCaption; // this would highlight also column or row headers
1344+ end else
1345+ if gdFocused in aState then begin
1346+ // we leave alone the current selected/focused cell
1347+ end else
1348+ if (aCol=Grid.Col) or (aRow=grid.Row) then
1349+ grid.Canvas.Brush.Color := clSkyBlue; // highlight rows and columns with clSkyBlue color
1350+ end;
12721351```
12731352
12741353### Avoiding the empty space at the right of the last column
12751354
1276-The 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:
1355+The 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:
12771356
1278-- Set `AutoFillColumns` to `true`.
1279-- Using the Columns editor, add as many columns as you need.
1280-- Select each column in the object tree above the object inspector and set its `SizePriority` to `0`.
1281-- Now decide while column is widened such that all empty space is used up. Set the `SizePriority` of this column to `1`.
1357+ * Set `AutoFillColumns` to `true`.
1358+ * Using the Columns editor, add as many columns as you need.
1359+ * Select each column in the object tree above the object inspector and set its `SizePriority` to `0`.
1360+ * Now decide while column is widened such that all empty space is used up. Set the `SizePriority` of this column to `1`.
12821361
12831362### How to scroll a Grid in "realtime" (moving the scrollbar via mouse)
12841363
1285-If 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`.
1286-
1287-Example:
1364+If 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`.
12881365
1289-``` pascal
1290- StringGrid1.Options:=StringGrid1.Options + [goThumbTracking];
1366+Example:
12911367```
1368+StringGrid1.Options:=StringGrid1.Options + [goThumbTracking];
1369+```
12921370
12931371Unfortunately 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>
12941372
12951373### How to resize a row based on cells content
12961374
1297-Lacak in bug report [\#38473](https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/38473) contributed the following code:
1298-
1299-``` pascal
1300-
1375+Lacak in bug report [#38473](<https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/38473>) contributed the following code:
1376+```
13011377type
1302-{ TStringGridHelper }
1303- TStringGridHelper = class helper for TStringGrid
1304- protected
1305- procedure AutoSizeRow(aRow: integer);
1306- end;
1307-
1308-{ TStringGridHelper }
1309-procedure TStringGridHelper.AutoSizeRow(aRow: integer);
1310-var
1311- aCanvas: TCanvas;
1312- aCol, maxRowHeight:integer;
1313- aText: String;
1314- textRect: TRect;
1315-begin
1316- aCanvas := GetWorkingCanvas(Canvas);
1317- maxRowHeight := DefaultRowHeight;
1318- for aCol:=0 to ColCount-1 do begin
1319- aText := Cells[aCol,aRow];
1320- textRect := Rect(0, 0, ColWidths[aCol], MaxInt);
1321- DrawText(aCanvas.Handle, PChar(aText), Length(aText), textRect, DT_CALCRECT or DT_WORDBREAK);
1322- if maxRowHeight < textRect.Height then
1323- maxRowHeight := textRect.Height
1324- end;
1325- if aCanvas<>Canvas then FreeWorkingCanvas(aCanvas);
1326- RowHeights[aRow] := maxRowHeight+2;
1327-end;
1378+ { TStringGridHelper }
1379+ TStringGridHelper = class helper for TStringGrid
1380+ protected
1381+ procedure AutoSizeRow(aRow: integer);
1382+ end;
1383+
1384+ { TStringGridHelper }
1385+ procedure TStringGridHelper.AutoSizeRow(aRow: integer);
1386+ var
1387+ aCanvas: TCanvas;
1388+ aCol, maxRowHeight:integer;
1389+ aText: String;
1390+ textRect: TRect;
1391+ begin
1392+ aCanvas := GetWorkingCanvas(Canvas);
1393+ maxRowHeight := DefaultRowHeight;
1394+ for aCol:=0 to ColCount-1 do begin
1395+ aText := Cells[aCol,aRow];
1396+ textRect := Rect(0, 0, ColWidths[aCol], MaxInt);
1397+ DrawText(aCanvas.Handle, PChar(aText), Length(aText), textRect, DT_CALCRECT or DT_WORDBREAK);
1398+ if maxRowHeight < textRect.Height then
1399+ maxRowHeight := textRect.Height
1400+ end;
1401+ if aCanvas<>Canvas then FreeWorkingCanvas(aCanvas);
1402+ RowHeights[aRow] := maxRowHeight+2;
1403+ end;
13281404```
13291405
1330-Add that somewhere in your source code and then you can resize a row as if the AutoSizeRow procedure is part of the stringgrid, example:
1331-
1332- // Resize the row index 2
1406+Add that somewhere in your source code and then you can resize a row as if the AutoSizeRow procedure is part of the stringgrid, example:
1407+```
1408+// Resize the row index 2
13331409 grid.AutoSizeRow(2);
1410+```
13341411
13351412## DBGrids Howto and Examples
13361413
13371414### Example: How to set a memo editor for dbgrids
13381415
1339-You 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))
1416+You 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>))
13401417
1341-- 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.
1342-- In the OnSelectEditor event put the following code - adapt if you don't want to edit logical column 3, physical column 4:
1418+ * 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.
1419+ * In the OnSelectEditor event put the following code - adapt if you don't want to edit logical column 3, physical column 4:
13431420
1344-``` pascal
1345- if (Column.DesignIndex = 3) then
1346- begin
1347- GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
1348- GridCellMemo.Text:=Column.Field.AsString;
1349- Editor := GridCellMemo;
1350- end;
13511421```
1352-
1353-- Suppose your datasource is called Datasource1 and DBGrid is called ResultsGrid. Then, in the OnEditingDone event for the GridCellMemo put the following:
1422+if (Column.DesignIndex = 3) then
1423+ begin
1424+ GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
1425+ GridCellMemo.Text:=Column.Field.AsString;
1426+ Editor := GridCellMemo;
1427+ end;
1428+```
13541429
1355-(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.)
1430+ * Suppose your datasource is called Datasource1 and DBGrid is called ResultsGrid. Then, in the OnEditingDone event for the GridCellMemo put the following:
13561431
1357-``` pascal
1358- if not(Datasource1.State in [dsEdit, dsInsert]) then
1359- Datasource1.Edit;
1360-
1361- Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
1432+(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.)
13621433```
1434+if not(Datasource1.State in [dsEdit, dsInsert]) then
1435+ Datasource1.Edit;
1436+
1437+ Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
1438+```
13631439
1364-**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).
1440+**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>).
13651441
1366-**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.
1442+**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.
13671443
13681444### Example: Avoid displaying text fields as "(Memo)" for DBGrids
13691445
13701446now (08/2023) exist the dbgrid option dgDisplayMemoText in object inspector - option
13711447
1372-When 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.
1373-
1374-``` pascal
1375- var
1376- sql : UTF8String;
1377- Queryu : TSQLQuery;
1378-.....
1379- sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
1380- Queryu.SQL.Text:=sql;
1448+
1449+When 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.
13811450```
1451+var
1452+ sql : UTF8String;
1453+ Queryu : TSQLQuery;
1454+ .....
1455+ sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
1456+ Queryu.SQL.Text:=sql;
1457+```
13821458
13831459
1384-for SQLITE3, to avoid cast function usage, just give len for fields in your sqlite database :\
1385-::- when you import from csv or create table : create table mytable (myfield TEXT (10))\
1386-::- or using sqlstudio or other to give len to fields
13871460
1388-### Sorting columns or rows in DBGrid with sort arrows in column header
1461+ for SQLITE3, to avoid cast function usage, just give len for fields in your sqlite database :
13891462
1390-Here 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.
1463+
13911464
1392-**Prerequisites:**
1465+ \- when you import from csv or create table : create table mytable (myfield TEXT (10))
13931466
1394-- You will need an imagelist to store the up/down sort arrows. Assign your imagelist to the dbgrid's **TitleImageList** property.
1395-- Ensure the **[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]** is large enough to hold the new indexes you will be creating. For this example I had it set to 100.
1396-- You will also need a private var to store the last column used to sort, for this example I have called it **FLastColumn**.
1467+ \- or using sqlstudio or other to give len to fields
13971468
1398-For this example the **TSQLQuery** used is called OpenQuery.
1469+### Sorting columns or rows in DBGrid with sort arrows in column header
13991470
1400-> **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.
1471+Here 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.
14011472
1402-In 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.
1473+**Prerequisites:**
14031474
1404-``` pascal
1405-FLastColumn: TColumn; //store last grid column we sorted on
1475+ * You will need an imagelist to store the up/down sort arrows. Assign your imagelist to the dbgrid's **TitleImageList** property.
1476+ * 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.
1477+ * You will also need a private var to store the last column used to sort, for this example I have called it **FLastColumn**.
14061478
1407-procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
1408-const
1409- ImageArrowUp=0; //should match image in imagelist
1410- ImageArrowDown=1; //should match image in imagelist
1411-var
1412- ASC_IndexName, DESC_IndexName:string;
1413- procedure UpdateIndexes;
1414- begin
1415- // Ensure index defs are up to date
1416- OpenQuery.IndexDefs.Updated:=false; {<<<--This line is critical. IndexDefs.Update will not
1417- update if already true, which will happen on the first column sorted.}
1418- Openquery.IndexDefs.Update;
1419- end;
1420-begin
1421- ASC_IndexName:='ASC_'+Column.FieldName;
1422- DESC_IndexName:='DESC_'+Column.FieldName;
1423- // indexes can't sort binary types such as ftMemo, ftBLOB
1424- if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
1425- exit;
1426- // check if an ascending index already exists for this column.
1427- // if not, create one
1428- if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
1429- begin
1430- OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
1431- UpdateIndexes; //ensure index defs are up to date
1432- end;
1433- // Check if a descending index already exists for this column
1434- // if not, create one
1435- if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
1436- begin
1437- OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
1438- UpdateIndexes; //ensure index defs are up to date
1439- end;
1479+For this example the **TSQLQuery** used is called OpenQuery.
14401480
1441- // Use the column tag to toggle ASC/DESC
1442- column.tag := not column.tag;
1443- if boolean(column.tag) then
1444- begin
1445- Column.Title.ImageIndex:=ImageArrowUp;
1446- Openquery.IndexName:=ASC_IndexName;
1447- end
1448- else
1449- begin
1450- Column.Title.ImageIndex:=ImageArrowDown;
1451- OpenQuery.IndexName:=DESC_IndexName;
1452- end;
1453- // Remove the sort arrow from the previous column we sorted
1454- if (FLastColumn <> nil) and (FlastColumn <> Column) then
1455- FLastColumn.Title.ImageIndex:=-1;
1456- FLastColumn:=column;
1457-end;
1481+![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.
1482+
1483+In 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.
14581484```
1485+FLastColumn: TColumn; //store last grid column we sorted on
1486+
1487+ procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
1488+ const
1489+ ImageArrowUp=0; //should match image in imagelist
1490+ ImageArrowDown=1; //should match image in imagelist
1491+ var
1492+ ASC_IndexName, DESC_IndexName:string;
1493+ procedure UpdateIndexes;
1494+ begin
1495+ // Ensure index defs are up to date
1496+ OpenQuery.IndexDefs.Updated:=false; {<<<--This line is critical. IndexDefs.Update will not
1497+ update if already true, which will happen on the first column sorted.}
1498+ Openquery.IndexDefs.Update;
1499+ end;
1500+ begin
1501+ ASC_IndexName:='ASC_'+Column.FieldName;
1502+ DESC_IndexName:='DESC_'+Column.FieldName;
1503+ // indexes can't sort binary types such as ftMemo, ftBLOB
1504+ if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
1505+ exit;
1506+ // check if an ascending index already exists for this column.
1507+ // if not, create one
1508+ if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
1509+ begin
1510+ OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
1511+ UpdateIndexes; //ensure index defs are up to date
1512+ end;
1513+ // Check if a descending index already exists for this column
1514+ // if not, create one
1515+ if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
1516+ begin
1517+ OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
1518+ UpdateIndexes; //ensure index defs are up to date
1519+ end;
1520+
1521+ // Use the column tag to toggle ASC/DESC
1522+ column.tag := not column.tag;
1523+ if boolean(column.tag) then
1524+ begin
1525+ Column.Title.ImageIndex:=ImageArrowUp;
1526+ Openquery.IndexName:=ASC_IndexName;
1527+ end
1528+ else
1529+ begin
1530+ Column.Title.ImageIndex:=ImageArrowDown;
1531+ OpenQuery.IndexName:=DESC_IndexName;
1532+ end;
1533+ // Remove the sort arrow from the previous column we sorted
1534+ if (FLastColumn <> nil) and (FlastColumn <> Column) then
1535+ FLastColumn.Title.ImageIndex:=-1;
1536+ FLastColumn:=column;
1537+ end;
1538+```
14591539
14601540### Selecting Records in a DBGrid using checkboxes
14611541
1462-The 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.
1463-
1464-But 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.
1542+The 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.
14651543
1466-Note this technique needs Lazarus r31148 or later which implements event OnUserCheckboxState.
1544+But 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.
14671545
1468-``` pascal
1546+Note this technique needs Lazarus r31148 or later which implements event OnUserCheckboxState.
1547+```
14691548...
1470-uses ..., dbgrids, stdctrls, ...
1471-
1472-type
1473-
1474- { TForm1 }
1475-
1476- TForm1 = class(TForm)
1477- ...
1478- procedure DBGrid1CellClick(Column: TColumn);
1479- procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
1480- procedure FormCreate(Sender: TObject);
1481- procedure FormDestroy(Sender: TObject);
1482- ...
1483- private
1484- RecList: TBookmarklist;
1485- ...
1486- end;
1487-
1488-procedure TForm1.DBGrid1CellClick(Column: TColumn);
1489-begin
1490- if Column.Index=1 then
1491- RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
1492-end;
1493-
1494-procedure TForm1.FormCreate(Sender: TObject);
1495-begin
1496- RecList := TBookmarkList.Create(DbGrid1);
1497-end;
1498-
1499-procedure TForm1.FormDestroy(Sender: TObject);
1500-begin
1501- RecList.Free;
1502-end;
1503-
1504-procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
1505-begin
1506- if RecList.CurrentRowSelected then
1507- AState := cbChecked
1508- else
1509- AState := cbUnchecked;
1510-end;
1549+ uses ..., dbgrids, stdctrls, ...
1550+
1551+ type
1552+
1553+ { TForm1 }
1554+
1555+ TForm1 = class(TForm)
1556+ ...
1557+ procedure DBGrid1CellClick(Column: TColumn);
1558+ procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
1559+ procedure FormCreate(Sender: TObject);
1560+ procedure FormDestroy(Sender: TObject);
1561+ ...
1562+ private
1563+ RecList: TBookmarklist;
1564+ ...
1565+ end;
1566+
1567+ procedure TForm1.DBGrid1CellClick(Column: TColumn);
1568+ begin
1569+ if Column.Index=1 then
1570+ RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
1571+ end;
1572+
1573+ procedure TForm1.FormCreate(Sender: TObject);
1574+ begin
1575+ RecList := TBookmarkList.Create(DbGrid1);
1576+ end;
1577+
1578+ procedure TForm1.FormDestroy(Sender: TObject);
1579+ begin
1580+ RecList.Free;
1581+ end;
1582+
1583+ procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
1584+ begin
1585+ if RecList.CurrentRowSelected then
1586+ AState := cbChecked
1587+ else
1588+ AState := cbUnchecked;
1589+ end;
15111590```
15121591
15131592### Example: How to export a DBGrid to a text file
15141593
1515-There 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.
1516-
1517-``` pascal
1594+There 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.
1595+```
15181596// aGrid: The grid to be exported
1519-// lpp: Lines per page. Not including the text header
1520-// pageBreak: insert a page break that some printers could use for starting a new page
1521-// require: uses LazUTF8
1522-function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
1523-const
1524- PX_PER_CHAR = 7;
1525-
1526-var
1527- Ds: TDataset;
1528- book: TBookMark;
1529- recCount: Integer;
1530- line: string;
1531- column: TColumn;
1532-
1533- function WidthToChar(aWidth: Integer): integer;
1534- begin
1535- result := trunc(aWidth/PX_PER_CHAR);
1536- end;
1537-
1538- procedure AddNext(theText: string; alignment: TAlignment);
1539- var
1540- width: Integer;
1541- begin
1542- if (line<>'') and (line<>#12) then
1543- line := line + ' ';
1544-
1545- width := WidthToChar(Column.Width);
1546-
1547- case Alignment of
1548- taRightJustify: line := line + UTF8PadLeft(theText, width);
1549- taCenter: line := line + UTF8PadCenter(theText, width);
1550- else line := line + UTF8PadRight(theText, width);
1551- end;
1552- end;
1553-
1554- procedure CollectHeader;
1555- begin
1556- AddNext(Column.Title.Caption, Column.Title.Alignment);
1557- end;
1558-
1559- procedure CollectField;
1560- var
1561- field: TField;
1562- begin
1563- field := Column.Field;
1564-
1565- if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
1566- AddNext(field.AsString, Column.Alignment)
1567- else if Field.DataType<>ftBlob then
1568- AddNext(field.DisplayText, Column.Alignment)
1569- else
1570- AddNext('(blob)', Column.Alignment);
1571- end;
1572-
1573- procedure LoopColumns(printingRecord: boolean);
1574- var
1575- c: TCollectionItem;
1576- begin
1577- if (not printingRecord) and pageBreak and (result.count>0) then
1578- line := #12
1579- else
1580- line := '';
1581- for c in aGrid.Columns do begin
1582- Column := TColumn(c);
1583- if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
1584- if printingRecord then CollectField
1585- else CollectHeader;
1597+ // lpp: Lines per page. Not including the text header
1598+ // pageBreak: insert a page break that some printers could use for starting a new page
1599+ // require: uses LazUTF8
1600+ function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
1601+ const
1602+ PX_PER_CHAR = 7;
1603+
1604+ var
1605+ Ds: TDataset;
1606+ book: TBookMark;
1607+ recCount: Integer;
1608+ line: string;
1609+ column: TColumn;
1610+
1611+ function WidthToChar(aWidth: Integer): integer;
1612+ begin
1613+ result := trunc(aWidth/PX_PER_CHAR);
15861614 end;
1587- end;
1588- result.add(line);
1589- end;
1590-
1591-begin
1592- result := TStringList.create;
1593- Ds := aGrid.DataSource.DataSet;
1594- Ds.DisableControls;
1595- book := Ds.GetBookmark;
1596- try
1597- Ds.First;
1598- recCount := 0;
1599- while not DS.EOF do begin
1600- if RecCount mod lpp = 0 then
1601- LoopColumns(false);
1602- LoopColumns(true);
1603- inc(recCount);
1604- Ds.Next;
1615+
1616+ procedure AddNext(theText: string; alignment: TAlignment);
1617+ var
1618+ width: Integer;
1619+ begin
1620+ if (line<>'') and (line<>#12) then
1621+ line := line + ' ';
1622+
1623+ width := WidthToChar(Column.Width);
1624+
1625+ case Alignment of
1626+ taRightJustify: line := line + UTF8PadLeft(theText, width);
1627+ taCenter: line := line + UTF8PadCenter(theText, width);
1628+ else line := line + UTF8PadRight(theText, width);
1629+ end;
1630+ end;
1631+
1632+ procedure CollectHeader;
1633+ begin
1634+ AddNext(Column.Title.Caption, Column.Title.Alignment);
1635+ end;
1636+
1637+ procedure CollectField;
1638+ var
1639+ field: TField;
1640+ begin
1641+ field := Column.Field;
1642+
1643+ if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
1644+ AddNext(field.AsString, Column.Alignment)
1645+ else if Field.DataType<>ftBlob then
1646+ AddNext(field.DisplayText, Column.Alignment)
1647+ else
1648+ AddNext('(blob)', Column.Alignment);
1649+ end;
1650+
1651+ procedure LoopColumns(printingRecord: boolean);
1652+ var
1653+ c: TCollectionItem;
1654+ begin
1655+ if (not printingRecord) and pageBreak and (result.count>0) then
1656+ line := #12
1657+ else
1658+ line := '';
1659+ for c in aGrid.Columns do begin
1660+ Column := TColumn(c);
1661+ if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
1662+ if printingRecord then CollectField
1663+ else CollectHeader;
1664+ end;
1665+ end;
1666+ result.add(line);
1667+ end;
1668+
1669+ begin
1670+ result := TStringList.create;
1671+ Ds := aGrid.DataSource.DataSet;
1672+ Ds.DisableControls;
1673+ book := Ds.GetBookmark;
1674+ try
1675+ Ds.First;
1676+ recCount := 0;
1677+ while not DS.EOF do begin
1678+ if RecCount mod lpp = 0 then
1679+ LoopColumns(false);
1680+ LoopColumns(true);
1681+ inc(recCount);
1682+ Ds.Next;
1683+ end;
1684+ finally
1685+ Ds.GotoBookmark(book);
1686+ Ds.FreeBookmark(book);
1687+ Ds.EnableControls;
1688+ end;
16051689 end;
1606- finally
1607- Ds.GotoBookmark(book);
1608- Ds.FreeBookmark(book);
1609- Ds.EnableControls;
1610- end;
1611-end;
16121690```
16131691
1614-To use it one simply does:
1615-
1616-``` pascal
1692+To use it one simply does:
1693+```
16171694var
1618- L: TStringList;
1619-begin
1620- L := GridTextExporter(grid, 80, true);
1621- L.SaveToFile('grid.txt');
1622- L.Free;
1623-end;
1695+ L: TStringList;
1696+ begin
1697+ L := GridTextExporter(grid, 80, true);
1698+ L.SaveToFile('grid.txt');
1699+ L.Free;
1700+ end;
16241701```
16251702
16261703---
1627-*From the Free Pascal wiki, page "Grids Reference Page", as it stood on 2023-12-06 after a change by Wp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
1704+*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