V4 V5

This is a step-by-step guide for migrating an application built with ReoGrid V4 to V5 (ReoGrid One).

There is no automated migration tool. V5 is a complete redesign with no backward compatibility, and your code will need to be rewritten. This page describes the order in which to do that work with the least amount of rework.

For the full list of API changes, see Differences from V4. This page focuses on the process.


1. First, decide whether to migrate

Migration is not mandatory. V4 continues as the 4.x line, and your existing applications will not stop working. License keys are shared as well.

Migration is worth considering if any of the following applies.

SituationWhat V5 solves
Memory or speed becomes a problem beyond tens of thousands of rowsAbout 16–23 B per cell (V4: about 150–200 B); work is proportional to the visible range
You want to work with sheets on a server or in batch processingWorkbook / Worksheet are UI-independent; everything works without a screen
You want to run on Linux / macOSCovered by the Avalonia and headless editions
You want to export PDF directlyOne line with PdfExporter; Japanese fonts embedded
You want to exchange files with the Web editionreogrid-json is the shared format

Conversely, do not migrate yet if either of the following applies.

  • You cannot move to .NET 10. V5 supports .NET 10 and later only (net48 / net8 are not supported)
  • You depend on one of the “features not yet in V5” listed below

Estimating the effort

Most of the rewriting is mechanical. As a rough guide, an application centered on cell operations, formatting and I/O can be migrated by rewriting a few hundred lines. What takes time is code that depends on V4’s fine-grained events, and working around the feature gaps described below.


2. Pre-migration check — does V5 have the features you use?

Before you start, go through the following list. If anything applies, that will be your biggest challenge.

Features not yet in V5

FeatureTypical V4 usageOptions for now
ChartsThe chart types under Chart/Pair with a separate charting library / stay on V4
Shapes and text boxesDrawing/Images still work in V5
Data bindingIDataSource / ArrayDataSourceWrite values in a loop (IRowDataSource for large data)
ReoScriptRunScriptRemoved. It will not return
Some cell typesDatePicker / RadioButton / NumberInput / image-based typesCan be implemented as custom cell types

Charts, shapes and data binding are not available in V5.0. There is no committed schedule for them — they will be considered based on demand (see the roadmap section of the release notes). Because the license is version-independent (one license covers V4 and V5), you can simply keep using V4 for as long as you need those features.

In V5, but under a different API

These four are available — only the names and the call shape changed.

V4V5
Commentws.SetComment(row, col, text)Cell Notes
IValidator / CellValidationws.AddValidation(range, rule)Data Validation
TextSearch/FindEngine.FindNext(...) / FindEngine.ReplaceAll(...)
Cell and sheet protectionws.Protect() / ws.SetRangeLock(range, LockState.Unlocked)

File formats

V4 formatV5
RGF (.rgf)Cannot be read. Migrate via XLSX (next section)
XLSXReads as-is
BinaryFormatterRemoved

Licensing

Keys already issued for V4 work as-is with V5. No reissue request is needed. A single key covers WinForms, WPF, Avalonia and headless (Applying a license key).


3. Move your data

V5 cannot read RGF files. Convert them to XLSX while V4 is still in place.

① Open the .rgf file in your V4 application (or the V4 Editor)
② Save it as XLSX
③ Read it in V5 with XlsxReader.Read()

Do this before uninstalling V4. Only V4 can perform the conversion.

If you need to keep reading and writing RGF files, consider keeping that part on V4 and leaving it out of the migration.


4. Replace the project references

Change your .csproj as follows.

<PropertyGroup>
  <!-- was: <TargetFramework>net48</TargetFramework> -->
  <TargetFramework>net10.0-windows</TargetFramework>
  <UseWindowsForms>true</UseWindowsForms>   <!-- for WPF: <UseWPF>true</UseWPF> -->
</PropertyGroup>

<ItemGroup>
  <!-- was: <PackageReference Include="unvell.ReoGrid4" Version="4.*" /> -->
  <PackageReference Include="unvell.ReoGrid.One" Version="5.0.0" />
