V4 V5

StyleRecord is an immutable record describing a cell’s formatting. It replaces V4’s WorksheetRangeStyle plus PlainStyleFlag.

The namespace is unvell.ReoGrid.Core.Style.

Each property and what it does

The basics

using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.Style;

ws.SetCellStyle(0, 0, new StyleRecord
{
	FontFamily = "Segoe UI",
	FontSize = 12f,
	Bold = true,
	Color = 0xFF333333,             // text color, ARGB
	BackgroundColor = 0xFFEFEFEF,   // background color, ARGB
	TextAlign = HAlign.Center,
	VerticalAlign = VAlign.Middle,
});

Properties

Every one is nullable. null means “not set — inherit from the level above”.

PropertyTypeWhat it is
FontFamilystring?Font name
FontSizefloat?Font size in points
Bold / Italic / Underline / Strikethroughbool?Text decoration
Coloruint?Text color (ARGB)
BackgroundColoruint?Background color (ARGB)
TextAlignHAlign?General / Left / Center / Right
VerticalAlignVAlign?Bottom / Middle / Top
TextWrapModeTextWrap?None / Wrap / BreakWord
Indentushort?Indent steps
RotationAnglefloat?Text rotation in degrees (-90..90); TextRotation.Stacked (255) for vertical text

General in TextAlign means what it does in Excel: numbers right, text left.

Colors are uint ARGB

Not System.Drawing.Color. Keeping the core and the I/O independent of System.Drawing is what makes PDF export and headless use on Linux possible.

0xFFEFEFEF
  ^^        alpha (FF = opaque)
    ^^^^^^  RGB

To turn one into a Color in the WinForms layer, use Color.FromArgb(unchecked((int)argb)).

Applying a change

// StyleRecord is immutable; build a variant with a with-expression
var current = ws.GetCellStyle(0, 0) ?? StyleRecord.Default;
ws.SetCellStyle(0, 0, current with { Bold = true });

GetCellStyle returns only what was set directly on that cell — nothing inherited — and null when nothing was. For the resolved result, use GetEffectiveStyle.

null is not false

// null means "not set" - inherit from above. That is not the same as false
var s = new StyleRecord { Bold = true, Italic = null };

var cleared = s with { Bold = null };   // drop the bold setting entirely
var explicitOff = s with { Bold = false };   // not bold, even though the level above is

V4’s PlainStyleFlag tracked which fields were in effect separately. In V5 being nullable is that mechanism, so the flags can never drift out of sync with the values.

Wrapping and placement

ws.SetCellStyle(0, 0, new StyleRecord { TextWrapMode = TextWrap.Wrap });
ws.SetCellStyle(1, 0, new StyleRecord { TextWrapMode = TextWrap.BreakWord });
ws.SetCellStyle(2, 0, new StyleRecord { Indent = 2 });
ws.SetCellStyle(3, 0, new StyleRecord { RotationAngle = 45f });

Wrap breaks between words; BreakWord breaks between characters. Text in a cell with no wrap setting overflows into the neighbour when that neighbour is empty (as in Excel). A newline (\n) always breaks the line, wrapping or not.

Line breaking is done by the core, so WinForms, WPF, Avalonia and PDF all break at the same place. Text without spaces — Japanese, for instance — breaks between characters, with kinsoku rules applied so that a line never starts with 。」) or ends with 「(.

Rotated and vertical text

// degrees counter-clockwise: 45 tilts up to the right, -45 down
ws.SetCellStyle(0, 0, new StyleRecord { RotationAngle = 45f });
ws.SetCellStyle(0, 1, new StyleRecord { RotationAngle = -90f });

// stacked text (one glyph per line, upright, top to bottom) — a marker, not an angle
ws.SetCellStyle(0, 2, new StyleRecord { RotationAngle = TextRotation.Stacked });

// back to level text
ws.SetCellStyle(0, 3, new StyleRecord { RotationAngle = 0f });

// the helpers
var st = ws.GetEffectiveStyle(0, 2);
bool rotated = TextRotation.IsRotated(st.RotationAngle);       // true for stacked too
bool stacked = TextRotation.IsStacked(st.RotationAngle ?? 0f); // true only for stacked

// clamp into -90..90; Stacked (255) passes through untouched
float safe = TextRotation.Normalize(120f);   // -> 90

_ = (rotated, stacked, safe);

RotationAngle is degrees counter-clockwise, valid from -90 to 90 — the same numbers Excel’s alignment dialog shows. Anything outside that range is clamped by TextRotation.Normalize.

Stacked text is not an angle

TextRotation.Stacked (255) is not an angle at all: it is Excel’s vertically stacked text. Glyphs stay upright and are laid one per line, reading top to bottom.

That 255 is OOXML’s own value (<alignment textRotation="255"/>), so keeping it as-is lets a stacked cell round-trip through xlsx untouched, with no second style property that only one file format would ever set.

Stacking works on runes, not chars, so a surrogate pair — an emoji, a rare kanji — stays one glyph instead of splitting across two lines.

How it is drawn

Rotated text is laid out horizontally in its own frame and then turned as a block about its centre. Line breaking, measurement and baselines therefore go through the same shared path as unrotated text, and the four surfaces (WinForms, WPF, Avalonia, PDF) agree.

  • The rotated bounding box is placed by the cell’s alignment and then clipped to the cell, so rotated text never spills into a neighbour.
  • Wrapping measures the room along the rotated direction (the cell’s width at 0°, its height at 90°, interpolated in between).
  • Stacked text is always centred horizontally.
  • Row-height and column-width auto-fit measure the rotated size too, so a cell fits the way it is drawn.

Rich-text cells are not rotated — they are drawn level.

I/O and UI

  • reogrid-jsonrotationAngle (a V5 extension), omitted when 0.
  • XLSX — round-trips as textRotation. OOXML packs three meanings into that one attribute (0-90 counter-clockwise, 91-180 as “90 plus the clockwise angle”, 255 for stacked), and both the reader and the writer unpack it.
  • All three controls expose SetSelectionRotation(float?); null clears it.
  • Studio: Format ▸ Text Rotation (45° / -45° / 90° / -90° / Vertical Text / Custom Angle… / None).

Clearing

ws.SetCellStyle(0, 0, null);   // clear this cell's own settings (back to pure inheritance)

Sharing

Identical StyleRecord values are merged into one automatically, by record value equality — so a fresh new every time still shares one instance. There is no need to hoist a style into a variable to reuse it.

See Performance and Memory.

Was this article helpful?