-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathFMX.MultiHeaderGrid.pas
More file actions
9812 lines (8584 loc) · 349 KB
/
Copy pathFMX.MultiHeaderGrid.pas
File metadata and controls
9812 lines (8584 loc) · 349 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
unit FMX.MultiHeaderGrid;
interface
uses
System.Classes, System.Types, System.UITypes, System.Generics.Collections, System.Generics.Defaults,
FMX.Types, FMX.Controls, FMX.Graphics, FMX.StdCtrls, FMX.Objects, FMX.Layouts,
FMX.Memo, FMX.ListBox, FMX.DateTimeCtrls, FMX.DateTimeCtrls.Types, FMX.Edit, FMX.EditBox,
FMX.NumberBox, FMX.Text, Data.DB;
type
THeaderLevel = class;
THeaderLevels = class;
// Kind of inplace editor the DB grid uses for a column, selected from the
// bound field's DataType / FieldKind. See TMultiHeaderDBGrid.EditorKindForField.
TMHGEditorKind = (
ekNone, // not editable (binary/structured/calculated/read-only)
ekMemo, // TMemo - text fields, value round-trips via Field.AsString
ekNumber, // TEdit - numeric text (holds '' for SQL NULL)
ekCheckBox, // ftBoolean - toggled in place (no editor control)
ekComboBox, // TComboBox - lookup fields (fkLookup)
ekDate, // TDateEdit - ftDate
ekTime, // TTimeEdit - ftTime
ekDateTime // TDateEdit + TTimeEdit composite - ftDateTime/ftTimeStamp
);
// Per-column choice of how a datetime-typed field is edited. Lets the
// column author override the default (composite date+time) editor.
// dteDateTime : date + time composite (default for datetime fields)
// dteDate : date only (time component left untouched on commit)
TMHGDateTimeEditKind = (
dteDateTime,
dteDate
);
TCellStyle = record
strict private
FFontStyle : TFontStyles;
FFontSize : Single;
FCellColor : TAlphaColor;
FFontName : string;
FFontColor : TAlphaColor;
FTextVAlignment : TTextAlign;
FTextHAlignment : TTextAlign;
FSelectedCellColor : TAlphaColor;
FSelectedFontColor : TAlphaColor;
FWordWrap : Boolean;
FFontStyleIsSet : Boolean;
FFontSizeIsSet : Boolean;
FCellColorIsSet : Boolean;
FFontNameIsSet : Boolean;
FFontColorIsSet : Boolean;
FTextVAlignmentIsSet : Boolean;
FTextHAlignmentIsSet : Boolean;
FSelectedCellColorIsSet : Boolean;
FSelectedFontColorIsSet : Boolean;
FIsMergedCell : Boolean;
FWordWrapIsSet : Boolean;
FParentCol : Integer;
FParentRow : Integer;
procedure SetCellColor(const Value: TAlphaColor);
procedure SetFontColor(const Value: TAlphaColor);
procedure SetFontName(const Value: string);
procedure SetFontSize(const Value: Single);
procedure SetFontStyle(const Value: TFontStyles);
procedure SetSelectedCellColor(const Value: TAlphaColor);
procedure SetSelectedFontColor(const Value: TAlphaColor);
procedure SetParentCol(const Value: Integer);
procedure SetParentRow(const Value: Integer);
procedure SetCellColorIsSet(const Value: Boolean);
procedure SetFontColorIsSet(const Value: Boolean);
procedure SetFontNameIsSet(const Value: Boolean);
procedure SetFontSizeIsSet(const Value: Boolean);
procedure SetFontStyleIsSet(const Value: Boolean);
procedure SetSelectedCellColorIsSet(const Value: Boolean);
procedure SetSelectedFontColorIsSet(const Value: Boolean);
procedure SetTextHAlignment(const Value: TTextAlign);
procedure SetTextHAlignmentIsSet(const Value: Boolean);
procedure SetTextVAlignment(const Value: TTextAlign);
procedure SetTextVAlignmentIsSet(const Value: Boolean);
private
procedure SetWordWrap(const Value: Boolean);
procedure SetWordWrapIsSet(const Value: Boolean);
public
class operator Initialize(out Dest: TCellStyle);
function IsEmpty: boolean;
property IsMergedCell: Boolean read FIsMergedCell;
procedure SetMergedCell(ParenCol,ParenRow: integer);
procedure ClearMergedCell;
property FontName: string read FFontName write SetFontName;
property FontSize: Single read FFontSize write SetFontSize;
property FontStyle: TFontStyles read FFontStyle write SetFontStyle;
property FontColor: TAlphaColor read FFontColor write SetFontColor;
property CellColor: TAlphaColor read FCellColor write SetCellColor;
property SelectedFontColor: TAlphaColor read FSelectedFontColor write SetSelectedFontColor;
property SelectedCellColor: TAlphaColor read FSelectedCellColor write SetSelectedCellColor;
property TextVAlignment: TTextAlign read FTextVAlignment write SetTextVAlignment;
property TextHAlignment: TTextAlign read FTextHAlignment write SetTextHAlignment;
property FontNameIsSet: Boolean read FFontNameIsSet write SetFontNameIsSet;
property FontSizeIsSet: Boolean read FFontSizeIsSet write SetFontSizeIsSet;
property FontStyleIsSet: Boolean read FFontStyleIsSet write SetFontStyleIsSet;
property FontColorIsSet: Boolean read FFontColorIsSet write SetFontColorIsSet;
property CellColorIsSet: Boolean read FCellColorIsSet write SetCellColorIsSet;
property TextVAlignmentIsSet: Boolean read FTextVAlignmentIsSet write SetTextVAlignmentIsSet;
property TextHAlignmentIsSet: Boolean read FTextHAlignmentIsSet write SetTextHAlignmentIsSet;
property SelectedCellColorIsSet: Boolean read FSelectedCellColorIsSet write SetSelectedCellColorIsSet;
property SelectedFontColorIsSet: Boolean read FSelectedFontColorIsSet write SetSelectedFontColorIsSet;
property WordWrap: Boolean read FWordWrap write SetWordWrap;
property WordWrapIsSet: Boolean read FWordWrapIsSet write SetWordWrapIsSet;
property ParentCol: Integer read FParentCol write SetParentCol;
property ParentRow: Integer read FParentRow write SetParentRow;
end;
TMergedCell = record
ColSpan, RowSpan : Integer;
CellStyle : TCellStyle;
function Col: Integer;
function Row: Integer;
end;
THeaderElement = class
private
FLevel : THeaderLevel;
FCaption : string;
FColSpan : Integer;
FRowSpan : Integer;
FColSkip : Integer;
FStyle : TCellStyle;
procedure SetCaption(const Value: string);
procedure SetStyle(const Value: TCellStyle);
public
constructor Create(HeaderLevel: THeaderLevel);
property Caption: string read FCaption write SetCaption;
property ColSkip: Integer read FColSkip write FColSkip default 0;
property ColSpan: Integer read FColSpan write FColSpan default 1;
property RowSpan: Integer read FRowSpan write FRowSpan default 1;
property Style: TCellStyle read FStyle write SetStyle;
end;
THeaderLevel = class(TObjectList<THeaderElement>)
FHeight: Integer;
procedure SetHeight(const Value: Integer);
public
FLevels : THeaderLevels;
constructor Create(HeaderLevels: THeaderLevels; InsertBefore: integer = -1);
function AddColumn(Caption: string = '';
ColSpan: Integer = 1;
RowSpan: Integer = 1): THeaderElement;
function FillRow(Caption: string = ''): THeaderElement;
property Height: Integer read FHeight write SetHeight;
end;
TMultiHeaderGrid = class;
TMHGColumns = class;
// Base, non-data-bound column descriptor shared by all grids. Carries
// everything needed to build a (possibly grouped, multi-row) header and
// to lay out a column: Title, GroupHeader/Separator, widths, word wrap
// and the four alignments. The data-bound TMHGDBColumn (DB grid) extends
// this with FieldName and per-column data Color.
TMHGHeaderColumn = class(TCollectionItem)
private
FTitle : string;
FWidth : Integer;
FMinWidth : Integer;
FMaxWidth : Integer;
FVisible : Boolean;
FWordWrap : Boolean;
FAlignment : TTextAlign; // data cells, horizontal
FVertAlignment : TTextAlign; // data cells, vertical
FHeaderAlignment : TTextAlign; // header, horizontal
FHeaderVertAlignment : TTextAlign; // header, vertical
FGroupHeader : string;
FGroupHeaderSeparator : Char;
procedure SetTitle(const Value: string);
procedure SetWidth(const Value: Integer);
procedure SetMinWidth(const Value: Integer);
procedure SetMaxWidth(const Value: Integer);
procedure SetVisible(const Value: Boolean);
procedure SetWordWrap(const Value: Boolean);
procedure SetAlignment(const Value: TTextAlign);
procedure SetVertAlignment(const Value: TTextAlign);
procedure SetHeaderAlignment(const Value: TTextAlign);
procedure SetHeaderVertAlignment(const Value: TTextAlign);
procedure SetGroupHeader(const Value: string);
procedure SetGroupHeaderSeparator(const Value: Char);
protected
function GetDisplayName: string; override;
// Splits GroupHeader into its individual group-level captions.
function GroupPath: TArray<string>;
procedure Changed;
public
constructor Create(Collection: TCollection); override;
procedure Assign(Source: TPersistent); override;
// Sets several common properties at once; returns Self for chaining.
// Width/MinWidth/MaxWidth of -1 mean "leave unchanged".
function SetProps(const ATitle: string;
const AGroupHeader: string = '';
AWidth: Integer = -1;
AAlignment: TTextAlign = TTextAlign.Leading;
AMinWidth: Integer = -1;
AMaxWidth: Integer = -1): TMHGHeaderColumn;
published
property Title: string read FTitle write SetTitle;
property Width: Integer read FWidth write SetWidth default 80;
property MinWidth: Integer read FMinWidth write SetMinWidth default 0;
property MaxWidth: Integer read FMaxWidth write SetMaxWidth default 0;
property Visible: Boolean read FVisible write SetVisible default True;
property WordWrap: Boolean read FWordWrap write SetWordWrap default False;
property Alignment: TTextAlign read FAlignment write SetAlignment default TTextAlign.Leading;
property VertAlignment: TTextAlign read FVertAlignment write SetVertAlignment default TTextAlign.Center;
property HeaderAlignment: TTextAlign read FHeaderAlignment write SetHeaderAlignment default TTextAlign.Center;
property HeaderVertAlignment: TTextAlign read FHeaderVertAlignment write SetHeaderVertAlignment default TTextAlign.Center;
property GroupHeader: string read FGroupHeader write SetGroupHeader;
property GroupHeaderSeparator: Char read FGroupHeaderSeparator write SetGroupHeaderSeparator default ';';
end;
TMHGColumns = class(TOwnedCollection)
private
FGrid: TMultiHeaderGrid;
function GetItem(Index: Integer): TMHGHeaderColumn;
procedure SetItem(Index: Integer; const Value: TMHGHeaderColumn);
protected
procedure Update(Item: TCollectionItem); override;
public
// AItemClass lets the descendant TMHGDBColumns (DB grid) run this
// collection with its richer item class (TMHGColumn).
constructor Create(AGrid: TMultiHeaderGrid;
AItemClass: TCollectionItemClass = nil); reintroduce; overload;
function Add: TMHGHeaderColumn;
function AddColumn(const ATitle: string = '';
const AGroupHeader: string = ''): TMHGHeaderColumn;
property Grid: TMultiHeaderGrid read FGrid;
property Items[Index: Integer]: TMHGHeaderColumn read GetItem write SetItem; default;
end;
THeaderLevels = class(TObjectList<THeaderLevel>)
Grid : TMultiHeaderGrid;
constructor Create(Grid : TMultiHeaderGrid);
function AddRow: THeaderLevel; overload;
function AddRow(Heigth: integer): THeaderLevel; overload;
function AddRowOnTop: THeaderLevel; overload;
function AddRowOnTop(Heigth: integer): THeaderLevel; overload;
function GetElementAtCell(ACol, ARow: integer): THeaderElement;
end;
FColData = record
Width : integer;
ContentWidth : integer;
UserWidth : integer;
MinWidth : integer;
MaxWidth : integer;
TextVAlignment : TTextAlign;
TextHAlignment : TTextAlign;
WordWrap : Boolean;
end;
TStartEditingEvent = procedure(Sender: TObject; ACol, ARow: Integer; var InitialChar: Char) of object;
TDrawCellEvent = procedure(Sender: TObject; ACol, ARow: Integer; Canvas: TCanvas; const Rect: TRectF; IsSelected: boolean; const Text: string; var Handled: Boolean) of object;
TGetCellTextEvent = procedure(Sender: TObject; ACol, ARow: Integer; var Text: string) of object;
TSetCellTextEvent = procedure(Sender: TObject; ACol, ARow: Integer; const Text: string) of object;
TGetCellStyleEvent = procedure(Sender: TObject; ACol, ARow: Integer; var CellStyle: TCellStyle) of object;
TSetCellStyleEvent = procedure(Sender: TObject; ACol, ARow: Integer; const CellStyle: TCellStyle) of object;
TColumnsResizedEvent = procedure(Sender: TObject; StartRow, EndRow: integer) of object;
TRowResizedEvent = procedure(Sender: TObject; ARow: Integer) of object;
TGridScrollEvent = procedure(Sender: TObject; Left,Top: Integer) of object;
// Fired on a double-click. Handled is True by default: leave it True to
// suppress the grid's built-in double-click handling (opening the inplace
// editor); set it False to let the grid process the double-click as usual.
TGridDblClickEvent = procedure(Sender: TObject; var Handled: Boolean) of object;
// Fired before a row is inserted/appended/deleted via the keyboard shortcuts
// (Insert / Down-on-last-row / Ctrl+Del). Set Allow:=False to veto the change.
TRowModifyEvent = procedure(Sender: TObject; ARow: Integer; var Allow: Boolean) of object;
TResizeMode = (rmNone, rmColumn, rmHeaderRow, rmGridRow);
TResizeQuality = (rqNoChange, rqPrecise, rqFast);
TScrollShowMode = (smAuto, smShow, smHide);
TMultiHeaderGrid = class(TControl)
private
function FillText(Canvas: TCanvas; const ARect: TRectF; const AText: string; const WordWrap: Boolean; const AOpacity: Single;
const Flags: TFillTextFlags; const ATextAlign, AVTextAlign: TTextAlign): TRectF;
type
TRowData = packed record
Top : integer;
Height : Word;
AutoSized : boolean;
end;
var
VScrollBar: TScrollBar;
HScrollBar: TScrollBar;
HScrollPanel: TPaintBox;
CornerPanel: TPanel;
FRowCount: Integer;
FDefaultColWidth: integer;
FDefaultRowHeight: integer;
FColData: array of FColData;
FRowData: Array of TRowData;
FHeaderLevels: THeaderLevels;
FGridLines: Boolean;
FHeaderLineColor: TAlphaColor;
FGridLineColor: TAlphaColor;
FGridLineWidth: Single;
FSelectedCell: TPoint;
FOnSelectCell: TNotifyEvent;
FOnDrawCell: TDrawCellEvent;
FOnGetCellText: TGetCellTextEvent;
FOnSetCellText: TSetCellTextEvent;
FOnGetCellStyle: TGetCellStyleEvent;
FOnSetCellStyle: TSetCellStyleEvent;
FOnStartEditing: TStartEditingEvent;
FOnDblClick: TGridDblClickEvent;
FOnCellClick: TNotifyEvent;
FOnHeaderClick: TNotifyEvent;
// VCL-TDBGrid-like column focus events. FLastColEvent holds the column
// index OnColEnter last fired for, so OnColExit/OnColEnter fire once per
// genuine column change regardless of the path (mouse, keyboard or
// programmatic Col:=). -1 means "no column entered yet".
FOnColEnter: TNotifyEvent;
FOnColExit: TNotifyEvent;
FLastColEvent: Integer;
FHeaderFont: TFont;
FHeaderFontColor: TAlphaColor;
FHeaderCellColor: TAlphaColor;
FCellFont: TFont;
FSelectedCellColor: TAlphaColor;
FSelectedFontColor: TAlphaColor;
FCellColorAlternate: TAlphaColor;
FCellPadding: TBounds;
FCellFontColor: TAlphaColor;
FViewTop: Integer;
FBackgroundColor: TAlphaColor;
FViewLeft: Integer;
FCellColor: TAlphaColor;
FResizeEnabled: Boolean;
FResizeHeaderRowEnabled: Boolean;
FResizeColEnabled: Boolean;
FResizeRowEnabled: Boolean;
FResizeStartColumnIndex: Integer;
FResizeEndColumnIndex: Integer;
FResizeRowIndex: Integer;
FResizeMode: TResizeMode;
FResizeStartPos: TPointF;
FResizeStartWidths: array of Integer;
FResizeStartHeight: Integer;
FResizeMargin: Integer;
FOnColumnResized: TColumnsResizedEvent;
FOnHeaderResized: TRowResizedEvent;
FOnRowResized: TRowResizedEvent;
FOnGridScroll: TGridScrollEvent;
FOnInsertRow: TRowModifyEvent;
FOnAppendRow: TRowModifyEvent;
FOnDeleteRow: TRowModifyEvent;
FLastClickIsOnCell: boolean;
FDrawRect : TRectF;
FRowSelect: Boolean;
FWordWrap: Boolean;
FHeaderWordWrap: Boolean;
// When word wrap is on, AutoSizeCols first sizes wrapped columns down to
// their widest-word width (so text wraps). With this on (the default), a
// follow-up pass reconciles the total against the viewport: leftover
// horizontal space is handed back to wrapped columns (growing them toward
// their natural one-line width, so they wrap less), and an overflow shrinks
// columns proportionally toward their word-width floor to fit. When off,
// columns are sized to word width regardless of available space.
FConservativeWrap: Boolean;
FColumns: TMHGColumns;
FRebuildingColumns: Boolean;
// One-shot guard: at design time a freshly dropped grid seeds a few
// placeholder columns on its first layout (so it is not an empty
// rectangle). Set once so it never fights the user emptying Columns later.
FDesignIsLoaded: Boolean;
// Raised by bulk operations (header/column rebuilds) that apply word-wrap
// to many columns at once, so the per-setter row re-fit doesn't run once
// per column. The caller does a single sizing pass when finished.
FSuppressAutoSize: Boolean;
// True only while AutoSizeRows runs. Row-provider descendants check this
// and skip extending RowCount during the measurement sweep (it iterates
// every row).
FInLayout: Boolean;
FGridCellsHasWordWrap: boolean;
// Precision of the last explicit AutoSize the user requested. Internal
// re-sizes (the inplace editor's row re-fit, commit re-fit, the visible-
// rows pass) reuse this so editing matches whatever mode the grid was last
// sized in - keeping fast grids fast and precise grids pixel-consistent.
FAutoSizePrecise: Boolean;
FVerticalScroll: TScrollShowMode;
FHorisontalScroll: TScrollShowMode;
// Inplace cell editor (basic grids). A single reusable TMemo is moved
// over the cell being edited.
FEditor: TMemo;
FEditorHost: TLayout;
// Currently active inplace editor control. For the base/string grids
// this is always FEditor (the TMemo). The DB grid may swap in a typed
// control (TComboBox/TDateEdit/TTimeEdit/...) per field type. (Boolean
// fields use no editor - they toggle in place.)
FActiveEditor: TControl;
FEditing: Boolean;
FEditCol: Integer;
FEditRow: Integer;
// Anchor column widened for the editor and its width before editing
// started, so a transient widen can be shrunk back (but not below the
// original) when editing ends. FEditWidenAnchor<0 means "none".
FEditWidenAnchor: Integer;
FEditWidenStartW: Integer;
// When True a column transiently widened to fit the inplace editor keeps
// its enlarged width when editing ends; when False (default) it is fully
// restored to its pre-edit width.
FKeepEditorWidenedColumn: Boolean;
FReadOnly: Boolean;
FPainted: boolean;
// Set while the base handles vkEnd so the shared down-navigation skips the
// synchronous on-demand grow; the DB descendant pages to Eof separately.
FEndJump: boolean;
FMaxColumnAutoWidth: integer;
// When set, AutoSizeCols distributes any leftover viewport width across
// columns so they fill the whole viewport (bounded by column Min/MaxWidth).
FFitColumnsIntoView: Boolean;
FInFitColumns: Boolean; // re-entrancy guard for fit-on-resize
FFetchesOnDemand: Boolean; // cached: dataset fetches rows lazily (FetchOptions.Mode)
// Grid would automatically try to keep rows & columns autsized to fit data
FKeepRowsAutoSized: Boolean;
FKeepColumnsAutoSized: Boolean;
function ResizeStartWidth: Integer;
procedure SetHeaderWordWrap(const Value: Boolean);
procedure SetColumns(const Value: TMHGColumns);
// Builds a (possibly grouped, multi-row) header + applies column
// geometry/alignment/wordwrap from a TMHGColumns collection.
procedure BuildHeaderFromColumns(const ACols: array of TMHGHeaderColumn);
procedure RebuildFromColumns;
// True when wrapping applies to the header element at (ALevel,ACol).
function HeaderCellWordWrap(AElement: THeaderElement): Boolean;
// Effective word-wrap for a data cell, matching the cell-draw logic
// (per-cell style override, else grid/column flag).
function EffectiveCellWordWrap(ACol, ARow: Integer): Boolean;
// Effective cell style for a data cell, matching the cell-draw logic
// (per-cell style override, else grid/column flag).
function EffectiveCellStyle(ACol, ARow: Integer): TCellStyle;
// Raw height (before padding/gridline) the cell's text needs. Single source
// of truth shared by AutoSizeRows (committed text) and the inplace editor
// (uncommitted FEditor.Text) so the two never disagree by a fractional
// pixel. AText is the text to measure; RowSpan divides a merged cell's
// height across its rows.
function MeasureCellTextHeight(ACol, ARow: Integer; const AText: string;
RowSpan: Integer): Single;
// Width available to a header element's text (merged rect, padded).
function HeaderElementTextWidth(ALevel, ACol: Integer): Single;
procedure StartCellEditing(ACol, ARow: Integer; InitialChar: Char);
// Widens the editing cell's (anchor) column if EditorMinColWidth needs more
// room than it currently has. Called at edit start and re-callable while a
// non-wrapping editor's content grows (e.g. typing a long number).
procedure WidenColForEditor(ACol, ARow: Integer); virtual;
// Called when editing ends: shrinks a transiently-widened anchor column to
// ATargetW (the final value's needed width), but never below its pre-edit
// width. ATargetW<0 restores the pre-edit width (cancel).
procedure FinishEditColWidth(ATargetW: Integer);
procedure EnsureEditor(TextAlign: TTextAlign);
// Parents a control into FEditorHost and wires the shared key/exit
// handlers, so the memo and the DB grid's typed editors all sit in the host
// over the opaque backing, clipped to the cell.
procedure WireEditor(C: TControl);
procedure PositionEditor; virtual;
// Lays the active editor out inside the already-positioned host, given the
// host interior size, and shows it. Base fills the host with the
// Client-aligned TMemo; the DB grid overrides for its typed editors.
procedure LayoutActiveEditor(AWidth, AHeight: Single); virtual;
// Takes down the editor UI: hides the host (and its FEditorBack child) and
// the active editor. Descendants override to hide their extra editors too.
procedure HideEditor; virtual;
procedure CommitEditing;
procedure CancelEditing; virtual;
procedure EditorKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
Shift: TShiftState);
procedure EditorExit(Sender: TObject); virtual;
// Grows (or shrinks) the row being edited so it fits the editor's current text,
// then re-lays the editor out within the resized cell. Fires on every keystroke
// via FEditor.OnChange while the cell content is still uncommitted, so we
// measure the live editor rather than the stored cell text.
procedure MemoEditorTextChanged(Sender: TObject);
protected
// --- Columns extensibility hooks -------------------------------------
// Factory for the Columns collection, called once from the constructor.
// The base grid creates a plain TMHGColumns collection; the DB grid
// overrides it to create TMHGColumns (items carry FieldName, Color, ...).
function CreateColumns: TMHGColumns; virtual;
// Notification from the Columns collection on any add/remove/reorder/
// property change. The base grid rebuilds the header from the collection;
// the DB grid defers a full table rebuild instead.
procedure ColumnsChanged; virtual;
// Initializes a Columns item created by growing ColCount. The base grid
// only stamps the default width; the DB grid overrides it to also assign
// the next unused FieldName so a grown column binds to a real field.
procedure InitNewColumn(ACol: TMHGHeaderColumn); virtual;
// --- Inplace editor extensibility hooks -----------------------------
// The base/string grids always edit through the shared TMemo (FEditor).
// The DB grid overrides these to select a typed editor control per field
// (TMemo / TComboBox / TDateEdit / TTimeEdit / composite). Boolean fields
// are not edited through a control - they toggle in place.
//
// PrepareCellEditor: choose/create the control for (ACol,ARow), parent it,
// wire OnKeyDown/OnExit, and return it. Base returns the shared TMemo.
function PrepareCellEditor(ACol, ARow: Integer): TControl; virtual;
// Load the cell's current value into the active editor (InitialChar<>#0
// means the user started typing - replace content with that char).
procedure LoadEditorValue(ACol, ARow: Integer; InitialChar: Char); virtual;
// Read the active editor's value back as display text.
function GetEditorText: string; virtual;
// Persist the editor's value into the cell/field. Base writes Cells[].
// Returns True if the value was accepted (commit succeeded).
function CommitEditorValue(ACol, ARow: Integer): Boolean; virtual;
// Active editor control (FActiveEditor, or the TMemo as a fallback).
function ActiveEditorControl: TControl; virtual;
// After the editor is shown, place the caret at the end of its text (no
// selection). Base handles the shared TMemo; descendants override for
// their own typed editors.
procedure PlaceEditorCaretAtEnd(Ed: TControl); virtual;
// Minimum column width (px) the chosen editor needs to be usable for the
// cell at (ACol,ARow). 0 means "no requirement" (the memo wraps/scrolls
// and is fine in a narrow column). StartCellEditing widens the column to
// at least this before positioning the editor. Descendants override to
// request room for fixed-size controls (combo/date/time/datetime).
function EditorMinColWidth(ACol, ARow: Integer): single; virtual;
procedure SetReadOnly(const Value: Boolean);
// CanEditCell: an inplace editor may OPEN for this cell (by type/bounds).
// It is independent of ReadOnly so a read-only grid still opens the editor
// for selecting/copying text. CellIsModifiable gates actual writes: it is
// False when ReadOnly (and, in the DB grid, when the dataset can't modify),
// and drives the editor's own ReadOnly state plus the row-edit shortcuts.
function CanEditCell(ACol, ARow: Integer): Boolean; virtual;
function CellIsModifiable(ACol, ARow: Integer): Boolean; virtual;
// Applies CellIsModifiable to the active editor control so a read-only cell
// opens an editor that allows selection/copy but not changes.
procedure ApplyEditorReadOnly(Ed: TControl; AModifiable: Boolean); virtual;
// Keyboard row shortcuts. Insert inserts a blank row before ARow, AppendRow
// adds one after the last row (Down on the last row), DeleteRow removes ARow.
// All are no-ops when the grid/dataset is not modifiable. The base grid acts
// on the string-grid row model; the DB grid overrides to drive the dataset.
procedure InsertRow(ARow: Integer); virtual;
procedure AppendRow; virtual;
// Handles a plain Down keypress. Returns True if it fully handled the key
// (so KeyDown does no further navigation). The base appends when Down is
// pressed on the last row of a modifiable grid; the DB grid overrides to
// follow VCL TDBGrid's NextRow semantics (append at Eof, cancel an
// unmodified pending insert). Returns False to fall through to plain move.
function HandleDownKey: Boolean; virtual;
procedure DeleteRow(ARow: Integer); virtual;
// Fire the veto events; return True when the change may proceed.
function DoInsertRow(ARow: Integer): Boolean;
function DoAppendRow(ARow: Integer): Boolean;
function DoDeleteRow(ARow: Integer): Boolean;
// Boolean (toggle) cells are not edited through an inplace control: they
// flip in place on a click / Space / Enter. CellIsToggle reports such a
// cell; ToggleCell flips its value and persists it. The base grid has no
// such cells (both are no-ops); TMultiHeaderDBGrid overrides them for
// ftBoolean fields.
function CellIsToggle(ACol, ARow: Integer): Boolean; virtual;
function ToggleCell(ACol, ARow: Integer): Boolean; virtual;
// Glyph rectangle for a toggle cell's checkbox, shared by drawing and
// mouse hit-testing so a click on the box (vs. around it) is detected
// consistently with what is painted.
function ToggleGlyphRect(ACol, ARow: Integer; const ARect: TRectF): TRectF; virtual;
// Paints the built-in checkbox glyph for a toggle cell. Self-contained
// (vector drawing, no image list). Descendants may override to restyle.
procedure DrawToggleCell(Canvas: TCanvas; ACol, ARow: Integer;
const ARect: TRectF; IsSelected, AChecked: Boolean); virtual;
procedure SetRowCount(Value: Integer);
function GetColCount: Integer;
procedure SetColCount(Value: Integer);
// Sizes FColData (per-column geometry) to Columns.Count, stamping defaults
// on any newly added slots. Used by the rebuild paths, which must resize
// internal geometry to match the collection WITHOUT mutating it.
procedure EnsureColData;
procedure SetDefaultColWidth(const Value: integer);
procedure SetDefaultRowHeight(const Value: integer);
procedure SetGridLines(const Value: Boolean);
procedure SetGridLineColor(const Value: TAlphaColor);
procedure SetGridLineWidth(const Value: Single);
procedure SetSelectedCell(const Value: TPoint);
procedure SetCellFont(const Value: TFont);
procedure SetHeaderFont(const Value: TFont);
procedure SetCellColorAlternate(const Value: TAlphaColor);
function GetColLeft(Index: Integer): Integer;
function GetColWidth(Index: Integer): Integer;
procedure SetColWidth(Index: Integer; const Value: Integer);
function GetRowHeight(Index: Integer): Integer;
procedure SetRowHeight(Index: Integer; const Value: Integer);
function HeaderCellIsFiller(ALevel, ACol: Integer): Boolean;
function HeaderMergedRect(ALevel, ACol: Integer): TRectF;
procedure DrawGridLines(Canvas: TCanvas);
procedure DrawCells(Canvas: TCanvas);
procedure DrawHeaders(Canvas: TCanvas);
procedure DrawCell(Canvas: TCanvas; ACol, ARow: Integer; ARect: TRectF);
procedure DrawHeaderCell(Canvas: TCanvas; ALevel, ACol: Integer);
procedure SetCellPadding(const Value: TBounds);
procedure SetHeaderFontColor(const Value: TAlphaColor);
procedure SetCellFontColor(const Value: TAlphaColor);
procedure SetViewTop(const Value: Integer); virtual;
procedure VScrollBarChange(Sender: TObject);
procedure HScrollBarChange(Sender: TObject);
procedure SetResizeEnabled(const Value: Boolean);
function GetResizeMargin: Integer;
procedure SetResizeMargin(const Value: Integer);
function IsResizeArea(X, Y: Single; out AStartCol, AEndCol, ARow: Integer): TResizeMode;
procedure StartColumnResize(StartCol, EndCol: Integer; X: Single);
procedure StartHeaderRowResize(ARow: Integer; Y: Single);
procedure StartGridRowResize(ARow: Integer; Y: Single);
procedure UpdateGroupColumnWidth(StartCol, EndCol, TotalWidth: Integer);
procedure UpdateColumnWidth(StartCol, EndCol: Integer; NewWidth: Integer);
procedure UpdateHeaderRowHeight(ARow: Integer; NewHeight: Integer);
procedure UpdateRowHeight(ARow: Integer; NewHeight: Integer);
function GetCells(ACol, ARow: Integer): string;
procedure SetCells(ACol, ARow: Integer; const Value: string);
function GetCellStyle(ACol, ARow: Integer): TCellStyle;
procedure SetCellStyle(ACol, ARow: Integer; const Value: TCellStyle);
function GetRowTops(Index: Integer): Integer;
procedure SetCol(const Value: Integer);
procedure SetRow(const Value: Integer);
procedure SetBackgroundColor(const Value: TAlphaColor);
procedure SetViewLeft(const Value: Integer);
procedure SetHeaderCellColor(const Value: TAlphaColor);
procedure SetSelectedCellColor(const Value: TAlphaColor);
procedure SetSelectedFontColor(const Value: TAlphaColor);
procedure SetCellColor(const Value: TAlphaColor);
procedure SetRowSelect(const Value: Boolean);
function GetColTextHAlignment(Index: Integer): TTextAlign;
function GetColTextVAlignment(Index: Integer): TTextAlign;
procedure SetColTextHAlignment(Index: Integer; const Value: TTextAlign);
procedure SetColTextVAlignment(Index: Integer; const Value: TTextAlign);
function GetColWordWrap(Index: Integer): Boolean;
procedure SetColWordWrap(Index: Integer; const Value: Boolean);
procedure SetWordWrap(const Value: Boolean);
procedure SetConservativeWrap(const Value: Boolean);
procedure SetKeepColumnsAutoSized(const Value: Boolean);
procedure SetKeepRowsAutoSized(const Value: Boolean);
procedure SetFitColumnsIntoView(const Value: Boolean);
// ConservativeWrap helper: after AutoSizeCols has sized wrapped columns to
// their word width, redistribute against the viewport - give spare width
// back to wrapped columns (up to their natural one-line width), or shrink
// them proportionally toward their word floor when the total overflows.
procedure ReconcileWrappedColumns(const AIsWrapped: TArray<Boolean>;
const AWordFloor, ANaturalW: TArray<Single>;
AViewportW: Integer);
// Distributes leftover viewport width across columns so they fill the whole
// viewport. Each column is bounded by its own MaxWidth; iterates so freed-up
// slack from maxed-out columns is redistributed to the rest.
procedure FitColumnsToViewport(AViewportW: Integer);
// After the header heights are known, find columns whose caption wrapped onto
// substantially more lines than the typical column (average line count rounded
// up) and widen them just enough to bring them down toward typical height -
// not all the way to one line (which would leave blank header lines). Capped
// by the column's MaxWidth / MaxColumnAutoWidth; if the total exceeds the
// viewport the grid scrolls horizontally. Active whenever HeaderWordWrap is
// on. Re-fits header heights itself. Returns True if any width changed.
function BalanceHeaderColumnWidths: Boolean;
// Number of wrapped lines the caption of the element governing (ALevel,ACol)
// needs at the column's current merged width (1 when not wrapping).
function HeaderElementLineCount(ALevel, ACol: Integer): Integer;
procedure SetHeaderLineColor(const Value: TAlphaColor);
procedure SetVerticalScroll(const Value: TScrollShowMode);
procedure SetHorisontalScroll(const Value: TScrollShowMode);
function GetColMaxWidth(Index: Integer): integer;
function GetColMinWidth(Index: Integer): integer;
procedure SetColMaxWidth(Index: Integer; const Value: integer);
procedure SetColMinWidth(Index: Integer; const Value: integer);
function GridHaveWordWrap: boolean;
protected
function CanObserve(const ID: Integer): Boolean; override;
// Pre-paint hook. The base does nothing; descendants (the DB grid) use it
// to flush a deferred, coalesced rebuild exactly once before drawing, so a
// burst of column-property changes triggers a single recompute instead of
// one per change. Call this before any operation that must observe an
// up-to-date layout (paint, size queries, autosize).
procedure EnsureLayout; virtual;
procedure Paint; override;
procedure Loaded; override;
procedure Resize; override;
procedure DoSelectCell; virtual;
procedure DoDrawCell(ACol, ARow: Integer; Canvas: TCanvas; const Rect: TRectF; IsSelected: boolean; const Text: string; var Handled: Boolean); virtual;
procedure DoGetCellText(ACol, ARow: Integer; var Text: string); virtual;
procedure DoSetCellText(ACol, ARow: Integer; const Text: string); virtual;
procedure DoGetCellStyle(ACol, ARow: Integer; var Style: TCellStyle); virtual;
procedure DoSetCellStyle(ACol, ARow: Integer; const Style: TCellStyle); virtual;
procedure DoCellClick(ACol, ARow: Integer); virtual;
procedure DoHeaderClick(ALevel, ACol: Integer); virtual;
procedure DblClick; override;
procedure DoColumnResized; virtual;
procedure DoHeaderRowResized; virtual;
procedure DoRowResized; virtual;
// Called from keyboard navigation before moving down, so the target row can
// be provided and the move is not capped at the current RowCount.
procedure EnsureRowAvailable(ARow: Integer); virtual;
// True only while AutoSizeRows runs (read by row-provider descendants).
function InLayout: Boolean;
procedure DoGridScroll; virtual;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); override;
procedure KeyDown(var Key: Word; var KeyChar: WideChar; Shift: TShiftState); override;
procedure UpdateSize;
procedure ScrollToSelectedCell;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function IsMergedCell(ACol, ARow: Integer): Boolean; overload;
function IsMergedCell(ACol, ARow: Integer; out MergedCell: TMergedCell): Boolean; overload;
function MergeCells(ACol, ARow, AColSpan, ARowSpan: Integer): Boolean; overload;
function MergeCells(ACol, ARow, AColSpan, ARowSpan: Integer; const ACaption: string): Boolean; overload;
procedure UnMergeCells(ACol, ARow: Integer);
procedure ClearMergedCells;
procedure AutoSize(Precision: TResizeQuality = rqNoChange);
procedure AutoSizeCols(IncreaseOnly: boolean = False; FirstRow: integer = -1; LastRow : integer = -1);
procedure AutoSizeVisibleCols;
procedure AutoSizeRows(FromRow: integer = 0; ToRow: integer = -1;
FResizeStartColumnIndex: integer = -1; FResizeEndColumnIndex: integer = -1;
TryOptimise: boolean = False; ViewBottomY: integer = -1);
procedure AutoSizeVisibleRows(FResizeStartColumnIndex: integer = -1; FResizeEndColumnIndex: integer = -1);
// Sizes each header level's height to fit its (optionally wrapped or
// multi-line) captions. Shared by all grid descendants.
procedure AutoSizeHeaders;
procedure ClearSelection;
property ColLefts[Index: Integer]: Integer read GetColLeft;
property ColWidths[Index: Integer]: Integer read GetColWidth write SetColWidth;
property ColTextHAlignment[Index: Integer]: TTextAlign read GetColTextHAlignment write SetColTextHAlignment;
property ColTextVAlignment[Index: Integer]: TTextAlign read GetColTextVAlignment write SetColTextVAlignment;
property ColMinWidth[Index: Integer]: integer read GetColMinWidth write SetColMinWidth;
property ColMaxWidth[Index: Integer]: integer read GetColMaxWidth write SetColMaxWidth;
property ColWordWrap[Index: Integer]: Boolean read GetColWordWrap write SetColWordWrap;
function GetCellRect(ACol, ARow: Integer): TRectF; overload;
function GetCellRect(ACol, ARow: Integer; out MergedCell: TMergedCell): TRectF; overload;
function GetMergedCellRect(ACol, ARow: Integer): TRectF;
function GetHeaderRect(ALevel, ACol: Integer): TRectF;
property RowHeights[Index: Integer]: Integer read GetRowHeight write SetRowHeight;
property RowTops[Index: Integer]: Integer read GetRowTops;
property Cells[ACol, ARow: Integer]: string read GetCells write SetCells;
property CellStyle[ACol, ARow: Integer]: TCellStyle read GetCellStyle write SetCellStyle;
property SelectedCell: TPoint read FSelectedCell write SetSelectedCell;
property Header: THeaderLevels read FHeaderLevels;
property ViewTop: Integer read FViewTop write SetViewTop;
property ViewLeft: Integer read FViewLeft write SetViewLeft;
function ViewBottom: Integer;
function ViewCellsHeight: Integer;
function HeaderHeight: Integer;
function ViewPortWidth: Integer;
function ViewPortHeight: Integer;
function ViewPortDataHeight: Integer;
function FullTableWidth: Integer;
function FullTableHeight: Integer;
procedure Invalidate;
function RowAtHeightCoord(Y: Integer): integer;
published
property Align;
property Anchors;
property ClipChildren;
property ClipParent;
property Cursor;
property DragMode;
property EnableDragHighlight;
property Enabled;
property Locked;
property Height;
property HitTest;
property Margins;
property Opacity;
property PopupMenu;
property Position;
property RotationAngle;
property RotationCenter;
property Scale;
property Size;
property TabStop;
property Visible;
property Width;
// Double-click. Handled is True by default; leave it True to suppress the
// grid's built-in handling (opening the inplace editor), set it False to
// let the grid process the double-click normally.
property OnDblClick: TGridDblClickEvent read FOnDblClick write FOnDblClick;
property OnResize;
// --- Layer 1: events the grid does not intercept (fire natively) ---
property OnEnter;
property OnExit;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseWheel;
property OnGesture;
property OnDragEnter;
property OnDragLeave;
property OnDragOver;
property OnDragDrop;
property OnDragEnd;
// --- Layer 2: events the grid intercepts; dispatch ensured in the
// overridden MouseMove / KeyDown (inherited now called) ---
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnKeyDown;
property OnKeyUp;
property ColCount: Integer read GetColCount write SetColCount stored False;
property RowCount: Integer read FRowCount write SetRowCount default 10;
property Col: Integer read FSelectedCell.X write SetCol;
property Row: Integer read FSelectedCell.Y write SetRow;
property DefaultColWidth: integer read FDefaultColWidth write SetDefaultColWidth;
property DefaultRowHeight: integer read FDefaultRowHeight write SetDefaultRowHeight;
property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor default TAlphaColorRec.White;
property GridLines: Boolean read FGridLines write SetGridLines default True;
property GridLineColor: TAlphaColor read FGridLineColor write SetGridLineColor default TAlphaColorRec.Gray;
property GridLineWidth: Single read FGridLineWidth write SetGridLineWidth;
property HeaderLineColor: TAlphaColor read FHeaderLineColor write SetHeaderLineColor default TAlphaColorRec.Black;
property HeaderFont: TFont read FHeaderFont write SetHeaderFont;
property HeaderFontColor: TAlphaColor read FHeaderFontColor write SetHeaderFontColor default TAlphaColorRec.Black;
property HeaderCellColor: TAlphaColor read FHeaderCellColor write SetHeaderCellColor default TAlphaColorRec.Lightgray;
property CellFont: TFont read FCellFont write SetCellFont;
property CellFontColor: TAlphaColor read FCellFontColor write SetCellFontColor default TAlphaColorRec.Black;
property CellColor: TAlphaColor read FCellColor write SetCellColor default TAlphaColorRec.White;
property CellColorAlternate: TAlphaColor read FCellColorAlternate write SetCellColorAlternate default TAlphaColorRec.Lightblue;
property SelectedFontColor: TAlphaColor read FSelectedFontColor write SetSelectedFontColor default TAlphaColorRec.Black;
property SelectedCellColor: TAlphaColor read FSelectedCellColor write SetSelectedCellColor default TAlphaColorRec.Lightblue;
property RowSelect: Boolean read FRowSelect write SetRowSelect default False;
// When True the inplace cell editor is disabled and the grid is view-only.
property ReadOnly: Boolean read FReadOnly write SetReadOnly default True;
// A column may be transiently widened to fit the inplace editor. When False
// (default) the column returns to its pre-edit width when editing ends; when
// True it keeps the enlarged width.
property KeepEditorWidenedColumn: Boolean read FKeepEditorWidenedColumn write FKeepEditorWidenedColumn default False;
property WordWrap: Boolean read FWordWrap write SetWordWrap default False;
// When word wrap is on, controls whether AutoSizeCols reconciles wrapped
// column widths against the available viewport: grows wrapped columns back
// toward their one-line width when there's spare horizontal space (less
// wrapping), and shrinks proportionally toward word width on overflow.
// On by default; turn off to size wrapped columns purely to word width.
property ConservativeWrap: Boolean read FConservativeWrap write SetConservativeWrap default True;
// When set, header captions wrap to the cell width during drawing and
// are accounted for by AutoSizeHeaders. On by default.
property HeaderWordWrap: Boolean read FHeaderWordWrap write SetHeaderWordWrap default True;
// Grid would automatically try to keep rows & columns autsized to fit data
property KeepColumnsAutoSized: Boolean read FKeepColumnsAutoSized write SetKeepColumnsAutoSized default True;
property KeepRowsAutoSized: Boolean read FKeepRowsAutoSized write SetKeepRowsAutoSized default True;
// When True, AutoSizeCols expands columns to fill the whole viewport width
// after content sizing, distributing spare space proportionally (each column
// still bounded by its own Min/MaxWidth). Off by default.
property FitColumnsIntoView: Boolean read FFitColumnsIntoView write SetFitColumnsIntoView default False;
// Maximum width of the column for autowidth computing
// Can be overriden by MaxWidth property of the column.
property MaxColumnAutoWidth: integer read FMaxColumnAutoWidth write FMaxColumnAutoWidth default 400;
// Declarative header/column definitions. When populated they build the
// (grouped) header and set column widths/alignment/word wrap. When empty
// the grid keeps whatever header was created procedurally via
// Header.AddRow. TMultiHeaderDBGrid redeclares this property with its
// data-bound collection type (TMHGColumns).
property Columns: TMHGColumns read FColumns write SetColumns;
property HorisontalScroll: TScrollShowMode read FHorisontalScroll write SetHorisontalScroll default TScrollShowMode.smAuto;
property VerticalScroll: TScrollShowMode read FVerticalScroll write SetVerticalScroll default TScrollShowMode.smAuto;
property CellPadding: TBounds read FCellPadding write SetCellPadding;
property OnSelectCell: TNotifyEvent read FOnSelectCell write FOnSelectCell;
property OnDrawCell: TDrawCellEvent read FOnDrawCell write FOnDrawCell;
property OnGetCellText: TGetCellTextEvent read FOnGetCellText write FOnGetCellText;
property OnSetCellText: TSetCellTextEvent read FOnSetCellText write FOnSetCellText;
property OnGetCellStyle: TGetCellStyleEvent read FOnGetCellStyle write FOnGetCellStyle;
property OnSetCellStyle: TSetCellStyleEvent read FOnSetCellStyle write FOnSetCellStyle;
property OnStartEditing: TStartEditingEvent read FOnStartEditing write FOnStartEditing;
property OnCellClick: TNotifyEvent read FOnCellClick write FOnCellClick;
property OnHeaderClick: TNotifyEvent read FOnHeaderClick write FOnHeaderClick;
// VCL TDBGrid-like column focus events (fire on a genuine column change
// via mouse, keyboard or programmatic Col:=).
property OnColEnter: TNotifyEvent read FOnColEnter write FOnColEnter;
property OnColExit: TNotifyEvent read FOnColExit write FOnColExit;
property ResizeEnabled: Boolean read FResizeEnabled write SetResizeEnabled default True;
property ResizeRowEnabled: Boolean read FResizeRowEnabled write FResizeRowEnabled default True;
property ResizeColEnabled: Boolean read FResizeColEnabled write FResizeColEnabled default True;
property ResizeHeaderRowEnabled: Boolean read FResizeHeaderRowEnabled write FResizeHeaderRowEnabled default True;
property ResizeMargin: Integer read GetResizeMargin write SetResizeMargin default 2;
property OnColumnResized: TColumnsResizedEvent read FOnColumnResized write FOnColumnResized;
property OnHeaderResized: TRowResizedEvent read FOnHeaderResized write FOnHeaderResized;
property OnRowResized: TRowResizedEvent read FOnRowResized write FOnRowResized;
property OnGridScroll: TGridScrollEvent read FOnGridScroll write FOnGridScroll;
// Fired (with a veto flag) before the keyboard row shortcuts modify rows.
property OnInsertRow: TRowModifyEvent read FOnInsertRow write FOnInsertRow;
property OnAppendRow: TRowModifyEvent read FOnAppendRow write FOnAppendRow;
property OnDeleteRow: TRowModifyEvent read FOnDeleteRow write FOnDeleteRow;
end;
TMultiHeaderStringGrid = class(TMultiHeaderGrid)
private
FCellTexts: array of array of string;
FCellStyles: array of array of TCellStyle;
protected
procedure DoGetCellText(ACol, ARow: Integer; var Text: string); override;
procedure DoSetCellText(ACol, ARow: Integer; const Text: string); override;
procedure DoGetCellStyle(ACol, ARow: Integer; var Style: TCellStyle); override;
procedure DoSetCellStyle(ACol, ARow: Integer; const Style: TCellStyle); override;
public
constructor Create(AOwner: TComponent); override;
end;
TMultiHeaderDBGrid = class;
TMHGDBColumns = class;
// Single column of TMultiHeaderDBGrid. Extends the shared
// TMHGHeaderColumn (Title, GroupHeader, widths, alignments, word wrap)
// with the data-bound bits: FieldName and a per-column data Color.
//
// Multi-line grouped headers are produced exactly as in UniGUI:
// GroupHeader holds one or more group captions joined with
// GroupHeaderSeparator (default ';'). Adjacent columns that share the
// same leading group path are merged into spanning header cells.
TMHGColumn = class(TMHGHeaderColumn)