细品RibbonX(56):使用Visual Studio开发Excel商务应用程序(2)
让我们接着上文介绍,继续丰富已开发的应用程序。
下面,让应用程序执行更丰富的计算。例如,可能想计算一定范围内的抵押值,而不是单个值,以便为客户提供多种选择。在上例中,我们能够指导用户输入不同的值,执行相同的计算多次,然而这种方式往往会花费很多时间。因此,需要使用不同的方法使应用程序更好的执行任务。
较好的解决方案是使用对话框来输入计算范围,而使用对话框启动器来访问对话框。添加了对话框启动器的新界面如下图。

相应的XML代码如下:
<dialogBoxLauncher> <button id="RedundantCalcsLaunch" screentip="多重计算" supertip="多次执行同一公式的计算." onAction="DisplayRedundantCalc"/> </dialogBoxLauncher>
要实现本例的功能,需要:
- 为每个等式创建不同的对话框,需要创建3个对话框。
- 代码必须区分不同的等式。
- 任何用于与用户交互的对话框需要与功能区中的信息相链接,功能区控件同样需要从对话框中更新。
- 代码对所需的ThisAddIn方法作多次调用,填充单元格区域中的单元格。
- 取决于你的需求,可以提供额外的信息,例如列标题和行标题,使输出更易理解。
设计对话框
本例限定用户选择两个范围,这样可以在表中显示输出。设计的“选择贷款范围”窗体如下图所示。

“选择年金范围”窗体设计如下图所示。

“选择有效利率范围”窗体设计如下图所示。

