sgcSpreadsheet: an Excel Style Spreadsheet for Delphi and .NET

· Components
sgcSpreadsheet: an Excel Style Spreadsheet for Delphi and .NET
✦  sgcUI · sgcSpreadsheet

A spreadsheet.
Inside your application.

sgcSpreadsheet is the new Excel style spreadsheet control of sgcUI. Your users type values and formulas, work across several sheets and draw charts from their cells. Your code fills the workbook, adds its own functions and reads and writes XLSX files that Excel opens.

  • VCL
  • FireMonkey
  • C++ Builder
  • WinForms
  • WPF
  • Avalonia
sgcSpreadsheet with a sales table and column, line and pie charts drawn from its cell ranges
211worksheet functions
7chart types
5UI frameworks
XLSXload and save

Sooner or later a business application needs a sheet. Users plan a budget, price a quote, calculate costs or check the numbers a customer sent in an Excel file, and they expect formulas, several sheets and charts while they do it. A plain grid is not enough for any of that.

sgcSpreadsheet is an Excel style spreadsheet control built on sgcGrid and on a formula engine shared in sgcUICore. It keeps a real workbook with sheets, defined names and dynamic arrays, it recalculates only the cells a change affects, and it loads and saves XLSX. The same engine drives the VCL and FireMonkey controls for Delphi and C++ Builder and the WinForms, WPF and Avalonia controls for .NET.

sgcSpreadsheet in Action

Formulas, spilling arrays, several sheets, live charts and XLSX files on every platform. Also on YouTube.

Formulas Your Users Already Know

People who work with numbers think in Excel formulas. In sgcSpreadsheet a text that starts with = is a formula, and the engine computes 211 worksheet functions: math, statistics, text, date and time, lookup, logical, information and dynamic array functions. The engine lives in sgcUICore, so sgcGrid formulas give the same results.

=SUM(B2:B13)
211 functionsFrom SUM and AVERAGE to TEXT and the date and time functions, one engine for every platform.
=XLOOKUP(C2,…)
XLOOKUP includedThe lookup functions your users reach for first, with references to other sheets such as =Sheet2!A1.
=SORT(A2:A20)↓
Arrays that spillSEQUENCE, SORT, FILTER and UNIQUE fill the cells below them, with #SPILL! and A1# references.
B2 → B14 → D3
Incremental recalculationA dependency graph knows which cells read which, so a change recalculates only the cells that depend on it.
=SUM(Sales)*(1+Rate)
Defined namesFormulas use meaningful names instead of cell addresses, with workbook and sheet scope.
'007
Forced textA leading apostrophe stores the text as typed, so a code such as 007 stays a text.
The Functions demo with math formulas and their results, one sheet per category
The Functions demo: one sheet per category, each formula next to its result

Sheets, Names and Spilling Arrays

A spreadsheet is a workbook with one or more sheets. The tabs under the grid switch the sheet and the + tab adds one, and a renamed sheet is renamed in every formula that refers to it. From code, AddSheet, ActiveSheet and SheetCount manage the sheets, and Grid.Sheets.Names keeps the defined names of the workbook.

The method below fills a price list, names its ranges, and builds a summary sheet that reads it. SORT returns the whole list of products, which spills into the cells below, and A1# refers to that spilled range however long the list grows. XLOOKUP finds a price and the VAT name adds the tax.

uses
  sgcSpreadsheet_VCL;

procedure TForm1.BuildPriceList;
var
  vSummary: Integer;
begin
  // Sheet1: a price list, row and column are 0-based, (0, 0) is A1
  sgcVCLSpreadsheet1.SetCellInput(0, 0, 'Product');
  sgcVCLSpreadsheet1.SetCellInput(0, 1, 'Price');
  sgcVCLSpreadsheet1.SetCellInput(1, 0, 'Monitor');
  sgcVCLSpreadsheet1.SetCellInput(1, 1, '219');
  sgcVCLSpreadsheet1.SetCellInput(2, 0, 'Keyboard');
  sgcVCLSpreadsheet1.SetCellInput(2, 1, '49');
  sgcVCLSpreadsheet1.SetCellInput(3, 0, 'Mouse');
  sgcVCLSpreadsheet1.SetCellInput(3, 1, '25');
  // defined names: formulas use them instead of cell addresses
  sgcVCLSpreadsheet1.Grid.Sheets.Names.Add('Products', '=Sheet1!$A$2:$A$4');
  sgcVCLSpreadsheet1.Grid.Sheets.Names.Add('Prices', '=Sheet1!$B$2:$B$4');
  sgcVCLSpreadsheet1.Grid.Sheets.Names.Add('VAT', '0.21');
  // a second sheet that reads the first one
  vSummary := sgcVCLSpreadsheet1.AddSheet('Summary');
  sgcVCLSpreadsheet1.ActiveSheet := vSummary;
  // a dynamic array: the sorted names spill into A1:A3
  sgcVCLSpreadsheet1.SetCellInput(0, 0, '=SORT(Products)');
  // A1# is the whole spilled range, however long the list grows
  sgcVCLSpreadsheet1.SetCellInput(0, 2, 'Items');
  sgcVCLSpreadsheet1.SetCellInput(0, 3, '=ROWS(A1#)');
  // XLOOKUP finds the price of a product, VAT included
  sgcVCLSpreadsheet1.SetCellInput(1, 2, 'Monitor');
  sgcVCLSpreadsheet1.SetCellInput(1, 3, '=XLOOKUP(C2,Products,Prices)*(1+VAT)');
  // every sheet, formula and name goes to a file that Excel opens
  sgcVCLSpreadsheet1.SaveToXLSX('prices.xlsx');