</ItemGroup>
V4 packageV5
unvell.ReoGrid4unvell.ReoGrid.One
unvell.ReoGrid4.Wpfunvell.ReoGrid.One.Wpf
(none)unvell.ReoGrid.One.Avalonia, unvell.ReoGrid.One.Core

The V5 packages include XLSX / PDF I/O. No additional references are needed.


5. Rewrite the using directives

At this point you will get a flood of compile errors — that is expected. The following mapping resolves most of them.

V4V5
using unvell.ReoGrid;using unvell.ReoGrid.Core;
(WinForms control)using unvell.ReoGrid.WinForms;
(WPF control)using unvell.ReoGrid.Wpf;
using unvell.ReoGrid.Graphics;using unvell.ReoGrid.Core.Style;
using unvell.ReoGrid.CellTypes;using unvell.ReoGrid.Core.CellTypes;
using unvell.ReoGrid.IO;using unvell.ReoGrid.IO.Excel; / unvell.ReoGrid.Core.IO;
using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.CellTypes;
using unvell.ReoGrid.Core.Style;

The full list is in Installation.


6. Rewrite the code

Work in the following order. Fixing the parts with the fewest dependencies first reduces rework.

① Creating workbooks

// V4: var sheet = reoGridControl.CurrentWorksheet;
// V4: var ws2   = reoGridControl.Worksheets.Create("Sheet2");
// V4: var wb    = ReoGridControl.CreateMemoryWorkbook();   // when there is no UI
var wb = new Workbook();
var ws = wb.AddWorksheet("Sheet1");

control.LoadWorkbook(wb);              // only when there is a UI
var active = control.ActiveWorksheet;  // V4: control.CurrentWorksheet

CreateMemoryWorkbook() for screenless use is no longer needed. In V5, new Workbook() has no dependency on a control to begin with.

② Cell values — where most of the rewriting happens

// V4: sheet["A1"] = 10;
// V4: sheet[4, 0] = "text";
ws.SetNumber(0, 0, 10);
ws.SetText(4, 0, "text");

// V4: var s = sheet.GetCellData<string>("A2");
// V4: object v = sheet["A1"];
object? v = ws.GetObjectValue(0, 0);
string shown = ws.GetDisplayText(0, 0);

The indexer behaves differently (watch out)

// In V4, ws[0, 0] was an object get/set.
// In V5, ws[0, 0] is get-only and returns a CellCursor - assigning to it will not compile.
//
//   ws[0, 0] = 10;              // CS0200: cannot assign
//
// Go through the cursor, or use the typed methods.
ws[0, 0].SetNumber(10);
ws.SetNumber(0, 0, 10);

Assignments become compile errors, so you will notice them — reads are the ones to watch. var v = ws[0, 0]; is a value in V4 but a CellCursor in V5. Check the type wherever the result is received with var.

There is no sugar equivalent to V4’s bulk fill from an object[] (sheet["B5"] = new object[] {...}). Write a loop instead.

③ Formulas

// V4: sheet["A3"] = "=SUM(A1:A2)";     // assigned as a value
ws.SetFormula(2, 0, "SUM(A1:A2)");      // no leading '='

// V4: string f = sheet.GetCellFormula("A3");   // returned "=SUM(A1:A2)"
string? f = ws.GetFormula(2, 0);                // "SUM(A1:A2)" (no '=')

The leading = is the easiest thing to get wrong. When handling strings typed by the user (where you want the leading = to decide whether it is a formula), use the control’s SetActiveCellInput(text). Like V4, it accepts strings with the = prefix.

④ Styles

// V4: worksheet.SetRangeStyles(range, new WorksheetRangeStyle {
// V4:     Flag = PlainStyleFlag.BackColor | PlainStyleFlag.FontStyleBold,
// V4:     BackColor = Color.Yellow, Bold = true });

// V5: no flags (null means "not set"). Colors are uint ARGB.
ws.SetRangeStyle(range, new StyleRecord { BackgroundColor = 0xFFFFFF00u, Bold = true });

// To change part of a style and keep the rest, use MutateRangeStyle.
ws.MutateRangeStyle(range, s => s with { BackgroundColor = 0xFFFFFF00u, Bold = true });

