前言
本博客中创建的投资计算器根据存入金额和回报率计算每个投资周期的特定回报。作为累积衡量标准,它计算指定时间内赚取的总利息以及当前投资的未来价值。以下是我们将在接下来的部分中学习设计的计算器的快速视图:
以下步骤将帮助进一步演示我们如何使用 Blazor 创建此投资计算器。
使用 FlexGrid 设置 Blazor 应用程序
我们首先使用 Blazor 服务器应用程序模板创建 Blazor 应用程序:
创建应用程序后,我们需要使用 Nuget Package Manager 安装C1.Blazor.FlexGrid包,并添加所需的客户端引用以开始使用 FlexGrid 控件。FlexGrid快速入门可以为您提供有关如何将 FlexGrid 控件添加到 Blazor 应用程序的详细步骤。
FlexGrid 在绑定和非绑定模式下都能很好地工作。对于此应用程序,我们将使用 FlexGrid 的非绑定模式,因为我们需要输入一些值,根据这些值执行计算以填充 FlexGrid 中的其他单元格。请参阅描述 FlexGrid 的非绑定模式的演示和文档。
下面的代码假设项目已根据 FlexGrid 快速入门进行配置,并且 Razor 组件已添加到项目中。现在,将以下代码添加到 razor 页面,通过显式添加所需的行数和列数来添加和配置 FlexGrid 控件以实现非绑定模式:
代码语言:javascript复制@page "/"
@using C1.Blazor.Core
@using C1.Blazor.Grid
<FlexGrid @ref="grid"
CellFactory="@cellFactory"
MergeManager="@custommergemanager"
HeadersVisibility="GridHeadersVisibility.None"
SelectionMode="GridSelectionMode.Cell"
GridLinesVisibility="GridLinesVisibility.None"
CellEditEnded="OnCellEditEnded" BeginningEdit="OnBeginningEdit" SelectionChanging="OnSelectionChanging"
Style="@("max-height:100vh; max-width:97vh;position: absolute; top: 10%; left: 25%; ")">
<FlexGridColumns>
<GridColumn Width="14" IsReadOnly="true" />
<GridColumn Width="67" IsReadOnly="true" />
<GridColumn Width="147" Format="c2" IsReadOnly="true" />
<GridColumn Width="147" Format="c2" IsReadOnly="true" />
<GridColumn Width="147" Format="c2" IsReadOnly="true" />
<GridColumn Width="164" Format="c2" />
<GridColumn Width="14" IsReadOnly="true" />
</FlexGridColumns>
<FlexGridRows>
@for (int i = 0; i < 378; i )
{
if (i == 0)
{
<GridRow Height="50" />
}
else
{
<GridRow Height="25" />
}
}
</FlexGridRows>
</FlexGrid>
@code
{
FlexGrid grid;
GridCellFactory cellFactory = new CustomCellFactory();
CustomMergeManager custommergemanager = new CustomMergeManager();
}
如何设计 Blazor 计算器布局
现在,让我们开始自定义 FlexGrid 外观,使其类似于投资计算器。我们可以通过调整列宽、行高、合并单元格、格式化单元格以及将计算器字段标签填充到 FlexGrid 中适当的单元格来实现相同的目的。以下部分将为您提供有关应用所有所需自定义的详细信息。
合并单元格
FlexGrid 提供对跨行或列合并单元格的内置支持,前提是相邻单元格具有相同的内容。我们可以通过继承GridMergeManager类来自定义FlexGrid的默认合并行为,定义跨行和列合并单元格的自定义逻辑。
对于此实现,我们需要定义一个自定义 MergeManager,它将合并 FlexGrid 中预定义的单元格列表,以便为投资计算器呈现适当的单元格表示形式。下面的代码合并 FlexGrid 中所需的单元格:
代码语言:javascript复制//Define custom MergeManager class to merge cell ranges based on custom logic
public class CustomMergeManager : GridMergeManager
{
public override GridCellRange GetMergedRange(GridCellType cellType, GridCellRange range)
{
//Merge cells containing Calculator title
if (cellType == GridCellType.Cell && (range.Row == 0 && range.Column >= 0 && range.Column <= 5))
{
GridCellRange range1 = new GridCellRange(0, 0, 0, 5);
return range1;
}
//Merge cells containing calculator description
if (cellType == GridCellType.Cell && range.Column >= 1 && range.Column <= 2)
{
if (range.Row == 2 || range.Row == 3 || range.Row == 5 || range.Row == 6 || range.Row == 8 || range.Row == 9)
{
GridCellRange range2 = new GridCellRange(range.Row, 1, range.Row, 2);
return range2;
}
}
//Merge cells containing calculator field labels
if (cellType == GridCellType.Cell && range.Column >= 3 && range.Column <= 4)
{
if (range.Row == 2 || range.Row == 3 || range.Row == 4 || range.Row == 6 || range.Row == 7 || range.Row == 8 || range.Row == 10 || range.Row == 11)
{
GridCellRange range3 = new GridCellRange(range.Row, 3, range.Row, 4);
return range3;
}
}
return base.GetMergedRange(cellType, range);
}
}
}
添加字段标签
在下面的代码中,我们将投资计算器字段标签填充到未绑定 FlexGrid 的相应单元格中:
代码语言:javascript复制//Override AfterRender method to populate grid for Calculator fields
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
GenerateCalculator();
}
// Fill unbound grid to showcase calculator fields and results
private void GenerateCalculator()
{
//Populate calculator field labels
grid[0, 0] = "Investment Calculator";
grid[2, 1] = "Calculate your investment ";
grid[3, 1] = "returns.";
grid[5, 1] = "Enter values into the yellow";
grid[6, 1] = " boxes.";
grid[8, 1] = "Results will be shown in the ";
grid[9, 1] = "green boxes.";
grid[2, 3] = "Initial Investment Amount:";
grid[3, 3] = "Annual Rate of Return:";
grid[4, 3] = "Deposit Amount per Period:";
grid[6, 3] = "Duration of Investment (In Years):";
grid[7, 3] = "Number of Deposits Per Year:";
grid[8, 3] = "Total Number of Periods (Upto 360):";
grid[10, 3] = "Total Interest Income:";
grid[11, 3] = "Ending Balance(FV):";
grid[15, 1] = "Period";
grid[15, 2] = "Initial Balance";
grid[15, 3] = "Interest Earned";
grid[15, 4] = "New Deposit";
grid[15, 5] = "New Balance";
//Populate initial values for initial investment Amount, Return rate and deposit amount per period
grid[2, 5] = 5000;
grid[3, 5] = Convert.ToString(10) "%";
grid[4, 5] = 100;
//Populate initial values for Investment duration(in years), number of deposits per year
grid[6, 5] = Convert.ToString(30);
grid[7, 5] = Convert.ToString(12);
//Invoke method to calculate investment return
CalculateReturn();
}
应用单元格样式
我们已在适当的合并单元格中添加了所有必需的标签。现在,让我们对单元格应用样式,以增强投资计算器的外观和感觉,并使其看起来更加真实。要将样式应用于 FlexGrid 中的单元格,请继承GridCellFactory类以创建自定义 CellFactory 类,该类可让您单独设置每个单元格的样式。您可以通过应用背景颜色、前景色、边框、字体等来设置单元格的样式。
下面的代码定义了一个自定义 CellFactory 并设置 FlexGrid 中所有单元格的样式:
代码语言:javascript复制public override void PrepareCellStyle(GridCellType cellType, GridCellRange range, C1Style style, C1Thickness internalBorders)
{
base.PrepareCellStyle(cellType, range, style, internalBorders);
//Style Calculator border
if (cellType == GridCellType.Cell)
{
if (range.Column == 0 && range.Row >= 1 && range.Row <= 376)
{
style.BorderColor = C1Color.Black;
style.BorderLeftWidth = new C1Thickness(2);
}
if (range.Column == 6 && range.Row >= 1 && range.Row <= 376)
{
style.BorderColor = C1Color.Black;
style.BorderRightWidth = new C1Thickness(2);
}
if (range.Row == 0)
{
style.BorderColor = C1Color.Black;
style.BorderBottomWidth = new C1Thickness(2);
}
if (range.Row == 376)
{
style.BorderColor = C1Color.Black;
style.BorderBottomWidth = new C1Thickness(2);
}
}
//Style calculator title
if (cellType == GridCellType.Cell && range.Column >= 0 && range.Column <= 6 && range.Row == 0)
{
style.BackgroundColor = C1Color.FromARGB(255, 112, 173, 70); ;
style.FontSize = 32;
style.FontWeight = "Arial";
style.Color = C1Color.White;
}
//Style calculator description
if (cellType == GridCellType.Cell && range.Column == 0 && range.Row == 3)
{
style.FontSize = 10;
style.FontWeight = "Arial";
}
//Style Calculator fields labels and inputs
if (cellType == GridCellType.Cell && range.Column >= 3 && range.Column <= 4)
{
if (range.Row >= 2 && range.Row <= 11)
{
if (range.Row != 5 && range.Row != 9)
{
style.BorderColor = C1Color.Black;
style.BorderWidth = new C1Thickness(1);
style.BackgroundColor = C1Color.FromARGB(255, 112, 173, 70);
style.Color = C1Color.White;
style.JustifyContent = C1StyleJustifyContent.FlexEnd;
}
}
if (range.Row == 12 && range.Column >= 3 && range.Column <= 4)
{
style.BorderColor = C1Color.Black;
style.BorderTopWidth = new C1Thickness(1);
}
}
if (cellType == GridCellType.Cell && range.Column == 5)
{
if (range.Row >= 2 && range.Row <= 7)
{
if (range.Row != 5)
{
style.BorderColor = C1Color.Black;
style.BorderWidth = new C1Thickness(1);
style.BackgroundColor = C1Color.White;
style.JustifyContent = C1StyleJustifyContent.FlexEnd;
}
}
if (range.Row >= 8 && range.Row <= 11)
{
if (range.Row != 9)
{
style.BorderColor = C1Color.Black;
style.BorderWidth = new C1Thickness(1);
style.BackgroundColor = C1Color.FromARGB(255, 226, 239, 219);
style.JustifyContent = C1StyleJustifyContent.FlexEnd;
}
}
if (range.Row == 12)
{
style.BorderColor = C1Color.Black;
style.BorderTopWidth = new C1Thickness(1);
}
}
//Style investment return table
if (cellType == GridCellType.Cell && range.Column >= 1 && range.Column <= 5)
{
if (range.Row >= 15 && range.Row <= 375)
{
if (range.Row == 15)
{
style.BackgroundColor = C1Color.FromARGB(255, 112, 173, 70);
style.Color = C1Color.White;
style.JustifyContent = C1StyleJustifyContent.Center;
}
else
{
if (range.Row % 2 == 0)
style.BackgroundColor = C1Color.FromARGB(255, 226, 239, 219);
else
style.BackgroundColor = C1Color.FromARGB(255, 255, 255, 255);
style.JustifyContent = C1StyleJustifyContent.FlexEnd;
}
if (range.Column == 1)
{
style.JustifyContent = C1StyleJustifyContent.Center;
}
}
if (range.Row == 376)
{
style.BorderColor = C1Color.Black;
style.BorderTopWidth = new C1Thickness(1);
}
}
}
}
以下是 FlexGrid 控件的快速浏览,在执行上述所有步骤后,该控件被设计为投资计算器:
实施投资计算器计算
上面设计的投资计算器具有三种色调。深绿色用于指定包含字段标签的单元格,这些标签是静态值。白色单元格是输入单元格,用户在其中输入所需的值来执行计算,浅绿色用于表示显示计算值的单元格,这些值是在此计算器中执行的所有计算的结果,因此投资回报。在所有这些单元格中,只有白色单元格是可编辑的,因为它们需要用户输入。
在本节中,我们将定义一个方法来执行所有计算以计算投资回报。以下方法计算每个投资期的投资回报、赚取的总利息以及投资的未来价值。使用基本运算符加、减、乘、除进行的计算很少。为了计算投资的未来价值,我们需要使用财务函数FV。
必须安装Microsoft.VisualBasic包才能调用 C#.Net 中的财务函数。Microsoft.VisualBasic 命名空间的 Financial 类中提供了不同的财务函数。在下面的代码中,我们使用了Financial 类中的FV财务函数。
请参阅下面的代码,了解如何在 C# 中实现各种计算,以使计算器正常工作并使用适当的投资回报值填充单元格。
代码语言:javascript复制//Method to calculate investment return
public async Task<bool> CalculateReturn()
{
//Fetch initial investment amount
int initialAmt = Convert.ToInt32(grid[2, 5]);
//Fetch Rate of return by removing percentage sign
string rate = (string)grid[3, 5];
int ror = Convert.ToInt32(rate.Replace("%", " "));
//Fetch deposit amount
int depositAmt = Convert.ToInt32(grid[4, 5]);
//Fetch total duration of investment(in years)
int investmentYears = Convert.ToInt32(grid[6, 5]);
//Fetch number of deposits in an year
int numDeposits = Convert.ToInt32(grid[7, 5]);
//Calculate total number of periods and assign to respective grid cell
int totalPeriods = investmentYears * numDeposits;
//Make sure total number of periods is not more than 360
if (totalPeriods <= 360)
{
grid[8, 5] = Convert.ToString(totalPeriods);
}
else
{
grid[8, 5] = null;
await JsRuntime.InvokeVoidAsync("alert", "Please make sure total number of periods is upto 360 !!");
return false;
}
//Calculate investment return for each period in investment duration
for (int period = 1, row = 16; row <= 375; row , period )
{
if (period <= totalPeriods)
{
grid[row, 1] = period;
if (row == 16)
{
grid[row, 2] = initialAmt;
}
else
{
grid[row, 2] = grid[row - 1, 5];
}
grid[row, 3] = (((Convert.ToDouble(ror) / Convert.ToDouble(numDeposits)) * Convert.ToInt32(grid[row, 2])) / 100);
grid[row, 4] = depositAmt;
grid[row, 5] = Convert.ToInt32(grid[row, 2]) Convert.ToDouble(grid[row, 3]) Convert.ToInt32(grid[row, 4]);
}
else
{
grid[row, 1] = grid[row, 2] = grid[row, 3] = grid[row, 4] = grid[row, 5] = null;
}
}
//Calculate Future Value of investment/Ending Balance
double Rate = Convert.ToDouble(ror) / (Convert.ToDouble(numDeposits) * 100);
double NPer = Convert.ToDouble(totalPeriods);
double Pmt = Convert.ToInt32(depositAmt);
double PV = Convert.ToInt32(initialAmt);
double fv = -(Financial.FV(Rate, NPer, Pmt, PV));
grid[11, 5] = fv;
//Calculate total interest income
double endingBal = fv - initialAmt - (depositAmt * totalPeriods);
grid[10, 5] = endingBal;
return true;
}
自定义UI交互
由于投资计算器是使用 FlexGrid 创建的,因此必须处理与编辑和选择相关的 FlexGrid 的默认行为以满足计算器的行为。本节描述了更改计算器的用户交互行为必须处理的所有 FlexGrid 事件。
首先,我们需要处理FlexGrid 的CellEditEnded事件,以确保每当用户更改计算器中的任何输入值(即回报率、初始投资金额、存款金额或投资期限)时,计算器必须重新计算所有投资回报值。
下面的代码实现了上述行为:
代码语言:javascript复制//Handle Flexgrid's CellEditEdited event to recalcuate investment return
//when either of the values Rate of Return, Deposit Amount etc. are changed
public async void OnCellEditEnded(object sender, GridCellRangeEventArgs e)
{
//Parse string input value to int and assign to cell
if (e.CellRange.Row == 2 || e.CellRange.Row == 4)
{
grid[e.CellRange.Row, e.CellRange.Column] = Convert.ToInt32((string)grid[e.CellRange.Row, e.CellRange.Column]);
}
//Add percentage sign to Rate of Return
if (e.CellRange.Row == 3)
{
grid[e.CellRange.Row, e.CellRange.Column] = (string)grid[e.CellRange.Row, e.CellRange.Column] "%";
}
//Invoke method to reclaculate investment return based on new values.
await CalculateReturn();
}
接下来,我们处理FlexGrid的BeginningEdit事件来限制FlexGrid中的编辑。如上所述,FlexGrid 中的所有单元格都不应该是可编辑的。用户应该能够仅编辑那些需要用户输入值的单元格。
因此,下面的代码处理 BeginningEdit 事件以实现上述行为:
代码语言:javascript复制//Handle Flexgrid's BeginningEdit event to cancel editing for cells.
public void OnBeginningEdit(object sender, GridCellRangeEventArgs e)
{
if (e.CellRange.Row >= 8 && e.CellRange.Row <= 375)
e.Cancel = true;
}
最后,我们处理FlexGrid 的SelectionChanging事件,以确保用户只能选择 FlexGrid 中的可编辑单元格:
代码语言:javascript复制//Handle Flexgrid's SelectionChanging event to disable selection of non editable cells.
public void OnSelectionChanging(object sender, GridCellRangeEventArgs e)
{
if (!(e.CellRange.Row >= 2 && e.CellRange.Row <= 7))
{
if (e.CellRange.Row != 5)
e.Cancel = true;
}
else if (e.CellRange.Column >= 1 && e.CellRange.Column <= 4)
{
e.Cancel = true;
}
}
下面是一个 GIF,展示了正在运行的投资计算器: