Spreadsheet
TsgcHTMLComponent_Spreadsheet: 面向 Delphi、C++ Builder 和 .NET Web 应用程序的公式网格。用户像在 Excel 中一样输入值和公式,服务器负责计算,每次更改在保存之前都会到达你的代码。
TsgcHTMLComponent_Spreadsheet: 面向 Delphi、C++ Builder 和 .NET Web 应用程序的公式网格。用户像在 Excel 中一样输入值和公式,服务器负责计算,每次更改在保存之前都会到达你的代码。
把它放到窗体上,用代码填充其工作表并渲染。每次编辑都会发送到服务器,服务器检查编辑、重新计算依赖它的单元格,并返回已更改的单元格。浏览器中没有公式引擎,因此 Delphi 和 .NET 计算出相同的值。
TsgcHTMLComponent_Spreadsheet,工作表模型 TsgcHTMLSpreadsheetSheet 位于其 Sheet 属性中
带有工具栏、公式栏和状态栏的虚拟化网格,使用纯 JavaScript 和 CSS,不依赖外部库
Delphi, C++ Builder, .NET
用 SetCellByName 在代码中设置单元格。OnCellChange 检查并保存用户更改的每个单元格,启用 LiveSync 后,宿主会在更改后广播 GetLiveSyncFragmentHTML,使每个打开的页面重新加载已更改的单元格。
uses
sgcHTML_Formula, sgcHTML_Component_Spreadsheet, sgcHTMX_Engine_Server;
// 在窗体上: 一个 TsgcHTMLComponent_Spreadsheet (Spreadsheet1),设置为
// LiveSync = True,以及页面的 TsgcHTMX_Engine_Server (HTMXEngine1)
procedure TMainModule.DataModuleCreate(Sender: TObject);
begin
Spreadsheet1.Sheet.SetCellByName('A1', 'Item');
Spreadsheet1.Sheet.SetCellByName('B1', 'Amount');
Spreadsheet1.Sheet.SetCellByName('A2', 'Rent');
Spreadsheet1.Sheet.SetCellByName('B2', '1200');
Spreadsheet1.Sheet.SetCellByName('A3', 'Travel');
Spreadsheet1.Sheet.SetCellByName('B3', '340.50');
Spreadsheet1.Sheet.SetCellByName('A4', 'Total');
Spreadsheet1.Sheet.SetCellByName('B4', '=SUM(B2:B3)');
end;
// 用户更改的每个单元格: 输入、粘贴、填充或清除
procedure TMainModule.Spreadsheet1CellChange(Sender: TObject; aRow, aCol: Integer;
const aOldRaw: string; var aNewRaw: string; var aAccept: Boolean);
begin
if aRow = 0 then
aAccept := False // 标题行保持不变
else
StoreCell(sgcHTMLCellName(aRow, aCol), aNewRaw); // 你自己的存储: 一个表、一个文件
end;
// 更改已应用并重新计算: 通知每个打开的页面
procedure TMainModule.Spreadsheet1Change(Sender: TObject;
aSheet: TsgcHTMLSpreadsheetSheet);
begin
HTMXEngine1.BroadcastFragment(Spreadsheet1.GetLiveSyncFragmentHTML);
end;
// GET /budget.xlsx: 包含公式和样式的工作簿形式的工作表
procedure TMainModule.ExportBudget(aStream: TStream);
begin
Spreadsheet1.SaveToXLSX(aStream);
end;
@* Views/Sheets/Budget.cshtml: 团队共享预算 *@
@model BudgetModel
<sgc-spreadsheet name="budget" rows="40" cols="14"
frozen-rows="1" frozen-cols="1" css-height="560px"
allow-insert-delete="false" live-sync="true"
on-get-sheet="Model.GetSheet"
on-cell-change="Model.CellChange"
on-change="Model.Changed" />
using esegece.sgcWebSockets;
var budget = new TsgcHTMLComponent_Spreadsheet();
budget.LiveSync = true;
budget.Sheet.SetCellByName("A1", "Item");
budget.Sheet.SetCellByName("B1", "Amount");
budget.Sheet.SetCellByName("A2", "Rent");
budget.Sheet.SetCellByName("B2", "1200");
budget.Sheet.SetCellByName("B3", "340.50");
budget.Sheet.SetCellByName("A4", "Total");
budget.Sheet.SetCellByName("B4", "=SUM(B2:B3)");
budget.OnCellChange += CellChange;
budget.OnChange += Changed;
// OnCellChange: 检查并保存一个单元格
public void CellChange(object Sender, int aRow, int aCol, string aOldRaw,
ref string aNewRaw, ref bool aAccept)
{
if (aRow == 0)
aAccept = false;
else
store.Save(aRow, aCol, aNewRaw);
}
// OnChange: 已应用并重新计算,每个打开的页面重新加载已更改的单元格
public void Changed(object Sender, TsgcHTMLSpreadsheetSheet aSheet)
{
_ = hub.BroadcastAsync(((TsgcHTMLComponent_Spreadsheet)Sender).GetLiveSyncFragmentHTML());
}
// OnGetSheet: 每个用户一个工作表
public void GetSheet(object Sender, ref TsgcHTMLSpreadsheetSheet? aSheet)
{
aSheet = sheets.ForUser(User.Identity.Name);
}
// GET /budget.xlsx
using var ms = new MemoryStream();
budget.SaveToXLSX(ms);
你熟悉的 Excel 键盘、剪贴板和公式,集中在一个由服务器保存的工作表中。
以 = 开头的单元格是公式: 支持运算符 + - * / ^ % &、比较、括号、单元格引用以及 A1:C5 这样的区域,绝对部分写作 $A$1、A$1 或 $A1。优先级以及文本到数字的转换与 Excel 相同。错误显示为 #DIV/0!、#VALUE!、#REF!、#NAME?、#NUM! 和 #N/A,#CIRC! 标记处于循环引用中的单元格。
数学和统计: SUM、AVERAGE、MIN、MAX、COUNT、COUNTA、COUNTBLANK、PRODUCT、ROUND、ROUNDUP、ROUNDDOWN、INT、MOD、ABS、SQRT、POWER、PI。逻辑和信息: IF、IFERROR、AND、OR、NOT、TRUE、FALSE、ISBLANK、ISNUMBER、ISTEXT、ISERROR。文本: CONCAT、CONCATENATE、LEN、LEFT、RIGHT、MID、UPPER、LOWER、TRIM、VALUE。
条件函数: SUMIF、COUNTIF 和 AVERAGEIF,条件可以是 ">10"、"<>x" 以及通配符 * 和 ?。查找: VLOOKUP 和 HLOOKUP (精确或近似匹配)、INDEX 和 MATCH。日期: DATE、YEAR、MONTH、DAY、TODAY,基于 Excel 序列号。财务: PMT、FV、PV、NPV。共 54 个函数,语义与 Excel 一致,公式栏会补全函数名称。
加粗、斜体、对齐、文字颜色和填充颜色,以及每个单元格的数字格式: 常规、数字、货币、百分比、日期或文本,支持 0 到 10 位小数、千位分隔符、货币符号和日期格式。如果 sgcHTML 当前使用的区域设置定义了小数和千位分隔符,显示时就采用这些分隔符。
方向键、Tab 和 Shift+Tab、Enter 和 Shift+Enter、Home、End、Ctrl+Home、Ctrl+End、Ctrl+方向键、PageUp 和 PageDown。按 F2 或直接输入开始编辑,Esc 取消,Delete 清除。Shift+方向键、Shift+单击或鼠标拖动选择区域,单击标题选择整行或整列,Ctrl+D 和 Ctrl+R 向下和向右填充,Ctrl+Z 和 Ctrl+Y 撤销和重做用户自己的编辑。
公式栏显示单元格的原始内容,其名称框可跳转到某个单元格: 输入 B12 并按 Enter。编辑公式时,其中的引用会以颜色突出显示,用户可以单击单元格或拖动区域将其输入公式。
复制、剪切和粘贴使用制表符分隔的文本,这是 Excel 和 Google 表格放在剪贴板上的格式,因此单元格区块可以在它们之间双向移动。在工作表内复制并粘贴或填充到其他位置的公式,其相对引用会随之移动,绝对引用保持不变。
可以通过工具栏,或单元格或标题的上下文菜单,在上方或下方插入行,在左侧或右侧插入列,以及删除行和列。每个公式中的每个引用都会像 Excel 一样调整,指向已删除单元格的引用会变成 #REF!。拖动标题边缘可调整列宽,行和列可以冻结。
导出到 Excel 会下载一个 .xlsx 文件,其中包含公式及其值、单元格样式和数字格式、列宽以及冻结窗格。导入会读取 .xlsx 文件 (包含公式和样式) 或 .csv 文件,用户确认后将替换当前工作表。
启用 LiveSync 后,打开同一工作表的人可以看到彼此的更改: 其他用户更改的单元格会重新加载并闪烁。每次被接受的更改都会增加工作表的修订号,错过某次修订的页面会重新加载整个工作表。
状态栏显示所选区域的求和、平均值和计数,指示器说明更改是否已保存。页面中只包含可见的行和列,因此 10,000 行乘 104 列的工作表也能流畅滚动。即使没有 JavaScript,页面仍包含单元格的纯表格,爬虫可以读取。
工作表是一个 WAI-ARIA 网格,带有行数和列数以及索引,礼貌型实时区域会播报单元格引用及其值。它跟随页面主题,在 dir="rtl" 下镜像显示,遵循减少动态效果的设置,并适用于手机宽度: 触摸滚动、点按选择和双击编辑。
您最常使用的成员。
Sheet 是一个 TsgcHTMLSpreadsheetSheet。SetCell(aRow, aCol, aRaw) 和 SetCellByName('B2', '=A1*2') 设置单元格,Raw、Value、Text 和 Style 读取单元格。以 = 开头的 raw 是公式,开头的撇号则强制为文本。API 中的行和列从 0 开始,因此 A1 是第 0 行第 0 列。Lock 和 Unlock 用于保护从你自己的线程所做的更改。
OnCellChange(Sender, aRow, aCol, aOldRaw, aNewRaw, aAccept) 对用户更改的每个单元格运行。修改 aNewRaw 可更正值,将 aAccept 设为 False 可拒绝,或者直接保存。OnChange(Sender, aSheet) 在更改应用并重新计算之后触发,适合在这里广播 GetLiveSyncFragmentHTML。
OnGetSheet(Sender, aSheet) 返回发出请求的用户的工作表。它在渲染时和每次请求时调用,因此一个组件就能为所有用户提供服务,每个用户都有自己的工作表。
浏览器发送用户输入的内容,其余由服务器完成: 应用更改,通过依赖关系图重新计算依赖它的单元格,并且只返回已更改的单元格。Recalculate 执行完整计算,Evaluate 在任何单元格之外计算公式。Delphi 和 .NET 给出相同的结果、相同的 JSON 和相同的 XLSX 字节。
服务器从不信任浏览器。它检查 ReadOnly、开关 AllowFormulas、AllowFormatting、AllowInsertDelete、AllowImport 和 AllowExport,对照 MaxRows (10000) 和 MaxCols (104) 检查坐标,还检查 MaxCells (100000)、MaxCellLength (8192) 和 MaxImportSize (5 MB),然后对每个单元格调用 OnCellChange。OnImport 可以拒绝文件。
RowCount (100) 和 ColCount (26) 决定工作表大小,FrozenRows 和 FrozenCols 固定标题,DefaultColWidth、RowHeight 和 CSSHeight 设置尺寸,ShowToolbar、ShowFormulaBar 和 ShowStatusBar 打开或关闭各个栏。ExportFileName 为下载的工作簿命名。
SaveToXLSX 和 LoadFromXLSX 在代码中写入和读取工作簿,包括公式和样式。在工作表上,SaveToJSON 和 LoadFromJSON 使用 Delphi 和 .NET 逐字节写出相同内容的单一 JSON 格式保存它,SaveToCSV 和 LoadFromCSV 使用 RFC 4180 和原始值,LoadFromDataSet 加载标题行和记录。
InsertRows、DeleteRows、InsertCols 和 DeleteCols(aIndex, aCount) 像 Excel 一样调整每个引用。ShiftFormula(aFormula, aDeltaRow, aDeltaCol) 移动公式的相对引用并保持绝对引用不变,这与浏览器在粘贴和填充时应用的规则相同。
工作表会发送到 HTMX 引擎的组件事件端点,或发送到 ActionURL。两者都没有时,它就是工作表的只读查看器。
在 Razor 视图中,<sgc-spreadsheet> Tag Helper 渲染工作表,其请求由 UseSgcHtml 的 sgcHTML 中间件应答。其属性有 name、read-only、rows、cols、frozen-rows、frozen-cols、css-height、allow-* 开关、live-sync、action-url,以及处理程序 on-get-sheet、on-cell-change 和 on-change。
Demos\60.HTML\01.RunTime\35.Spreadsheet 是 sgc Sheets,一个在端口 8137 上运行的服务器,包含三个工作表: 所有人实时编辑的团队预算,带有 SUM 合计、占总数的百分比、货币格式,以及每次更改都会刷新的图表; 每位访问者各有一个工作表的贷款计算器,带有 PMT、向下填充的摊销表和余额图表; 以及按地区统计的销售额,带有 SUMIF 和 VLOOKUP、XLSX 导出和导入。设计时、Lazarus 和 .NET 版本与之对应。