PlainStyleFlag has been removed. A StyleRecord property being null is itself what means “not specified”. Colors are uint ARGB values, not System.Drawing.Color (opaque yellow is 0xFFFFFF00u).

The range-wide equivalents are SetRangeStyle / MutateRangeStyle. Unlike V4 they look at the shape of the range you pass (a sub-range, whole columns, whole rows, the whole sheet) and pick where to write: the cells, the row defaults, the column defaults, or RootStyle.

// A whole-column / whole-row / whole-sheet range is routed to the default style for
// you. Porting V4's SetRangeStyles(whole column) as a loop materializes one entry per cell.
ws.SetRangeStyle("A:A", new StyleRecord { TextAlign = HAlign.Right });   // the column default
ws.SetRangeStyle("1:1", new StyleRecord { Bold = true });                // the row default

// Naming the row / column directly works too.
ws.SetColumnStyle(0, new StyleRecord { TextAlign = HAlign.Right });
ws.SetRowStyle(0, new StyleRecord { Bold = true });

This is where migration makes the biggest performance difference. Translating V4 code straight into loops throws away the memory efficiency that is V5’s advantage (Style inheritance).

For the UI selection there are helpers such as control.SetSelectionBackColor(...); they go through the same MutateRangeStyle, so the routing applies there too.

⑤ Borders

// V4: worksheet.SetRangeBorders(range, BorderPositions.Outside,
// V4:     RangeBorderStyle.BlackSolid);
ws.Borders.SetOutline(range, new BorderEdge(BorderLineStyle.Solid, 0xFF000000u, 1f));

The BorderPositions bit flags and presets have been removed. Borders inside a range (the equivalent of InsideAll) are currently set in a loop (Borders).

⑥ Number formats

// V4: worksheet.SetRangeDataFormat(range, CellDataFormatFlag.Number,
// V4:     new NumberDataFormatter.NumberFormatArgs {
// V4:         DecimalPlaces = 2, UseSeparator = true });
ws.SetNumberFormat(range, "#,##0.00");

CellDataFormatFlag and the *FormatArgs types are gone entirely, replaced by the same format codes as Excel. V4’s NumberNegativeStyle.RedBrackets is written as #,##0;[Red](#,##0) (Number formats).

Japanese eras are format codes too. V4’s DateTimeFormatArgs (CultureName = "ja-JP" plus a pattern containing g) has no equivalent — write ws.SetNumberFormat(range, "[$-411]ggge\"年\"m\"月\"d\"日\"") instead. V4 leaned on .NET’s JapaneseCalendar; V5 carries its own era table, so the output does not depend on the machine (Japanese eras).

⑦ Structural operations

// V4: worksheet.SetColumnsWidth(2, 1, 160);
ws.Columns.SetSize(2, 160);

// V4: worksheet.FreezeToCell(1, 1);      // used to be model state
control.SetFreeze(1, 1);                  // in V5 this lives on the view

Freeze panes have moved to the view side. As a result, they are not persisted at the moment. You need to set them again each time after opening a file.

⑧ Cell types

// V4: worksheet[3, 1] = new CheckBoxCell();
// V4: var dd = new DropdownListCell("Apple", "Orange");
// V4: worksheet["C3"] = dd;
// V4: dd.SelectedItemChanged += ...;
ws.SetCellType(3, 1, CheckboxCellType.Instance);
ws.SetCellType(RangePosition.Parse("C3:C100"),
	new DropdownCellType(["Apple", "Orange"]));

// State is the cell value; the control raises the events.
ws.SetBoolean(3, 1, true);

Instead of assigning an instance to each cell, you now assign a descriptor to a range. Per-cell-type events (such as dd.SelectedItemChanged) have been removed; button clicks are received through the control’s CellButtonClicked (Cell type concepts).

⑨ Events

There is no simple substitution here. V4 had many events on Worksheet, but in V5 the model types have no events. The only events are the seven on the control layer.

