本类文章的标签为 ‘Visual Studio’


细品RibbonX(56):使用Visual Studio开发Excel商务应用程序(2)

1 颗星2 颗星3 颗星4 颗星5 颗星 (1 人投票, 平均: 5.00 out of 5)
Loading ... Loading ...

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

          <dialogBoxLauncher>
            <button id="RedundantCalcsLaunch"
                    screentip="多重计算"
                    supertip="多次执行同一公式的计算."
                    onAction="DisplayRedundantCalc"/>
          </dialogBoxLauncher>

要实现本例的功能,需要:

  • 为每个等式创建不同的对话框,需要创建3个对话框。
  • 代码必须区分不同的等式。
  • 任何用于与用户交互的对话框需要与功能区中的信息相链接,功能区控件同样需要从对话框中更新。
  • 代码对所需的ThisAddIn方法作多次调用,填充单元格区域中的单元格。
  • 取决于你的需求,可以提供额外的信息,例如列标题和行标题,使输出更易理解。

设计对话框
本例限定用户选择两个范围,这样可以在表中显示输出。设计的“选择贷款范围”窗体如下图所示。
RibbonXApplicationSample22
“选择年金范围”窗体设计如下图所示。
RibbonXApplicationSample23
“选择有效利率范围”窗体设计如下图所示。
RibbonXApplicationSample24
注:设计功能区的目的是减少复杂性。如果由于一些多余的设计选择而增加了更多的复杂性,那么用户将不会感受到使用功能区的任何好处。
创建计算代码
执行一次或多次计算实质上是相同的,但稍有不同。代码如下:

    ' 计算贷款金额并包括位置数据
    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

现在,一切准备就绪。运行代码,在窗体中选择区域,输入相应数据,得到的输出结果如下图所示。
RibbonXApplicationSample25

相关文章

细品RibbonX(55):在Visual Studio中开发Excel商务应用程序(1)

1 颗星2 颗星3 颗星4 颗星5 颗星 (1 人投票, 平均: 5.00 out of 5)
Loading ... Loading ...

大多数人使用Excel进行各种各样的计算,从普通的财务计算到复杂的科学计算。而有些人使用Excel创建演示图形,还有些人甚至使用Excel进行基本的文字处理或数据库需要。简而言之,Excel是一种相当灵活的应用程序,能够帮助你执行广泛的任务。并且,你会发现许多Excel定制是非常特别的。
下面,我们介绍一些定制Excel的应用示例。
许多商务活动使用非标准等式,例如保险公司使用专门的等式来确定费率。另一类非标准等式是一个行业的通用公式不一定在另一个行业使用,例如电路分析是电子工业的一项通用需求,而银行业不需要任何用于此分析的等式。还有一类非标准的等式是提供某种公共利益,例如,可能需要Excel工作表来帮助比较汽车贷款的利益,或者帮助决定家庭的抵押贷款。一些等式能够帮助执行下列任务,决定信用卡的利息或者为当前的信用卡选择最好的还款计划。一些特定的工作表可以帮助你转换度量单位,或者确定身体特征例如肥胖指标。
因此,当你遇到要使用这些非标准等式时,就强以考虑在Excel中创建一个非标准等式选项卡,来执行专门的任务。
下面的示例演示如何在同一选项卡中使用多个版本来完成多项非标准等式的计算任务。
下图展示了示例应用程序的一个界面。大多数人首先会注意到最显眼的“开始”组没有包含任何控件。许多开发者习惯使应用程序中的每项功能必须执行一项任务。本例中,“开始”组只是作为起点,使用三个标签提供信息,从而起到提示作用。XML代码如下:

        <group id="Start" label="开始">
          <labelControl id="StartLabel1"
                        label="选择一个"/>
          <labelControl id="StartLabel2"
                        label="开始点"/>
          <labelControl id="StartLabel3"
                        label="用于计算."/>
        </group>

