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.
| Situation | What V5 solves |
|---|---|
| Memory or speed becomes a problem beyond tens of thousands of rows | About 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 processing | Workbook / Worksheet are UI-independent; everything works without a screen |
| You want to run on Linux / macOS | Covered by the Avalonia and headless editions |
| You want to export PDF directly | One line with PdfExporter; Japanese fonts embedded |
| You want to exchange files with the Web edition | reogrid-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
| Feature | Typical V4 usage | Options for now |
|---|---|---|
| Charts | The chart types under Chart/ | Pair with a separate charting library / stay on V4 |
| Shapes and text boxes | Drawing/ | Images still work in V5 |
| Data binding | IDataSource / ArrayDataSource | Write values in a loop (IRowDataSource for large data) |
| ReoScript | RunScript | Removed. It will not return |
| Some cell types | DatePicker / RadioButton / NumberInput / image-based types | Can 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.
| V4 | V5 |
|---|---|
Comment | ws.SetComment(row, col, text) — Cell Notes |
IValidator / CellValidation | ws.AddValidation(range, rule) — Data Validation |
TextSearch/ | FindEngine.FindNext(...) / FindEngine.ReplaceAll(...) |
| Cell and sheet protection | ws.Protect() / ws.SetRangeLock(range, LockState.Unlocked) |
File formats
| V4 format | V5 |
|---|---|
RGF (.rgf) | Cannot be read. Migrate via XLSX (next section) |
| XLSX | Reads as-is |
BinaryFormatter | Removed |
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 package | V5 |
|---|---|
unvell.ReoGrid4 | unvell.ReoGrid.One |
unvell.ReoGrid4.Wpf | unvell.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.
| V4 | V5 |
|---|---|
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 event | V5 equivalent |
|---|---|
SelectionRangeChanged | control.SelectionChanged |
CellDataChanged | No equivalent — raise the notification from the code that changes the value |
BeforeCellEdit / AfterCellEdit | Track edit start and commit from your control operations |
CellMouseDown / CellMouseMove etc. | Receive what you need through control.ContextMenuRequested and CellButtonClicked |
BeforePaste | Wrap 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
| V4 | V5 |
|---|---|
wb.Load(path) | XlsxReader.Read(path) |
wb.Save(path, FileFormat.Excel2007) | XlsxWriter.Write(wb, path) |
worksheet.SaveRGF(path) | Removed → ReoGridJsonIO.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
| Error | Cause | Fix |
|---|---|---|
CS0200 Property or indexer cannot be assigned to | ws[r, c] = value | ws.SetNumber(r, c, v) / ws[r, c].SetNumber(v) |
CS0246 The type name WorksheetRangeStyle could not be found | Style type changed | StyleRecord |
CS0246 The type name PlainStyleFlag could not be found | Removed | Safe to delete (null plays the same role) |
CS1503 Cannot convert from Color to uint | Color type | Use a uint in 0xAARRGGBB form |
CS0246 The type name CellDataFormatFlag could not be found | Removed | SetNumberFormat(range, "format code") |
CS0117 BorderPositions does not contain that definition | Removed | ws.Borders.SetOutline(...) / SetEdge(...) |
CS1061 No definition for CurrentWorksheet | Renamed | ActiveWorksheet |
CS1061 No definition for SelectionRange | Moved to the view | control.Selection |
CS1061 No definition for CellDataChanged | Removed | Provide your own notification path |
CS0246 The type name FileFormat could not be found | Removed | XlsxWriter.Write(wb, path) |
Code that compiles but behaves differently
This is the easiest part of the migration to overlook.
| Symptom | Cause |
|---|---|
| Formulas are displayed as strings | Passing a leading = to SetFormula |
A value received with var is unusable | The indexer now returns a CellCursor |
| Slow startup, high memory usage | Formatting an entire column with a per-cell loop |
| Freeze panes are not saved | They are now view-side state (by design) |
| The selection is not saved | Same 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.
Read next
- Differences from V4 — the API comparison at a glance
- Applying a license key
- Release notes — planned future work