mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-07-08 18:16:08 +00:00
Replace the old Spreadsheet Import & Export demo with a dedicated
Document Processing section: Spreadsheet API (generate XLSX/CSV from
a typed list), Import & Export (XLSX and CSV subsections, imported
data rendered in RadzenTable, configurable CSV options with live
preview), and Formulas (in-code workbook formulas, stateless
Formula.Evaluate, stateful FormulaEngine, custom FormulaFunction).
Each multi-section demo follows the H5+RadzenExample convention with
matching Toc entries. Descriptions and titles rewritten around what
users search for ("Excel", "XLSX", "CSV", "import/export",
"evaluate Excel formulas in C#") rather than internal API names.
Also ships the public FormulaEngine surface and its contract tests.
99 lines
4.2 KiB
Plaintext
99 lines
4.2 KiB
Plaintext
@using Radzen.Documents.Spreadsheet
|
||
|
||
<RadzenStack Gap="1rem">
|
||
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0.5rem">
|
||
<RadzenLabel Text="Principal" Component="principal" />
|
||
<RadzenNumeric @bind-Value=@principal TValue="double" Min="0" Step="100"
|
||
Change=@(_ => Recompute()) Name="principal" Style="width:130px" />
|
||
|
||
<RadzenLabel Text="Annual rate (0.05 = 5%)" Component="rate" />
|
||
<RadzenNumeric @bind-Value=@rate TValue="double" Step="0.01" Format="0.00"
|
||
Change=@(_ => Recompute()) Name="rate" Style="width:90px" />
|
||
|
||
<RadzenLabel Text="Years" Component="years" />
|
||
<RadzenNumeric @bind-Value=@years TValue="double" Min="1" Max="50"
|
||
Change=@(_ => Recompute()) Name="years" Style="width:80px" />
|
||
</RadzenStack>
|
||
|
||
<RadzenTable>
|
||
<RadzenTableHeader>
|
||
<RadzenTableHeaderRow>
|
||
<RadzenTableHeaderCell>Context</RadzenTableHeaderCell>
|
||
<RadzenTableHeaderCell>Formula</RadzenTableHeaderCell>
|
||
<RadzenTableHeaderCell>Result</RadzenTableHeaderCell>
|
||
</RadzenTableHeaderRow>
|
||
</RadzenTableHeader>
|
||
<RadzenTableBody>
|
||
<RadzenTableRow>
|
||
<RadzenTableCell>FormulaEngine (headless)</RadzenTableCell>
|
||
<RadzenTableCell Style="font-family:monospace">=COMPOUND(@principal, @rate, @years)</RadzenTableCell>
|
||
<RadzenTableCell Style="font-family:monospace">@engineResult</RadzenTableCell>
|
||
</RadzenTableRow>
|
||
<RadzenTableRow>
|
||
<RadzenTableCell>Worksheet.FunctionRegistry</RadzenTableCell>
|
||
<RadzenTableCell Style="font-family:monospace">=COMPOUND(A1, B1, C1)</RadzenTableCell>
|
||
<RadzenTableCell Style="font-family:monospace">@worksheetResult</RadzenTableCell>
|
||
</RadzenTableRow>
|
||
</RadzenTableBody>
|
||
</RadzenTable>
|
||
|
||
<RadzenAlert AlertStyle="AlertStyle.Info" Variant="Variant.Flat" AllowClose="false">
|
||
Both contexts share the exact same <code>FormulaFunction</code> subclass. Register it on the
|
||
<code>FunctionStore</code> exposed by either <code>FormulaEngine.Functions</code> or
|
||
<code>Worksheet.FunctionRegistry</code>, and any cell formula can call it.
|
||
</RadzenAlert>
|
||
</RadzenStack>
|
||
|
||
@code {
|
||
double principal = 1000;
|
||
double rate = 0.05;
|
||
double years = 3;
|
||
string engineResult = "";
|
||
string worksheetResult = "";
|
||
|
||
protected override void OnInitialized() => Recompute();
|
||
|
||
void Recompute()
|
||
{
|
||
// 1) Stateless via FormulaEngine
|
||
var engine = new FormulaEngine();
|
||
engine.Functions.Add<CompoundFunction>();
|
||
engineResult = engine.Evaluate($"=COMPOUND({principal},{rate},{years})")?.ToString() ?? "(null)";
|
||
|
||
// 2) Inside a Workbook via Worksheet.FunctionRegistry
|
||
var wb = new Workbook();
|
||
var sheet = wb.AddSheet("Calc", 1, 4);
|
||
sheet.FunctionRegistry.Add<CompoundFunction>();
|
||
sheet.Cells[0, 0].Value = principal;
|
||
sheet.Cells[0, 1].Value = rate;
|
||
sheet.Cells[0, 2].Value = years;
|
||
sheet.Cells[0, 3].Formula = "=COMPOUND(A1, B1, C1)";
|
||
worksheetResult = sheet.Cells[0, 3].Value?.ToString() ?? "(null)";
|
||
}
|
||
|
||
/// <summary>
|
||
/// Compound interest: principal × (1 + rate)^years.
|
||
/// One class, registered on either context, callable from any cell.
|
||
/// </summary>
|
||
public sealed class CompoundFunction : FormulaFunction
|
||
{
|
||
public override string Name => "COMPOUND";
|
||
|
||
public override FunctionParameter[] Parameters =>
|
||
[
|
||
new("principal", ParameterType.Single, isRequired: true),
|
||
new("rate", ParameterType.Single, isRequired: true),
|
||
new("years", ParameterType.Single, isRequired: true),
|
||
];
|
||
|
||
public override CellData Evaluate(FunctionArguments args)
|
||
{
|
||
System.ArgumentNullException.ThrowIfNull(args);
|
||
var p = args.GetSingle("principal")?.GetValueOrDefault<double>() ?? 0d;
|
||
var r = args.GetSingle("rate")?.GetValueOrDefault<double>() ?? 0d;
|
||
var y = args.GetSingle("years")?.GetValueOrDefault<double>() ?? 0d;
|
||
return CellData.FromNumber(p * System.Math.Pow(1d + r, y));
|
||
}
|
||
}
|
||
}
|