这个选项卡按照工作流设计,确保用户从左至右,按顺序执行完整个工作过程。
RibbonXApplication1
选择正确的等式
如上图所示,默认选择的是“贷款”切换按钮,当选择“年金”或“有效利率”时,其右侧的组将呈现不同的项目供输入计算数据。当然,这里的“等式”组也可以使用拆分按钮或库的方式。无论使用哪种方式,都必须提供给用户选择等式。本例中,使用的切换按钮在许多情形下都工作得非常好。三个切换按钮的XML代码如下:

        <group id="Equations"
               label="等式">
          <toggleButton id="Loan"
                        label="贷款"
                        onAction="SetupLoan"
                        getPressed="SelectedEquation"/>
          <toggleButton id="Annuity"
                        label="年金"
                        onAction="SetupAnnuity"
                        getPressed="SelectedEquation"/>
          <toggleButton id="EffectiveRate"
                        label="有效利率"
                        onAction="SetupEF"
                        getPressed="SelectedEquation"/>
        </group>

当用户单击某个切换按钮时,应用onAction属性提供的方法

    Public Sub SetupLoan(ByRef control As Office.IRibbonControl, ByVal pressed As Boolean)
        ' 设置计算类型
        CalcType = "Loan"
 
        ' 设置按下状态
        pressed = True
 
        ' 使整个功能区无效
        ribbon.Invalidate()
    End Sub
 
    Public Sub SetupAnnuity(ByRef control As Office.IRibbonControl, ByVal pressed As Boolean)
        ' 设置计算类型
        CalcType = "Annuity"
 
        ' 设置按下状态
        pressed = True
 
        ' 使整个功能区无效
        ribbon.Invalidate()
    End Sub
 
    Public Sub SetupEF(ByRef control As Office.IRibbonControl, ByVal pressed As Boolean)
        ' 设置计算类型
        CalcType = "Effective Rate"
 
        ' 设置按下状态
        pressed = True
 
        ' 使整个功能区无效
        ribbon.Invalidate()
    End Sub

变量CalcType是一个字符串型的全局变量,用来追踪所使用的等式。这个变量决定应用程序的许多操作,甚至选项卡的最终外观。
设置pressed为true,改变目标控件的状态,但不会影响选项卡中的其他控件,而该应用程序在用户选择不同的等式时会更改许多控件,因此需要调用ribbon.Invalidate()来使整个功能区无效,从而重绘功能区。
然后,Excel调用getPressed属性指向的方法。本例中,所有三个切换按钮都使用相同的方法,因为它们实质上都执行相同的任务。SelectedEquation()方法的代码如下:

    Public Function SelectedEquation(ByRef control As Office.IRibbonControl) As Boolean
        ' 基于当前的等式确定按下状态
        Select Case CalcType
            Case "Loan"
                If control.Id = "Loan" Then
                    Return True
                Else
                    Return False
                End If
            Case "Annuity"
                If control.Id = "Annuity" Then
                    Return True
                Else
                    Return False
                End If
            Case "Effective Rate"
                If control.Id = "EffectiveRate" Then
                    Return True
                Else
                    Return False
                End If
            Case Else
                Return False
        End Select
    End Function

基于变量CalcType的值,代码使用Case语句来选择计算类型,并给调用者返回True或False。返回值确定是否按下了该控件。
定义多个功能区元素
本例虽然只是自定义设计了单个选项卡,但是多重设计给应用程序在单个选项卡中提供多个功能的外观界面。当用户选择特定的等式时,选项卡内容改变以反映该等式的需求。跨功能区的控件让用户从左移到右以解决特定的问题,而工作表显示用户输入的项目的结果,下图显示了选择一个等式并输入数据后的结果。
RibbonXApplication2
注:三个等式都是相当简单的财务等式,Excel已经提供了这些工作表函数:PMT、FV、EFFECT。由于三个函数所需要的输入项不同,因此功能区中设计的控件也不同。例如,计算FV函数的功能区界面如下(在“等式”组中选择“年金”):
RibbonXApplication3
下面列出了选项卡中呈现不同视图所需要的XML:

        <group id="DataEntry" getLabel="GetDataEntryLabel">
          <editBox id="Rate"
                   label="利率"
                   onChange="GetRateText"/>
          <dropDown id="Term"
                    label="期数"
                    getVisible="TermVisible"
                    getItemCount="TermCount"
                    getItemID="TermItemID"
                    getItemLabel="TermItemLabel"
                    onAction="GetSelectedTerm"/>
          <editBox id="Payment"
                   label="期初付款"
                   getVisible="PaymentVisible"
                   onChange="GetPaymentText"/>
          <editBox id="Amount"
                   getLabel="AmountLabel"
                   getVisible="AmountVisible"
                   onChange="GetAmountText"/>
        </group>

注意,本应用程序如何使用不同的属性来在不同的情形下看到每个所需要的控件。“利率”控件出现在每个应用程序中,因此不需要使用getVisible属性。其他的控件都有getVisible属性,根据当前等式选择决定该控件是否出现在功能区。各控件的getVisible属性对应的回调代码如下:

    Public Function TermVisible(ByRef control As Office.IRibbonControl) As Boolean
        ' 应用程序不会使用该字段进行有效利率计算
        If CalcType = "Effective Rate" Then
            Return False
        Else
            Return True
        End If
    End Function
 
    Public Function PaymentVisible(ByRef control As Office.IRibbonControl) As Boolean
        ' 当处理年金计算时应用程序仅使用该字段
        If CalcType = "Annuity" Then
            Return True
        Else
            Return False
        End If
    End Function
 
    Public Function AmountVisible(ByRef control As Office.IRibbonControl) As Boolean
        ' 应用程序不会使用该字段进行有效利率计算
        If CalcType = "Effective Rate" Then
            Return False
        Else
            Return True
        End If
    End Function

取决于变量CalcType,在功能区中呈现相应的控件。这是一项编程技巧,在代码中仅使用一个变量来控制应用程序的外观。
比较上面的两个图,你会发现在选择不同的切换按钮后,其右侧组中的标签不同,这是由getLabel属性来实现的:

    Public Function GetDataEntryLabel(ByRef control As Office.IRibbonControl) As String
        ' 通过计算类型的选择决定组标签
        Select Case CalcType
            Case "Loan"
                Return "输入贷款信息"
            Case "Annuity"
                Return "输入年金信息"
            Case "Effective Rate"
                Return "输入有效利率信息"
            Case Else
                Return "没有实现!"
        End Select
    End Function
 
    Public Function AmountLabel(ByRef control As Office.IRibbonControl) As String
        ' 通过计算类型的选择决定数量标签
        ' 由于有效利率计算不使用该控件,因此应用程序不给它提供标签
        Select Case CalcType
            Case "Loan"
                Return "贷款金额"
            Case "Annuity"
                Return "每月年金付款"
            Case Else
                Return "没有实现!"
        End Select
    End Function

应用程序根据选择调整控件及显示。注意,应该小心使用ribbon.Invalidate(),最好仅在应用程序中包含一次对该方法的调用,否则可能会引发很多问题。
获取在功能区中输入的数据
功能区不允许任何直接的交互,因此不能直接获取用户输入到功能区控件中的信息。但如何获取这些信息呢?仍然要使用回调。大多数控件提供了onChange属性,能够用于发现控件数据的变化,但下拉列表例外,需要使用onAction属性发现选项的变化。下面的代码显示了onChange属性的实现及Term控件的onAction属性的实现。

    Public Sub GetRateText(ByRef control As Office.IRibbonControl, ByVal text As String)
        ' 保存文本的输入值
        Rate = Int32.Parse(text)
    End Sub
 
    Public Sub GetSelectedTerm(ByRef control As Office.IRibbonControl, ByVal selectedId As String, ByVal selectedIndex As Int32)
        ' 存储默认值
        Term = 0
 
        ' 保存贷款项
        If CalcType = "Loan" Then
            Select Case selectedIndex
                Case 0
                    Term = 10
                Case 1
                    Term = 15
                Case 2
                    Term = 20
                Case 3
                    Term = 30
            End Select
        End If
 
        ' 保存年金项
        If CalcType = "Annuity" Then
            Select Case selectedIndex
                Case 0
                    Term = 5
                Case 1
                    Term = 7
                Case 2
                    Term = 10
                Case 3
                    Term = 15
                Case 4
                    Term = 20
            End Select
        End If
    End Sub
 
    Public Sub GetPaymentText(ByRef control As Office.IRibbonControl, ByVal text As String)
        ' 保存文本的输入值
        Payment = Int32.Parse(text)
    End Sub
 
    Public Sub GetAmountText(ByRef control As Office.IRibbonControl, ByVal text As String)
        ' 保存文本的输入值
        Amount = Int32.Parse(text)
    End Sub

使用Int32.Parse()将文本框中输入的数据值转换为数字,如果用户在文本框中输入的不是数字,那么Int32.Parse()方法将输出0。
执行计算
需要两段代码。第一段代码响应功能区中单击“开始计算”按钮的操作;第二段代码转换所有数据为字符串,然后在执行计算之前将它们放置到Excel中。

    Public Sub Calculate(ByRef control As Office.IRibbonControl)
        ' 选择计算并调用
        Select Case CalcType
            Case "Loan"
                Globals.ThisAddIn.CalculatePMT(Rate, Term, Amount)
            Case "Annuity"
                Globals.ThisAddIn.CalculateFV(Rate, Term, Payment, Amount)
            Case "Effective Rate"
                Globals.ThisAddIn.CalculateEFFECT(Rate)
        End Select
    End Sub

因为应用程序已经在全局变量中放置了所需的数据,因此所有需要做的就是调用合适的加载项函数并提供必需的输入。实际上,计算需要构建与Excel公式命令相同的字符串。下面的代码显示了这些计算函数:

    ' 计算贷款数量
    Public Sub CalculatePMT(ByVal Rate As Double, ByVal NPer As Int32, ByVal PV As Int32)
        ' 计算利率
        Dim PeriodicRate As Double = (Rate / 100) / 12
 
        ' 计算期数
        Dim Periods As Int32 = NPer * 12
 
        ' 执行计算
        Application.ActiveWindow.ActiveCell.Cells(1, 1) = _
              "=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)
        ' 计算利率
        Dim PeriodicRate As Double = (Rate / 100) / 12
 
        ' 计算期数
        Dim Periods As Int32 = NPer * 12
 
        ' 执行计算
        Application.ActiveWindow.ActiveCell.Cells(1, 1) = _
           "=FV(" + PeriodicRate.ToString() + "," + _
           Periods.ToString() + "," + PMT.ToString() + "," + _
           PV.ToString() + ",0)"
        Application.ActiveWindow.ActiveCell.Calculate()
    End Sub
 
    ' 计算有效利率
    Public Sub CalculateEFFECT(ByVal Rate As Double)
        ' 计算利率
        Dim PeriodicRate As Double = Rate / 100
 
        ' 执行计算
        Application.ActiveWindow.ActiveCell.Cells(1, 1) = _
           "=EFFECT(" + PeriodicRate.ToString() + ",12)"
        Application.ActiveWindow.ActiveCell.Calculate()
    End Sub

在工作表中简单地添加文本,不能确保发生计算,因此代码的末尾调用Application.ActiveWindow.ActiveCell.Calculate()来执行计算。

相关文章

细品RibbonX(54):RibbonX和Visual Studio(2)

1 颗星2 颗星3 颗星4 颗星5 颗星 (1 人投票, 平均: 5.00 out of 5)
Loading ... Loading ...

下面,让我们继续使用Visual Studio为功能区添加更多的功能。

  • 创建带有组和控件的新选项卡
  • 在现有的选项卡中添加新控件
  • 在Office菜单中添加新控件
  • 重利用现有选项卡中的控件
  • 重利用Office菜单中的控件

重利用是指将现有的控件实现新功能或者增强现有的功能。
在Visual Studio中处理图形
在使用Visual Studio时,必须提供图形作为加载项的一部分,这意味着要创建额外的代码。满足这项特定需求的XML作为customUI元素的一部分:

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" 
          onLoad="Ribbon_Load"
          loadImage="GetImage">

在代码中,loadImage属性提供对GetImage()方法的调用,但GetImage()方法不知道要求向Office应用程序发送哪个图标或图像,因此必须以某种方式创建使它能处理任何需求。下面是GetImage()方法的代码:

    Public Function GetImage(ByVal ImageName As String) As Bitmap
        ' 包含传递到Office的位图
        Dim ThisBitmap As Bitmap = New Bitmap(20, 20)
 
        ' 发现图像名和相应的资源
        Select Case ImageName
            Case "Colorblk2"
                ThisBitmap = New Bitmap(My.Resources.Colorblk2)
            Case "TIME"
                ThisBitmap = New Bitmap(My.Resources.TIME.ToBitmap())
        End Select
 
        ' 返回简单的位图
        Return ThisBitmap
    End Function

本例使用两类不同的图像。第一个是标准的位图,Colorblk2,因此可以直接使用;第二个是一个图标,使用前必须调用ToBitmap()方法将它转换为位图。
注意,代码需要从“资源”中请求图像,因此必须通过“添加资源”将图像添加到资源窗口。
当装载功能区时执行任务
使用Visual Studio创建的默认项目包括OnLoad()方法,当Office装载功能区时自动执行该方法。onLoad属性决定初始化时Office调用的方法,可以修改方法以满足需要,默认的方法名为onLoad()。本例中相应的回调代码为:

    Public Sub Ribbon_Load(ByVal ribbonUI As Office.IRibbonUI)
        '保存Ribbon引用
        Me.ribbon = ribbonUI
        '初始化下划线状态
        UnderlineState = False
        '显示装载消息
        MessageBox.Show("功能区装载")
    End Sub

