A Worksheet is one sheet. Reading and writing cells, formulas, styles, and inserting or
deleting rows and columns all happen here. It raises no events — when you need change
notifications, use the control’s events (Events).
Reading and writing values
using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.Style;
ws.SetNumber(0, 0, 1200);
ws.SetText(0, 1, "Notebook");
ws.SetBoolean(0, 2, true);
// when the type is only known at run time
ws.SetObjectValue(0, 3, DateTime.Today);
object? raw = ws.GetObjectValue(0, 0); // double / string / bool / DateTime / null
string shown = ws.GetDisplayText(0, 0); // the display string with the number format applied
ws.ClearCell(0, 3);
The typed SetNumber / SetText / SetBoolean are what you normally use.
SetObjectValue is the entry point for when the type is only known at run time; it sorts
the value into the right kind for you.
There are three ways to read, depending on what you want back.
| What you want | Method |
|---|---|
| A string for the screen or a report | GetDisplayText(r, c) — the number format is applied |
| A .NET value | GetObjectValue(r, c) |
| To handle each kind yourself, with no allocation | GetValue(r, c) → CellValue |
GetFormattedText(r, c, out uint? color) returns the display string plus the text color the
format code asks for ([Red] and friends).
Formulas
ws.SetFormula(2, 0, "SUM(A1:A2)"); // no leading '='
bool has = ws.HasFormula(2, 0);
string? text = ws.GetFormula(2, 0);
ws.ClearFormula(2, 0);
ws.Recalculate(); // recalculate everything, e.g. after a bulk load
SetFormula parses the formula, records its dependencies, and recalculates that cell and
everything that depends on it. You do not normally need Recalculate(); it is for when
you want to redo everything at once, such as right after loading an xlsx.
Styles
ws.SetCellStyle(0, 0, new StyleRecord { Bold = true });
ws.SetRowStyle(0, new StyleRecord { BackgroundColor = 0xFFEFEFEF });
ws.SetColumnStyle(3, new StyleRecord { TextAlign = HAlign.Right });
ws.RootStyle = ws.RootStyle with { FontFamily = "Segoe UI", FontSize = 11f };
// the result of resolving Cell < Row < Column < Sheet
StyleRecord effective = ws.GetEffectiveStyle(0, 0);
RootStyle is the default for the whole sheet. See
Style Inheritance for how the levels combine.
Inserting and deleting rows and columns
ws.InsertRows(3, 2); // insert two rows at row 3
ws.DeleteRows(10, 1);
ws.InsertColumns(1, 1);
ws.DeleteColumns(5, 3);
Cell values are not the only thing that moves: merges, borders, cell types, conditional
formats, row and column metadata, and formula references all follow along. A reference
that pointed into a deleted range becomes #REF!.
The used range
if (ws.TryGetUsedRange(out var used))
{
// false on an empty sheet. Clamp any full-range loop with this
Console.WriteLine($"{used.ToAddress()} / {used.CellCount} cells");
}
Always clamp here before writing anything that iterates the sheet. A default sheet has 1,048,576 rows, so a naive loop effectively never finishes.
Visiting only the cells that exist
// visits only cells that exist; empty ones never call back
ws.ReadWindow(0, 0, rows: 50, cols: 20, (row, col, value, styleId) =>
{
Console.WriteLine($"{row},{col} = {value.Kind}");
});
ReadWindow visits only cells that hold a value inside the window you give it. It beats
a double loop clamped to the used range, and it is what rendering and export use.
The cell cursor
ws.Cell(0, 0).SetNumber(10);
ws.Cell("B2").SetText("hello").SetStyle(new StyleRecord { Italic = true });
var cursor = ws.Cell("C3");
if (!cursor.IsEmpty)
Console.WriteLine(cursor.ObjectValue);
The CellCursor that Cell(...) returns is a struct that remembers a position, so it
allocates nothing. It reads better when you do several things to the same cell.
Default sizes
ws.SetDefaultSizes(rowHeight: 24, columnWidth: 100);
double dr = Worksheet.DefaultRowHeight; // 20 (logical pixels)
double dc = Worksheet.DefaultColumnWidth; // 80
For individual row heights and column widths, see Rows and Columns.
Everything that is not a cell value
Information that a single cell value cannot express — a merge, a border — lives on its own property.
| Property | What it holds |
|---|---|
Rows / Columns | Sizes, hidden state, default styles (Rows and Columns) |
Merges | Merged ranges (Merged Cells) |
Borders | Cell borders (Borders) |
CellTypes | Cell-type assignments (Cell Types) |
ConditionalFormats | Conditional formats (Conditional Formatting) |
Images | Images (Images) |
RowOutlines / ColumnOutlines | Grouping (Outlines) |
PrintSettings | Print setup (Printing and Page Setup) |
Formulas | The formula engine (Formulas) |
The Has* properties — HasImages, HasCellTypes, HasConditionalFormats,
HasRowOutlines — are safe to call on a sheet that has never used the feature. They answer
without building anything, so calling them every frame is fine.
Performance notes
- Think in used ranges, not sheet dimensions. Clamp with
TryGetUsedRange, or useReadWindow - Whole-row and whole-column formatting belongs on
SetRowStyle/SetColumnStyle. Walking the cells makes the work proportional to the sheet’s dimensions - The model raises no events. To detect changes, listen on the control