注:设计功能区的目的是减少复杂性。如果由于一些多余的设计选择而增加了更多的复杂性,那么用户将不会感受到使用功能区的任何好处。
创建计算代码
执行一次或多次计算实质上是相同的,但稍有不同。代码如下:
' 计算贷款金额并包括位置数据 Public Sub CalculatePMT(ByVal Rate As Double, ByVal NPer As Int32, ByVal PV As Int32, ByVal X As Int32, ByVal Y As Int32) ' 计算利率. Dim PeriodicRate As Double = (Rate / 100) / 12 ' 计算期数. Dim Periods As Int32 = NPer * 12 ' 执行计算. Application.ActiveWindow.ActiveCell.Cells(X, Y) = _ "=PMT(" + PeriodicRate.ToString() + "," + _ Periods.ToString() + "," + PV.ToString() + ",0,0)" Application.ActiveWindow.ActiveCell.Calculate() End Sub ' 计算年金金额并包括位置数据. Public Sub CalculateFV(ByVal Rate As Double, ByVal NPer As Int32, ByVal PMT As Int32, ByVal PV As Int32, ByVal X As Int32, ByVal Y As Int32) ' 计算利率. Dim PeriodicRate As Double = (Rate / 100) / 12 ' 计算期数. Dim Periods As Int32 = NPer * 12 ' 执行计算. Application.ActiveWindow.ActiveCell.Cells(X, Y) = _ "=FV(" + PeriodicRate.ToString() + "," + _ Periods.ToString() + "," + PMT.ToString() + "," + _ PV.ToString() + ",0)" Application.ActiveWindow.ActiveCell.Calculate() End Sub ' 计算有效利率并包括数据位置. Public Sub CalculateEFFECT(ByVal Rate As Double, ByVal X As Int32, ByVal Y As Int32) ' 计算利率. Dim PeriodicRate As Double = (Rate / 100) / 12 ' 执行计算. Application.ActiveWindow.ActiveCell.Cells(X, Y) = _ "=EFFECT(" + PeriodicRate.ToString() + ",12)" Application.ActiveWindow.ActiveCell.Calculate() ' 格式单元格. Dim ThisRange As Excel.Range ThisRange = Application.Cells(X, Y) ThisRange.NumberFormat = "0.0000%" End Sub
可以将上述代码与前文中相应的示例代码比较。最大的不同在于,添加了两个额外的位置参数,使得可以在任何位置放置输出的值。
定义对现有数据的链接
为功能区应用程序创建对话框时,需要创建对话框与合适的功能区控件的链接,否则两个元素将不同步并且应用程序会显示错误的数据。要创建链接,需要:
- 从功能区中获取信息
- 在功能区中设置信息
- 在XML中定义需要的回调
创建链接的XML代码如下:
<editBox id="Rate" label="有效利率" onChange="GetRateText" getText="SetRateText"/> <dropDown id="Term" label="期数" getVisible="TermVisible" getItemCount="TermCount" getItemID="TermItemID" getItemLabel="TermItemLabel" onAction="GetSelectedTerm" getSelectedItemIndex="SetSelectedTerm"/> <editBox id="Payment" label="期初支付" getVisible="PaymentVisible" onChange="GetPaymentText" getText="SetPaymentText"/> <editBox id="Amount" getLabel="AmountLabel" getVisible="AmountVisible" onChange="GetAmountText" getText="SetAmountText"/>
用于实现链接的属性取决于控件类型。大多数控件使用getText属性,从应用程序中提取信息并在控件中显示(例如,本例中的文本框);一些控件需要使用其它属性,例如本例中的getSelectedItemIndex属性。回调代码如下:
Public Function SetRateText(ByVal control As Office.IRibbonControl) As String ' 返回Rate变量的当前值. Return Rate.ToString() End Function Public Function SetSelectedTerm(ByVal control As Office.IRibbonControl) As Int32 ' 设置贷款项. If CalcType = "Loan" Then Select Case Term Case 10 Return 0 Case 15 Return 1 Case 20 Return 2 Case 30 Return 3 End Select End If ' 设置年金项. If CalcType = "Annuity" Then Select Case Term Case 5 Return 0 Case 7 Return 1 Case 10 Return 2 Case 15 Return 3 Case 20 Return 4 End Select End If ' 提供缺省的返回值. Return 0 End Function Public Function SetPaymentText(ByVal control As Office.IRibbonControl) As String ' 返回Payment变量的当前值. Return Payment.ToString() End Function Public Function SetAmountText(ByVal control As Office.IRibbonControl) As String ' 返回Amount变量的当前值. Return Amount.ToString() End Function
执行更丰富的计算
对话框启动器dialogBoxLauncher仅有一个onAction属性,因此任何计算的开始点都是该属性指向的方法DisplayRedundantCalc。当然,由于每个等式都不相同,因此都需要采用某种方式单独实现调用。
Public Sub DisplayRedundantCalc(ByVal control As Office.IRibbonControl) ' 选择正确的过程. Select Case CalcType Case "Loan" PerformLoanRangeCalc() Case "Annuity" PerformAnnuityRangeCalc() Case "Effective Rate" PerformEffectiveRateRangeCalc() End Select End Sub
代码根据所选择的项目不同,调用不同的对话框计算程序。
Private Sub PerformLoanRangeCalc() ' 创建对话框. Dim ThisSelection As LoanRangeSelection = New LoanRangeSelection() ' 在对话框中添加已存在的变量. ThisSelection.txtIntBeg.Text = Rate.ToString() ThisSelection.txtIntEnd.Text = Rate.ToString() ThisSelection.txtIntInc.Text = "1" ThisSelection.cbTermBeg.Text = Term.ToString() ThisSelection.cbTermEnd.Text = Term.ToString() ThisSelection.txtLoanAmt.Text = Amount.ToString() ' 显示对话框并且如果用户单击确定则处理数据. If ThisSelection.ShowDialog() = DialogResult.OK Then ' 转换数据值为Int32egers. Rate = Int32.Parse(ThisSelection.txtIntBeg.Text) Term = Int32.Parse(ThisSelection.cbTermBeg.Text) Amount = Int32.Parse(ThisSelection.txtLoanAmt.Text) ' 创建本地变量,包括计算数据. Dim EndRate As Int32 = Int32.Parse(ThisSelection.txtIntEnd.Text) Dim IncRate As Int32 = Int32.Parse(ThisSelection.txtIntInc.Text) Dim EndTerm As Int32 = Int32.Parse(ThisSelection.cbTermEnd.Text) ' 更新功能区中的值. ribbon.InvalidateControl("Rate") ribbon.InvalidateControl("Term") ribbon.InvalidateControl("Amount") ' 添加初始标题. Globals.ThisAddIn.SetHeading("利息", 1, 1) ' 执行计算. Dim i As Int32 For i = Rate To EndRate ' 计算X和Y的位置值. Dim X As Int32 = i + 2 - Rate Dim Y As Int32 = 2 ' 打印Int32erest利率. Globals.ThisAddIn.SetHeading(i.ToString() + "%", X, 1) ' 使用一系列if语句确定年设置. If Term = 10 And EndTerm >= 10 Then ' 执行计算. Globals.ThisAddIn.CalculatePMT(i, 10, Amount, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("10年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If If Term <= 15 And EndTerm >= 15 Then ' 执行计算. Globals.ThisAddIn.CalculatePMT(i, 15, Amount, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("15年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If If Term <= 20 And EndTerm >= 20 Then ' 执行计算. Globals.ThisAddIn.CalculatePMT(i, 20, Amount, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("20年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If If Term <= 30 And EndTerm >= 30 Then ' 执行计算. Globals.ThisAddIn.CalculatePMT(i, 30, Amount, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("30年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If Next End If End Sub Private Sub PerformAnnuityRangeCalc() ' 创建对话框. Dim ThisSelection As AnnuityRangeSelection = New AnnuityRangeSelection() ' 在对话框中添加已存在的变量. ThisSelection.txtIntBeg.Text = Rate.ToString() ThisSelection.txtIntEnd.Text = Rate.ToString() ThisSelection.txtIntInc.Text = "1" ThisSelection.cbTermBeg.Text = Term.ToString() ThisSelection.cbTermEnd.Text = Term.ToString() ThisSelection.txtLoanAmt.Text = Amount.ToString() ThisSelection.txtPayment.Text = Payment.ToString() ' 显示对话框并且如果用户单击确定则处理数据. If ThisSelection.ShowDialog() = DialogResult.OK Then ' 转换数据值为Int32egers. Rate = Int32.Parse(ThisSelection.txtIntBeg.Text) Term = Int32.Parse(ThisSelection.cbTermBeg.Text) Amount = Int32.Parse(ThisSelection.txtLoanAmt.Text) Payment = Int32.Parse(ThisSelection.txtPayment.Text) ' 创建本地变量以包含计算数据. Dim EndRate As Int32 = Int32.Parse(ThisSelection.txtIntEnd.Text) Dim IncRate As Int32 = Int32.Parse(ThisSelection.txtIntInc.Text) Dim EndTerm As Int32 = Int32.Parse(ThisSelection.cbTermEnd.Text) ' 更新功能区中的值. ribbon.InvalidateControl("Rate") ribbon.InvalidateControl("Term") ribbon.InvalidateControl("Amount") ribbon.InvalidateControl("Payment") ' 添加初始标题. Globals.ThisAddIn.SetHeading("利息", 1, 1) ' 执行计算. Dim i As Int32 For i = Rate To EndRate ' 计算X和Y的位置值. Dim X As Int32 = i + 2 - Rate Dim Y As Int32 = 2 ' 打印Int32erest利率. Globals.ThisAddIn.SetHeading(i.ToString() + "%", X, 1) ' 使用一系列if语句决定年设置. If Term = 5 And EndTerm >= 5 Then ' 执行计算. Globals.ThisAddIn.CalculateFV(i, 5, Amount, Payment, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("5年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If If Term <= 7 And EndTerm >= 7 Then ' 执行计算. Globals.ThisAddIn.CalculateFV(i, 7, Amount, Payment, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("7年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If If Term <= 10 And EndTerm >= 10 Then ' 执行计算. Globals.ThisAddIn.CalculateFV(i, 10, Amount, Payment, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("10年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If If Term <= 15 And EndTerm >= 15 Then ' 执行计算. Globals.ThisAddIn.CalculateFV(i, 15, Amount, Payment, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("15年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If If Term <= 20 And EndTerm >= 20 Then ' 执行计算. Globals.ThisAddIn.CalculateFV(i, 20, Amount, Payment, X, Y) ' 打印标题. Globals.ThisAddIn.SetHeading("20年", 1, Y) ' 如果已经使用则增加Y. Y = Y + 1 End If Next End If End Sub Private Sub PerformEffectiveRateRangeCalc() ' 创建对话框. Dim ThisSelection As EffectiveRateRangeSelection = New EffectiveRateRangeSelection() ' 在对话框中添加已存在的变量. ThisSelection.txtIntBeg.Text = Rate.ToString() ThisSelection.txtIntEnd.Text = Rate.ToString() ThisSelection.txtIntInc.Text = "1" ' 显示对话框并且如果用户单击确定则处理数据. If ThisSelection.ShowDialog() = DialogResult.OK Then ' 转换数据值为Int32egers. Rate = Int32.Parse(ThisSelection.txtIntBeg.Text) ' 创建本地变量以包含计算数据. Dim EndRate As Int32 = Int32.Parse(ThisSelection.txtIntEnd.Text) Dim IncRate As Int32 = Int32.Parse(ThisSelection.txtIntInc.Text) ' 更新功能区中的值. ribbon.InvalidateControl("Rate") ' 添加初始标题. Globals.ThisAddIn.SetHeading("利息", 1, 1) Globals.ThisAddIn.SetHeading("有效利率", 1, 2) ' 执行计算. Dim i As Int32 For i = Rate To EndRate ' 计算X和Y位置值. Dim X As Int32 = i + 2 - Rate ' 打印Int32erest利率. Globals.ThisAddIn.SetHeading(i.ToString() + "%", X, 1) ' 执行计算. Globals.ThisAddIn.CalculateEFFECT(i, X, 2) Next End If End Sub
考虑数据识别需求
对输出结果添加有意义的标题。当执行多重计算时,为使数据意义明确,必须提供标题。代码如下:
Public Sub SetHeading(ByVal Heading As String, ByVal X As Int32, ByVal Y As Int32) ' 添加所需要的标题. Application.ActiveWindow.ActiveCell.Cells(X, Y) = Heading End Sub