本例提供了一个以某种方式工作的控件示例,该控件取决于用户对另一个控件的选择。本例中,用户能够控制是否应用程序允许下划线功能。因为Excel需要确定在Ribbon装载时该控件的状态,所以代码包含了一个名为UnderlineState的初始化变量。
在调试时,在代码中包括状态信息是有帮助的。这里,只是简单地告诉功能区已经装载。如果没有看到这个消息框,则表明加载项出现了错误。
创建新的选项卡、组和控件
最经常执行的任务之一是为应用程序创建新的选项卡、组和控件。为达到此目的,开始仍然需要添加XML。本例中,添加的XML代码如下:

      <tab id="myTab"
           label="我的选项卡">
        <group id="MyGroup"
               label="我的组">
          <button id="myButton"
                  label="我的按钮"
                  image="Colorblk2"
                  size="large"
                  onAction="MyButton_ClickHandler"/>
        </group>
      </tab>

创建了一个名为“我的选项卡”的选项卡,包含一个名为“我的组”的组,其中含有一个名为“我的按钮”的按钮。使用了自定义图像,因此在image属性中添加了相应的信息。当用户单击“我的按钮”时,调用myButton_ClickHandler()方法:

    Public Sub myButton_ClickHandler(ByVal Control As Office.IRibbonControl)
        ' 显示一条简单的消息
        MessageBox.Show("我的按钮被单击")
    End Sub

修改或重利用已有的选项卡、组和控件
有时,希望修改或重利用已有的选项卡、组或控件,而不是创建新的。例如,可能想改变“格式刷”的功能,满足内在的格式需求,或者完全隐藏某些选项卡、组或控件。
下面的XML代码在“开始”选项卡中添加一个控件:

      <tab idMso="TabHome">
        <group id="BehaviorChange"
               label="操作"
               insertAfterMso="GroupFont">
          <toggleButton id="StopUnderline"
                        label="中止下划线功能"
                        onAction="StopUnderline_ClickHandler"
                        getPressed="StopUnderline_GetPressed"
                        size="large"
                        imageMso="ShapeFillColorPicker"
                        insertBeforeMso="UnderlineGallery"/>  
        </group>
      </tab>

在修改Office已有的元素时,使用idMso属性。新创建的“操作”组使用insertAfterMso属性指定该组在“开始”选项卡中的位置。
本例中,toggleButton控件除了需要通常的onAction属性外,还需要getPressed属性来获取按钮的状态。这两个属性对应的回调代码为:

    Public Sub StopUnderline_ClickHandler(ByVal Control As Office.IRibbonControl, ByVal Pressed As Boolean)
        ' 存储当前按钮的状态
        UnderlineState = Pressed
 
        ' 在屏幕上显示正确的状态
        ribbon.InvalidateControl(Control.Id)
    End Sub
 
    Public Function StopUnderline_GetPressed(ByVal Control As Office.IRibbonControl) As Boolean
        ' 返回当前的按下状态
        Return UnderlineState
    End Function

重利用现有的控件的处理过程与修改现有的控件不同。本例中,必须添加command元素:

  <commands>
    <command idMso="Underline"
             onAction="myUnderline"/>
    <command idMso="FileSaveAsExcel97_2003"
             onAction="FileSaveAs_ClickHandler"/>
  </commands>