end;

The summary shows the three products in order, 3 items, and 264.99 for the monitor. Change a price on the first sheet and only the cells that depend on it recalculate. FreezePanes(1, 0) keeps a header row in place while the rest of the sheet scrolls, and the formula bars TsgcVCLSpreadsheetFormulaBar and TsgcFMXSpreadsheetFormulaBar give your users a name box and the formula of the current cell.

Your Own Functions

Engineering, finance and laboratory applications need their own calculations. The formula engine has a function registry, and a function registered there works in every formula of the spreadsheet, of sgcGrid and of a TsgcCalcEvaluator, exactly like a built in one. The handler is a plain function, registered from the initialization section of a unit.

unit MyFunctions;

interface

implementation

uses
  sgcUICore_Calc;

{ =MARKUP(price, [rate]): the price plus a margin, 20% by default }
function DoMarkup(aCall: TsgcCalcCall): Variant;
var
  vPrice, vRate: Double;
  vError: string;
begin
  vError := aCall.Number(0, vPrice);
  vRate := 0.2;
  if (vError = '') and (aCall.Count > 1) then
    vError := aCall.Number(1, vRate);
  if vError <> '' then
    Result := vError
  else
    Result := vPrice * (1 + vRate);
end;

initialization
  // MARKUP takes 1 or 2 arguments
  sgcCalcRegisterFunction('MARKUP', DoMarkup, 1, 2);

end.

Add the unit to the project and your users can type =MARKUP(B2). With dynamic arrays the engine calls the function for each element of an array argument, so =MARKUP(B2:B4, 0.3) spills one result per price and =SUM(MARKUP(B2:B4)) works with no extra code. A function that needs the cells of a range is registered with the cfRanges flag and walks them with an iterator.

Charts from Cell Ranges

A chart shows the values of a range of cells and floats over the sheet, like a chart in Excel. It is drawn by sgcCharts, and when a cell of the range changes, or a formula of the range gives a new value, the chart follows. Users move and resize charts with the mouse, and each sheet has its own charts.

XLSX In, XLSX Out

Customers send XLSX files and expect XLSX files back. LoadFromXLSX reads every sheet of a workbook and SaveToXLSX writes every sheet, from a file name or a stream, so a file round trips between your application and Excel.

VCL, FireMonkey and .NET

Delphi and C++ Builder get TsgcVCLSpreadsheet for VCL and TsgcFMXSpreadsheet for FireMonkey, each with its formula bar. For .NET there are TsgcWinFormsSpreadsheet, TsgcWpfSpreadsheet and TsgcAvaloniaSpreadsheet, also with formula bars, in the NuGet packages esegece.sgcSpreadsheet, esegece.sgcSpreadsheet.WinForms, esegece.sgcSpreadsheet.Wpf and esegece.sgcSpreadsheet.Avalonia. The .NET controls use the same names as the Delphi ones, and a custom function can be a lambda.

using esegece.sgcSpreadsheet;
using esegece.sgcUICore;

// once at startup, before the first formula is parsed
sgcUICore_Calc.sgcCalcRegisterFunction("MARKUP", aCall =>
{
    double vPrice;
    double vRate = 0.2;
    string vError = aCall.Number(0, out vPrice);
    if ((vError.Length == 0) && (aCall.Count > 1))
        vError = aCall.Number(1, out vRate);
    if (vError.Length > 0)
        return vError;
    return vPrice * (1 + vRate);
}, 1, 2);

// sheet is a TsgcWinFormsSpreadsheet: open a workbook made in Excel
sheet.LoadFromXLSX("prices.xlsx");
sheet.ActiveSheet = 0;
// the custom function spills over the whole price column
sheet.SetCellInput(0, 2, "=MARKUP(B2:B4, 0.3)");
// a column chart of the range, drawn by sgcCharts
TsgcSpreadsheetChart chart = sheet.AddChart(TsgcSpreadsheetChartType.sctColumn, "A1:B4");
chart.Title = "Prices";
sheet.SaveToXLSX("prices-markup.xlsx");

Run the Demos

The Delphi and C++ Builder demos install with the sgcUI trial, and the .NET demos ship with the Registered .NET download.

The VCL Basic demo with a sales workbook and the formula bar
Basic: a sales workbook with a summary sheet, a frozen header row and the formula bar

Where sgcSpreadsheet Is Included

sgcSpreadsheet is included in sgcUI Enterprise and in the All-Access bundle, and it is sold on its own as a single package for Delphi and C++ Builder and for .NET. Installing sgcSpreadsheet installs sgcGrid and sgcCharts too, so the grid and the charts always work. There is no free Community edition. See the pricing page for the details.

Give Your Users a Spreadsheet

sgcSpreadsheet is part of sgcUI for Delphi and C++ Builder, on VCL and FireMonkey, and for .NET on WinForms, WPF and Avalonia.

Questions or feedback? Get in touch, you will get a reply from the people who wrote the code.