The spreadsheet engine parsed typed input with the invariant culture while
displaying values with the current culture, breaking the edit round-trip on
non-en-US hosts and making comma-decimal entry (10,50) impossible.
Workbook gains a Culture property (defaults to CurrentCulture) which the
component stamps from its inherited Culture parameter. It drives:
- Cell input parsing (CellData type inference), including day-month date
handling for cultures where the group and date separators collide (de-DE)
- Edit text and display rendering (GetValue, GetValueAsString, GetDisplayText)
- Number format rendering - format codes stay canonical invariant tokens while
separators, month names, and AM/PM designators follow the culture
- Formula entry and display via the new FormulaLocalizer (Excel FormulaLocal
semantics: ';' argument separators and ',' decimals in comma-decimal
cultures, lenient comma acceptance where unambiguous, canonical invariant
storage)
- Dialog input (data validation, conditional format, filter) via shared
conversion helpers on SpreadsheetDialogBase
XLSX and CSV files read and write canonical invariant values regardless of
the workbook culture, including autofit column widths. Malformed formulas now
surface as error trees instead of an unhandled lexer exception. Number format
parsing is cached (hard-bounded) since CellView reparsed per render.
Includes localization demos for the Spreadsheet and Document Processing
sections and culture test suites.
Note: headless code on non-en-US hosts now parses string values with the
host culture; set Workbook.Culture explicitly (or to InvariantCulture) for
host-independent processing.
Four implementations of 'coerce a cell value to a double' had drifted
across the spreadsheet codebase, with subtle differences:
- AdvancedFilterCriteria.TryCoerceToDouble: handled DateTime via
ToOADate
- Worksheet.Filter.TryCoerce: did NOT handle DateTime at all (bug —
date filters silently failed on DateTime-typed cells)
- ConditionalFormatRules.GreaterThanRule.TryGetNumber: did NOT handle
DateTime explicitly
- NumberFormat.TryGetNumber: handled DateTime via ToNumber (the
Lotus 1-2-3-compatible variant)
The codebase canonical is ToNumber() — it correctly applies the
phantom-Feb-29-1900 quirk for dates before March 1, 1900. ToOADate
diverged for that pre-1900 range.
Consolidate into NumericCoercion.TryCoerceToDouble. Filter and
ConditionalFormat now correctly handle DateTime. NumberFormat keeps
its CellDataType gate (intentional) but delegates non-string paths
to the shared helper.
Also fixes DataValidationRule's lingering ToOADate calls so date
validation agrees with date filtering and conditional formatting
for pre-1900 dates, AND collapses 16 empty visit overrides in
FilterCriterionVisitorBase plus removes 50+ lines of trivial Accept
wrappers + dead defensive null guards in StringFilterCriterion.
Replaced all == null with is null and != null with is not null
across 109 spreadsheet files. The is null pattern uses a direct
reference check that cannot be overridden by operator overloads.
137 files with namespace Radzen.Documents.Spreadsheet now live under
Documents/Spreadsheet/ directory, matching their namespace. 76 function
files in Documents/Spreadsheet/Functions/. 27 UI files remain in
Spreadsheet/.