本示例重利用了两个控件:“开始”选项卡中的“下划线”按钮,以及“Office菜单”中“另存为”菜单的“Excel 97-2003工作簿”项目。本示例重载onAction属性,这是最常执行的重载。然而,应该看看其它需要的回调,例如处理切换按钮时的getPressed。

    Public Sub myUnderline(ByVal Control As Office.IRibbonControl, ByVal Pressed As Boolean, ByRef CancelDefault As Boolean)
        ' 检查下划线控件的状态
        If (UnderlineState) Then
            ' 显示错误消息
            MessageBox.Show("没有允许操作的下划线")
 
            ' 设置控件使之不能被按下
            Pressed = False
 
            ' 在屏幕上显示正确的状态
            ribbon.InvalidateControl(Control.Id)
 
            ' 告诉Office不要执行默认的操作
            CancelDefault = True
        Else
            ' 否则,告诉Office执行默认的操作
            CancelDefault = False
        End If
    End Sub
 
    Public Sub FileSaveAs_ClickHandler(ByVal Control As Office.IRibbonControl, ByRef CancelDefault As Boolean)
        ' 包含用户的响应
        Dim Response As DialogResult
 
        ' 要求用户保存文件
        Response = _
           MessageBox.Show("保存为老版本. 确定吗?", _
                           "老文件版本警告", _
                           MessageBoxButtons.YesNo)
 
        ' 检查响应
        If Response = DialogResult.Yes Then
            CancelDefault = False
        Else
            CancelDefault = True
        End If
    End Sub

myUnderline()方法控制“下划线”控件是否按正常状态工作,还是基于用户是否按下“中止下划线功能”按钮而提供其它功能。正常状态工作时,该方法简单地设置CancelDefault为false,告诉Office执行缺省的操作。
此外,当用户按下“中止下划线功能”按钮时,该方法显示一条消息,说明下划线控件不工作了。然后设置Pressed为false,意味着下划线控件不会显示像它正常状态下按下时的操作。接着,重设控件为期望的设置。最后,设置CancelDefault为true。
修改或重利用Office菜单
Office菜单包含许多配置和文件项,不直接与具体操作相关。当希望创建与具体操作无关的项目时,将其添加到OfficeMenu项中。

    <officeMenu>
      <menu idMso="FilePrepareMenu">
        <button id="NewPrepButton"
                label="我的准备按钮"
                description="Prepare Time"
                image="TIME"
                insertBeforeMso="FileProperties"
                onAction="NewPrepButton_ClickHandler"/>
      </menu>
      <splitButton idMso="FileSaveAsMenu">
        <menu idMso="FileSaveAsMenu">
          <button id="SayHello"
                  label="问好"
                  description="This button says hello!"
                  image="Colorblk2"
                  onAction="SayHello_ClickHandler"/>
        </menu>
      </splitButton>
    </officeMenu>

上述代码演示了如何处理不同的控件,例如本例中的菜单和拆分按钮。相应的回调代码如下:

    Public Sub NewPrepButton_ClickHandler(ByVal Control As Office.IRibbonControl)
        ' 显示一条简单的消息
        MessageBox.Show("准备好了吗")
    End Sub
 
    Public Sub SayHello_ClickHandler(ByVal Control As Office.IRibbonControl)
        ' 显示一条简单的消息
        MessageBox.Show("Hello")
    End Sub

本示例最终效果的部分截图如下:
RibbonxAndVisualStudio21

RibbonxAndVisualStudio22
使用startFromScratch模式创建功能区
要从头开始创建功能区,只需在ribbon元素中使用startFromScratch属性:

    <ribbon startFromScratch="true">

相关文章

细品RibbonX(53):RibbonX和Visual Studio(1)

1 颗星2 颗星3 颗星4 颗星5 颗星 (目前还没有人投票)
Loading ... Loading ...