V4 eventV5 equivalent
SelectionRangeChangedcontrol.SelectionChanged
CellDataChangedNo equivalent — raise the notification from the code that changes the value
BeforeCellEdit / AfterCellEditTrack edit start and commit from your control operations
CellMouseDown / CellMouseMove etc.Receive what you need through control.ContextMenuRequested and CellButtonClicked
BeforePasteWrap pre- and post-paste processing on the calling side

If you were detecting cell changes to trigger other processing, you need to provide that notification path yourself. In most cases the code writing the values is your own, so adding a hook there is the shortest route.

⑩ File I/O

V4V5
wb.Load(path)XlsxReader.Read(path)
wb.Save(path, FileFormat.Excel2007)XlsxWriter.Write(wb, path)
worksheet.SaveRGF(path)RemovedReoGridJsonIO.Write(wb)
worksheet.ExportAsCSV(path)CsvIO.Write(ws)
(via printing only)PdfExporter.Export(wb, path)

There is no automatic format detection through the FileFormat enum. Call the class for each format directly.

Formulas are not re-evaluated right after loading an XLSX file. The cached values saved by Excel are used. If you want to recompute yourself, call sheet.Recalculate() explicitly (XLSX I/O).


7. Common compile errors and fixes

ErrorCauseFix
CS0200 Property or indexer cannot be assigned tows[r, c] = valuews.SetNumber(r, c, v) / ws[r, c].SetNumber(v)
CS0246 The type name WorksheetRangeStyle could not be foundStyle type changedStyleRecord
CS0246 The type name PlainStyleFlag could not be foundRemovedSafe to delete (null plays the same role)
CS1503 Cannot convert from Color to uintColor typeUse a uint in 0xAARRGGBB form
CS0246 The type name CellDataFormatFlag could not be foundRemovedSetNumberFormat(range, "format code")
CS0117 BorderPositions does not contain that definitionRemovedws.Borders.SetOutline(...) / SetEdge(...)
CS1061 No definition for CurrentWorksheetRenamedActiveWorksheet
CS1061 No definition for SelectionRangeMoved to the viewcontrol.Selection
CS1061 No definition for CellDataChangedRemovedProvide your own notification path
CS0246 The type name FileFormat could not be foundRemovedXlsxWriter.Write(wb, path)

Code that compiles but behaves differently

This is the easiest part of the migration to overlook.

SymptomCause
Formulas are displayed as stringsPassing a leading = to SetFormula
A value received with var is unusableThe indexer now returns a CellCursor
Slow startup, high memory usageFormatting an entire column with a per-cell loop
Freeze panes are not savedThey are now view-side state (by design)
The selection is not savedSame as above (by design)

8. Verification

Things to check after migrating.

  • Load existing XLSX files and confirm the display matches V4
  • Open exported XLSX files in Excel and confirm nothing is broken
  • Formula results match (check whether Recalculate() is needed)
  • Number formats look the same (fractions and scientific notation are not supported; Japanese eras are, through format codes such as ggge)
  • Display on high-DPI environments (Application.SetHighDpiMode(HighDpiMode.PerMonitorV2))
  • Startup time and memory with large data (should have improved; if it got worse, suspect a formatting loop)
  • The license key is accepted (no watermark is shown)

Migration checklist

□ Confirmed the project can move to .NET 10
□ Confirmed every feature you use exists in V5 (charts, shapes, data binding, ReoScript)
□ Converted RGF files to XLSX (while V4 is still installed)
□ Swapped the csproj TFM and packages
□ Rewrote the using directives
□ Moved cell reads/writes to the typed methods
□ Removed the leading '=' from formulas
□ Moved styles to StyleRecord (whole columns/rows to default styles)
□ Moved borders and number formats to the new APIs
□ Moved cell types to range assignment
□ Prepared alternative notification paths for event-driven code
□ Moved file I/O to the per-format classes
□ Verified all of the above actually runs

Keeping parts on V4

You do not have to move everything at once. Because the V5 core has no UI dependency, you can also leave your existing V4 application as it is and write only new batch or server features against V5 (unvell.ReoGrid.One.Core).

The two can exchange data through XLSX.


Was this article helpful?