从 TDBGrid 和 cxGrid 迁移到 sgcGrid
sgcGrid 是一个 VCL 数据网格 TsgcVCLGrid,可绑定到任何 TDataSet,并在每个版本中提供分组、汇总、筛选、查找面板、分带和已保存的布局。本套件把您使用的每个 TDBGrid 和 cxGrid 成员对应到 sgcGrid 中的对应项,展示来自可编译演示的代码,并如实列出无法对应的部分。
sgcGrid 是一个 VCL 数据网格 TsgcVCLGrid,可绑定到任何 TDataSet,并在每个版本中提供分组、汇总、筛选、查找面板、分带和已保存的布局。本套件把您使用的每个 TDBGrid 和 cxGrid 成员对应到 sgcGrid 中的对应项,展示来自可编译演示的代码,并如实列出无法对应的部分。
使用标准 TDBGrid 的 Delphi VCL 团队,以及正在评估是否续订订阅的 DevExpress cxGrid 团队。
标准 VCL TDBGrid 可以显示和编辑数据集,仅此而已。它没有筛选行、列筛选、分组、页脚或分组汇总、查找面板、分带、最佳列宽,也没有布局的保存和加载。这些功能要么自己写代码,要么借助第三方网格。sgcGrid 把它们全部集成在一个控件中,并绑定到您现有的数据集。
DevExpress cxGrid(ExpressQuantumGrid)是一款功能广泛且成熟的网格。主要把它当作带分组、汇总、筛选和已保存布局的表格视图来使用的团队,可以用 sgcGrid 覆盖这些功能,并获得完整源代码,同时放弃一份远不止网格的订阅。
目前网格的成本:2026-10-20,DevExpress VCL 价格表提供三种订阅,全部包含 ExpressQuantumGrid 控件。最低的是 Express QuantumPack,价格为 999.99 美元,VCL Subscription 为 1,699.99 美元。在 2025 年 9 月 18 日的 Delphi-PRAXiS 帖子中,有用户报告他的 cxGrid 订阅从 400 美元涨到 550 美元,另一位成员引用了 DevExpress 的公告,称新的 Express GridPack 许可证不再出售(现有订阅用户仍可续订)。sgcGrid 按开发者授权,价格从 €199 起,附带完整源代码和一年更新,同时也包含在每个 sgcUI 版本中。
一个 TsgcVCLGrid 和一个 TsgcGridSource_DataSet,设置 DataSet、Grid 和 KeyField。数据集事件会让网格保持同步。
按 FieldName 匹配的设计列、查阅字段、单元格、行和多行选择、行头、自定义绘制、Ctrl+C、分组面板、页脚和分组汇总、筛选行、Excel 风格的列筛选、查找面板、分带、最佳列宽,以及 JSON 布局的保存和加载。
写回数据集的双向编辑(WriteBack)、光标同步(SyncCursor)、插入、删除、粘贴和撤销、卡片视图(ViewStyle)以及面向超大表的服务器模式。试用版包含这些功能供评估。社区版构建不包含它们:演示在社区版中仍可编译,其网格为只读。
下面每个 sgcGrid 名称都在随产品提供的源代码中,演示用到的名称都可以编译。
| TDBGrid | sgcGrid | 版本 |
|---|---|---|
TDBGrid | TsgcVCLGrid(单元 sgcGrid_VCL) | 全部 |
DataSource | TsgcGridSource_DataSet:DataSet、Grid、KeyField,然后 Active := True(单元 sgcGrid_Source_DataSet) | 全部 |
Columns, TColumn.FieldName | Columns、Columns[i].FieldName。设计列会被保留并按 FieldName 匹配;没有列时,AutoColumns 会创建它们 | 全部 |
Title.Caption, Width, Visible | Columns[i].Caption, Width, Visible | 全部 |
TColumn.ReadOnly | Columns[i].Editable | 全部(编辑:注册版) |
TColumn.PickList、查阅字段下拉框 | Columns[i].PickList;对于查阅字段,数据源会从 LookupDataSet 填充列表 | 全部(写入值:注册版) |
TField.DisplayFormat | Columns[i].DisplayFormat | 全部 |
ReadOnly, Options dgEditing | TsgcVCLGrid.ReadOnly(默认 True)和 TsgcGridSource_DataSet.WriteBack | 注册版 |
dgRowSelect | SelectionMode := gsmRow(不设置时为 gsmCell) | 全部 |
dgMultiSelect, SelectedRows | SelectionMode := gsmMultiRow; SelectedRows, SelectedRowCount, GetSelectedKeys, RowSelected[] | 全部 |
dgIndicator | RowHeader := [grhIndicator](还有 grhNumbers、grhCheckBox) | 全部 |
dgColLines, dgRowLines | GridLinesV, GridLinesH | 全部 |
| 当前记录跟随网格 | TsgcGridSource_DataSet.SyncCursor | 注册版 |
OnDrawColumnCell, DefaultDrawColumnCell | 带 aDefaultDraw 的 OnCustomDrawCell;仅处理颜色时,用 OnGetCellStyle 或 FormatRules | 全部 |
OnTitleClick | OnHeaderClick | 全部 |
| Ctrl+C(自定义代码) | 内置(Ctrl+C、Ctrl+Ins);文本来自 SelectionToText | 全部 |
| Insert 和 Ctrl+Del 键 | AllowInsert, AllowDelete, InsertRow, DeleteSelectedRows, OnBeforeDelete | 注册版 |
cxGrid 名称是 DevExpress 的标识符,仅供参考。
| cxGrid | sgcGrid | 版本 |
|---|---|---|
TcxGridDBTableView, DataController.DataSource | TsgcVCLGrid 和 TsgcGridSource_DataSet | 全部 |
TcxGridDBColumn.DataBinding.FieldName | Columns[i].FieldName | 全部 |
TcxGridDBColumn.GroupIndex | Columns[i].GroupIndex; GroupBy(['Field']), ClearGrouping, GroupCount | 全部 |
OptionsView.GroupByBox | ShowGroupPanel | 全部 |
DataController.Groups.FullExpand, FullCollapse | ExpandAll, CollapseAll | 全部 |
OptionsView.Footer | ShowFooter | 全部 |
DataController.Summary.FooterSummaryItems (skSum, skCount, skAverage, skMin, skMax) | Columns[i].SummaryType(gsuSum、gsuCount、gsuAverage、gsuMin、gsuMax)、SummaryFormat;值来自 FooterSummary(aCol)、FooterSummaryText(aCol);SummaryScope | 全部 |
DataController.Summary.DefaultGroupSummaryItems | GroupSummaries,使用分组中的列 SummaryType | 全部 |
FilterRow.Visible | ShowFilterRow, Columns[i].FilterRowOperator | 全部 |
| 列筛选下拉框 | ShowFilterButtons, ShowFilterPopup(aCol), ClearColumnFilter(aCol), ClearColumnFilters, OnFilterChange | 全部 |
FindPanel.DisplayMode | ShowFindPanel, FindText, FindNext, FindPrevious, FindOnlyMatches | 全部 |
TcxGridDBBandedTableView.Bands, Column.Position.BandIndex | Bands (Caption, ParentBand, Visible), Columns[i].Band | 全部 |
ApplyBestFit | BestFitColumns, BestFitColumn(aCol), BestFitOnDblClick, BestFitSampleRows | 全部 |
StoreToIniFile, RestoreFromIniFile | SaveLayoutToFile、LoadLayoutFromFile(JSON),也可以保存到字符串或流、从字符串或流加载;使用 AutoSaveLayout 和 LayoutStorageKey 可自动保存 | 全部 |
OptionsCustomize.ColumnMoving | ColumnMoving | 全部 |
| 列自定义对话框 | ShowColumnChooser | 全部 |
OnCustomDrawCell | OnCustomDrawCell | 全部 |
TcxGridDBCardView(第二个视图) | 在同一个网格上设置 ViewStyle := gvsCard,以及 CardOptions(CaptionField、CardWidth、CardHeight、ShowFieldCaptions) | 注册版 |
每个片段都复制自 MigrateFromDBGrid 演示,该演示可用 Delphi 7 和 Delphi 13(Win32 和 Win64)编译。注释中写明了它所替代的 TDBGrid 或 cxGrid 代码。
迁移前:带 DataSource := dsProducts 的 TDBGrid。迁移后:窗体上的数据源组件。
object SourceDB: TsgcGridSource_DataSet
DataSet = cdsProducts
Grid = GridDB
KeyField = 'ID'
Left = 280
Top = 520
end迁移前:带 dgEditing、dgRowSelect、dgMultiSelect 和 dgIndicator 的 Options。迁移后:
procedure TFormMain.chkOptionsClick(Sender: TObject);
begin
// TDBGrid.ReadOnly / Options dgEditing -> ReadOnly
{$IFDEF SGC_GRID_EDIT}
GridDB.ReadOnly := not chkEditing.Checked;
{$ENDIF}
// dgMultiSelect -> gsmMultiRow, dgRowSelect -> gsmRow, neither -> gsmCell
if chkMultiSelect.Checked then
GridDB.SelectionMode := gsmMultiRow
else if chkRowSelect.Checked then
GridDB.SelectionMode := gsmRow
else
GridDB.SelectionMode := gsmCell;
// dgIndicator -> RowHeader [grhIndicator] (grhNumbers, grhCheckBox too)
if chkIndicator.Checked then
GridDB.RowHeader := [grhIndicator]
else
GridDB.RowHeader := [];
end;迁移前:读取 Column.FieldName 和字段值的 OnDrawColumnCell。迁移后:aCol 是 Columns 的索引,aRow 是 Data 的视图行(CS_LOW_STOCK 是演示中的常量)。
procedure TFormMain.GridDBCustomDrawCell(Sender: TObject; aCanvas: TsgcUICanvas;
aRow, aCol, aLeft, aTop, aRight, aBottom: Integer;
aState: TsgcGridCellDrawStates; var aDefaultDraw: Boolean);
begin
// TDBGrid.OnDrawColumnCell: Column.FieldName and Field.AsInteger.
// sgcGrid: aCol is the Columns index, aRow the view row of Data. The
// background is painted already; aDefaultDraw False would skip the text
if (aRow < 0) or (aRow >= GridDB.Data.ViewCount) or
(GridDB.Columns[aCol].FieldName <> 'Qty') then
Exit;
if StrToIntDef(VarToStr(GridDB.Data.Cells[aRow, aCol]), 0) < CS_LOW_STOCK
then
begin
// a red marker at the left edge of a low stock cell
aCanvas.BeginPath;
aCanvas.Rectangle(aLeft, aTop + 4, aLeft + 4, aBottom - 4);
aCanvas.ClosePath;
aCanvas.FillPath($FFDC2626);
end;
end;迁移前:TDBGrid 上的 dgEditing,以及拖到 cxGrid GroupByBox 的列。迁移后:
procedure TFormMain.FormCreate(Sender: TObject);
begin
DoCreateData;
// TDBGrid: DataSource := dsProducts. sgcGrid: a TsgcGridSource_DataSet
// with DataSet, Grid and KeyField (set on the form). The grid columns are
// designed on the form like TDBGrid.Columns, matched by FieldName
{$IFDEF SGC_GRID_EDIT}
// TDBGrid dgEditing posts edits to the dataset: WriteBack. The current
// record follows the focused row and back: SyncCursor
SourceDB.WriteBack := True;
SourceDB.SyncCursor := True;
{$ELSE}
chkEditing.Checked := False;
chkEditing.Enabled := False;
chkEditing.Caption := 'Editing (Registered edition)';
GridDB.ReadOnly := True;
{$ENDIF}
SourceDB.Active := True;
SourceCx.Active := True;
chkOptionsClick(nil);
// cxGrid: drag a column to the GroupByBox. sgcGrid: ShowGroupPanel, and
// GroupBy from code (cxGridDBColumn.GroupIndex is Columns[i].GroupIndex)
GridCx.GroupBy(['Category']);迁移前:页脚汇总项、分组框、筛选行、查找面板和分带表格视图。迁移后:窗体上的列属性和网格属性。
Columns = <
item
FieldName = 'ID'
Caption = 'ID'
DataType = gdtInteger
Width = 60
SummaryType = gsuCount
Band = 0
end
item
FieldName = 'Product'
Caption = 'Product'
Width = 220
Band = 0
end
item
FieldName = 'Category'
Caption = 'Category'
Width = 140
Band = 0
end
item
FieldName = 'Price'
Caption = 'Price'
DataType = gdtFloat
Width = 110
DisplayFormat = '#,##0.00'
SummaryType = gsuAverage
SummaryFormat = '#,##0.00'
Band = 1
end
item
FieldName = 'Qty'
Caption = 'Qty'
DataType = gdtInteger
Width = 90
SummaryType = gsuSum
Band = 1
end>
FormatRules = <>
ShowFooter = True
ShowGroupPanel = True
GroupSummaries = True
ShowFilterButtons = True
ShowFilterRow = True
ShowFindPanel = True
Bands = <
item
Caption = 'Product'
end
item
Caption = 'Stock and price'
end>迁移前:ApplyBestFit、StoreToIniFile、RestoreFromIniFile 和 TcxGridDBCardView。迁移后:
procedure TFormMain.btnBestFitClick(Sender: TObject);
begin
// cxGridDBTableView.ApplyBestFit -> BestFitColumns (BestFitColumn(i) for
// one column, BestFitOnDblClick for the header divider)
GridCx.BestFitColumns;
end;
function TFormMain.DoLayoutFile: string;
begin
Result := ExtractFilePath(ParamStr(0)) + 'migratefromdbgrid_layout.json';
end;
procedure TFormMain.btnSaveLayoutClick(Sender: TObject);
begin
// cxGridDBTableView.StoreToIniFile -> SaveLayoutToFile (JSON: columns,
// widths, order, sort, grouping, filters, summaries, bands, view)
GridCx.SaveLayoutToFile(DoLayoutFile);
DoShowStatus('layout saved to ' + ExtractFileName(DoLayoutFile));
end;
procedure TFormMain.btnLoadLayoutClick(Sender: TObject);
begin
// cxGridDBTableView.RestoreFromIniFile -> LoadLayoutFromFile
if GridCx.LoadLayoutFromFile(DoLayoutFile) then
DoShowStatus('layout loaded')
else
DoShowStatus('no saved layout');
end;
procedure TFormMain.chkCardViewClick(Sender: TObject);
begin
{$IFDEF SGC_GRID_ADVANCED}
// cxGridDBCardView (a second view of the level) -> ViewStyle gvsCard of
// the same grid, CardOptions for the caption field and the card size
if chkCardView.Checked then
GridCx.ViewStyle := gvsCard
else
GridCx.ViewStyle := gvsTable;
{$ENDIF}
end;数据层保持不变,只有网格及其选项会变化。
安装 sgcGrid 软件包,然后在窗体上放置一个 TsgcVCLGrid 和一个 TsgcGridSource_DataSet。
把数据源的 DataSet 设为旧网格使用的数据集(网格不再需要 TDataSource),然后设置 Grid 和 KeyField,后者是原位更新和写回所需的唯一字段。
添加带 FieldName、Caption、Width、DisplayFormat 和 Editable 的 Columns,或者让 Columns 保持为空,由 AutoColumns 根据字段自动生成。
dgEditing 对应 ReadOnly 和 WriteBack,dgRowSelect 和 dgMultiSelect 对应 SelectionMode,dgIndicator 对应 RowHeader,当前记录用 SyncCursor。
把 OnDrawColumnCell 的代码移到 OnCustomDrawCell,如果只设置颜色,则移到 OnGetCellStyle。
ShowGroupPanel、ShowFooter、GroupSummaries、ShowFilterRow、ShowFilterButtons、ShowFindPanel;汇总用 Columns[i].SummaryType,分带用 Bands 和 Columns[i].Band,布局用 SaveLayoutToFile。
数据集打开后,在数据源上设置 Active := True。
规划迁移之前请先阅读这些内容。
ViewStyle,而不是某一层级上的第二个视图;主从关系是 Detail(注册版),不是 cxGrid 的层级。SummaryType。不支持在一列上设置多个汇总项。TDBGrid.SelectedField 和 SelectedIndex 没有直接对应项:请使用 SelectedCol 和 Columns[SelectedCol].FieldName。一个包含 40 件产品、带 Category 查阅字段的 TClientDataSet,显示在两个页面上:“From TDBGrid”和“From cxGrid”。
sgcUI 安装程序会把它放在 Demos\Grid\Delphi\MigrateFromDBGrid:MigrateFromDBGrid.dpr、frmMain.pas、frmMain.dfm 和一个 readme.html。
在 Delphi 7 到 13 的任意版本中打开 MigrateFromDBGrid.dpr,构建 Win32 或 Win64 并运行。每个页面都会把 TDBGrid 或 cxGrid 的概念与对应的 sgcGrid 项并排列出。
MigrateFromDBGrid.exe /test 会检查列和选项的映射、复制文本、分组、页脚汇总、分带、布局往返,以及在注册版中的写回和卡片视图,全部检查通过后以 0 退出。
公平地说:cxGrid 提供 sgcGrid 没有的东西,例如在一个网格控件中使用多个视图和层级,以及 INI 布局,而且它还附带一大套其他控件。如果您的应用依赖这些,迁移的工作量会比本页显示的更大。如果您使用的正是上面列出的网格功能,并希望以单个产品的形式、带完整源代码、在 Delphi 7 到 13 中使用,那么 sgcGrid 是合适的选择。完整功能列表请参阅 sgcGrid 产品页面。