RibbonX和Visual Studio互为补充,可以非常方便地使用它们创建许多令人惊叹的应用程序。Microsoft已经为Visual Studio和RibbonX协同工作做出了很多努力,不仅仅有用于创建每类RibbonX项目的特定模板,而且利用Visual Studio可以做更多的事情。
使用Visual Studio的优势
使用Visual Studio的最大的一个特点是将XML代码和资源注入到Office中的能力。如果你决定创建额外的选项卡、组和相关控件,以满足特定的用户需求,那么你能够这样做。使用这项技术,也能够定制满足特定的角色需求的显示,管理员可以比其他员工看到更多的控件。使用XML注入同时意味着能够在标准的XML文件中存储Ribbon变化,并且在运行期在需要时简单地添加功能。
资源——例如用于定义界面的XML——容易被注入到Office环境。例如,可以容易地在运行时为需要的按钮添加图标或位图,而不是必须包含它们作为文件的一部分。这种添加的灵活性意味着能够更新图标或位图,而无须接触到创建的加载项、模板或文档。成功执行的唯一的条件是,新资源要与原先使用的应用程序具有相同的文件名。
另一方面,Visual Studio提供了安全的环境,大大减少了受到安全威胁的情形。
最后,托管的代码有很多好处,可以排除内存泄漏,提供强大的错误处理,也更稳定。并且,使用托管的代码可以很容易地执行许多任务。例如,在Visual Studio中,处理Web服务只需添加一个简单的Web引用,然后访问Web服务功能就像任何其它类一样。
RibbonX基础
可以创建许多Office应用程序类型。显示在Visual Basic\Office文件夹中的文档和模板如下图所示。
ribbonxandvisualstudio1
在希望使用加载项时,选择Visual Basic\Office\2007文件夹。加载项将会创建跨文档和模板工作的应用程序,在用户删除或禁用之前,加载项总是可用的。当然,在需要时很容易添加和删除加载项。
创建基本的选项卡
与VBA不同,Visual Studio为处理Ribbon提供了一种完整的解决方案。处理Ribbon最容易的方法是以加载项工程模板开始。
步骤1 打开Visual Studio。
步骤2 选择“文件——新建项目”,将看到如上图所示的“新建项目”对话框。
步骤3 使用喜欢的语言,打开2007文件夹。
步骤4 选择应用程序的加载项模板,输入名称,单击“确定”。这里的名称为BasicTab。
为了处理Ribbon,需要添加其它项目。Ribbon文件包括代码文件,包含创建的控件的代码,还包括XML文件,告诉Office如何配置功能区。
步骤5 在“解决方案资源管理器”中的项目名称(BasicTab)中单击右键,选择“添加——新建项”,出现“添加新项——BasicTab”对话框,如下图所示。
ribbonxandvisualstudio2
步骤6 选择“功能区(XML)”,你可以输入名称或者使用默认的名称(这里使用默认的名称),单击“添加”。
Visual Studio添加一个新的代码文件和XML文件。Ribbon1.XML文件已经包含一个简单的选项卡、组和按钮,因此本例中保留默认项目。但是,在能够真正看到功能区修改之前,要添加XML文件到应用程序资源中。
步骤7 在解决方案资源管理器中打开项目属性窗口,拖动Ribbon1.xml到“资源”窗口中,如下图所示。
ribbonxandvisualstudio3
你可能已经注意到,使示例完整所必需的大多数代码已经作为创建的工程的一部分出现。当然,你需要作已一些修改以完善示例。
步骤8 按照提示,将Ribbon1.vb中的注释代码复制到ThisAddIn.vb中并取消注释。代码如下:

    Protected Overrides Function CreateRibbonExtensibilityObject() As Microsoft.Office.Core.IRibbonExtensibility
        Return New Ribbon1()
    End Function

步骤9 在新建的功能区中添加一些元素,即修改Ribbon1.xml代码,添加组:

        <group id="MyGroup"
               label="我的组">
          <toggleButton id="toggleButton1"
                        size="large"
                        label="我的按钮"
                        screentip="My Button Screentip"
                        onAction="OnToggleButton1"
                        imageMso="HappyFace" />
        </group>

步骤10 按F5键,调试代码。此时,会启动Excel 2007,界面如下图所示。
ribbonxandvisualstudio4
可以看到,与CustomUI Editor不同,Visual Studio可以创建中文的选项卡和组。
当发布该项目后,加载项会自动添加到Excel,以后打开Excel时都将启动该加载项。如何卸载呢?
步骤1 选择“Office菜单——Excel选项”,出现“Excel选项”对话框。
步骤2 选择“加载项”。看到“BasicTab”加载项列于“添动应用程序加载项”列表中,如下图所示。注意到,“类型”列表明这是一个COM加载项。
ribbonxandvisualstudio5
步骤3 在“管理”下拉列表中,选择“COM加载项”,单击“转到”。出现下图所示的“COM加载项”对话框。
ribbonxandvisualstudio6
步骤4 取消BasicTab加载项前的选择,单击“删除”,从列表中移除该加载项应用程序。
至此,你不会再看到该加载项对应用程序界面的修改。
然而,上述操作并没有从注册表中移除该加载项。你需要使用特别的实用程序或工具来移除注册表项。
步骤1 找到该加载项的完整文件路径。
步骤2 选择“开始—所有程序—Microsoft Visual Studio 2008—Visual Studio Tools—Visual Studio 2008命令提示”。
步骤3 在命令提示中,输入RegAsm/Unregister <完整的DLL名称>,按回车键。
RegAsm实用程序将告诉你成功注销了类型。

相关文章