使用VBA根据用户输入从不同工作表调用数据的优雅方式

时间:2015-10-17 21:04:04

标签: vba excel-vba excel

我正在学习VBA,正在努力创建一个模型,提示用户提供一种风险和地理区域(通过下拉菜单完成)和一年(使用输入框)​​,然后拉动来自与用户输入相对应的不同工作表的数据。例如,如果用户选择风险x,区域y和年份2025,则模型将从与风险x,区域y和2025年相对应的工作表中提取数据。

除了使用long if / then语句之外,还有什么更优雅(并且对新人来说有点学习)的方法呢?我不想写出20-30行if / then语句,并且想知道编码器会有多好。有任何想法吗?我的代码的示例部分如下所示。

Sub TryTwo()

    'For me to keep track of time complexity
    Dim StartTime As Double
    Dim SecondsElapsed As Double
    StartTime = Timer


    'Declare main variables
    Dim riskType As String
    Dim region As String
    Dim year As Integer
    Dim yearCurrent As Integer: yearCurrent = 2015
    Dim currentVulnerabilityScore As Integer

    'Declare the different sheets
    Dim ws1, ws2, ws3, ws4, ws5, ws6, ws7, ws8, ws9 As Worksheet

    'Declare variables used in vlookup function for cells D3:D7
    Dim riskLookUpValue As Integer
    Dim regionLookUpValue As Integer

    'Set the different sheets in workbook
    Set ws1 = ThisWorkbook.Sheets("LMC_Model")
    Set ws2 = ThisWorkbook.Sheets("Water_Risk")
    Set ws3 = ThisWorkbook.Sheets("Fire_Risk")
    Set ws4 = ThisWorkbook.Sheets("Drought_Risk")
    Set ws5 = ThisWorkbook.Sheets("Flood_Risk")
    Set ws6 = ThisWorkbook.Sheets("Sea Level Rise Risk")
    Set ws7 = ThisWorkbook.Sheets("Facility_Supplier Tab")
    Set ws8 = ThisWorkbook.Sheets("Risks")
    Set ws9 = ThisWorkbook.Sheets("Other")


    'Assign value to regionLookUpValue and input risk type into cell D3
    regionLookUpValue = Cells(2, 3).Value
    region = Application.WorksheetFunction.VLookup(regionLookUpValue, ws9.Range("b3:c12"), 2)
    Cells(3, 4).Value = region

    'Assign value to riskLookUpValue and input risk type into cell D4
    riskLookUpValue = Cells(2, 4).Value
    riskType = Application.WorksheetFunction.VLookup(riskLookUpValue, ws9.Range("b20:c24"), 2)
    Cells(4, 4).Value = riskType

    'assign year value in a loop that has user enter until right option found
    'Provide sentinal value of 0 to 'year' to ensure that user enters if statement and possibly do until loop
    year = 0
        If year = 0 Then
            year = InputBox("Please enter year (between 2016 and 2045)")
        End If

        Do
            If year < 2016 Or year > 2045 Then
                year = InputBox("Invalid year entry. Please enter year between 2016 and 2045")
        End If
        Loop Until year > 2015 And year < 2046
        Cells(5, 4).Value = year

1 个答案:

答案 0 :(得分:1)

我得到了你对里弗斯的看法。我将回答您的具体问题,然后再提出一种处理此问题的不同方法。

就您的问题而言,您可以将代码置于Workbook_SheetChange事件中。这样,您可以测试活动工作表中的风险或区域单元格是否已更改,并根据需要进行更改。您不需要使用任何If语句,因为每个激活的工作表将在其工作表上运行相同的代码段。当然,如果每张纸的范围不同,则需要一些额外的编码。下面的代码向您展示了如何执行此操作,但请继续阅读,因为我不认为您以最佳方式处理任务:

Option Explicit
Private mRiskSheets As Collection
Private Sub Workbook_Open()
    Dim ws As Worksheet
    Set mRiskSheets = New Collection

    Set ws = ThisWorkbook.Worksheets("Water_Risk")
    mRiskSheets.Add ws, ws.Name
    Set ws = ThisWorkbook.Worksheets("Fire_Risk")
    mRiskSheets.Add ws, ws.Name
    Set ws = ThisWorkbook.Worksheets("Drought_Risk")
    mRiskSheets.Add ws, ws.Name
End Sub

Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    Dim ws As Worksheet
    Dim v(1 To 3, 1 To 1) As Variant

    'Test if activated sheet is a risk sheet
    On Error Resume Next
    Set ws = mRiskSheets(Sh.Name)
    On Error GoTo 0
    If Not ws Is Nothing Then

        'Check if the risk type or region cells were changed
        If Not Intersect(Target, ws.Range("C2,D2")) Is Nothing Then

            'It's a change we're interested in so set the year question
            If Len(ws.Range("D5").Text) = 0 Then
                v(3, 1) = InputBox("Please enter year (between 2016 and 2045)")
            Else
                v(3, 1) = ws.Range("D5").Value2
            End If

            Do
                If v(3, 1) < 2016 Or v(3, 1) > 2045 Then
                    v(3, 1) = InputBox("Invalid year entry. Please enter year between 2016 and 2045")
                End If
            Loop Until v(3, 1) > 2015 And v(3, 1) < 2046

            ' Acquire the values
            v(1, 1) = Application.WorksheetFunction.VLookup(ws.Range("C2"), ws.Range("B3:C12"), 2)
            v(2, 1) = Application.WorksheetFunction.VLookup(ws.Range("D2"), ws.Range("B20:C24"), 2)

            'Write the values
            ws.Range("D3:D5").Value = v

        End If
    End If
End Sub

但是,我不会以你的方式处理这个问题。有几个困难:

  1. 一旦用户输入年份值,他就没有机会更改它。例如,假设他输入2016年并打算输入2017年。他将如何做到这一点?
  2. 如果用户未在年份InputBox中键入整数,例如2016.5或2019 *,那么您的代码将会出错。
  3. 事实上,您的InputBox只会像一个单元格一样,所以您也可以让用户直接将年份键入&#34; D5&#34;并且只是有一些条件格式。
  4. 如果没有InputBox,您至少可以测试三个单元格中的任何一个的变化&#34; C2&#34;,&#34; D2&#34;或&#34; D5&#34;。虽然很难知道何时想要处理变更事件。
  5. 如果风险类型列表存在任何问题,那么代码将抛出未处理的错误,因为查找失败。
  6. 如果是我,我有一个UserForm可以控制所有这些问题。您可以拥有4个ComboBoxes:风险类别,风险类型,地区和年份。一旦根据需要添加了所有内容,用户只需点击“获取数据”即可。 Button并写入数据。它可以减轻错误并允许重复输入。像这样:

    Sample UserForm

    在UserForm设计器中添加4 Comboboxes,将它们重命名为cboxRiskCat,cboxRiskType,cboxRegion和cboxYear,将其ColumnCount属性更改为&#39; 2&#39;,然后更改{{1属性为&#39; 2 - fmStyleDropDownList&#39;。添加Style并将其重命名为btnFetch。

    然后添加以下代码:

    CommandButton

    您可以通过在Option Explicit Private mRiskValues As Collection Private mRegionValues As Collection Private Sub btnFetch_Click() Dim v(1 To 3, 1 To 1) As Variant v(1, 1) = cboxRegion.Column(1) v(2, 1) = cboxRiskType.Column(1) v(3, 1) = cboxYear.Value ThisWorkbook.Worksheets(cboxRiskCat.Column(1)).Range("D3:D5").Value = v End Sub Private Sub cboxRiskCat_Change() If cboxRiskCat.ListIndex <> -1 Then ThisWorkbook.Worksheets(cboxRiskCat.Column(1)).Activate cboxRegion.List = mRegionValues(cboxRiskCat.Column(1)) cboxRegion.ListIndex = 0 cboxRiskType.List = mRiskValues(cboxRiskCat.Column(1)) cboxRiskType.ListIndex = 0 End If End Sub Private Sub UserForm_Initialize() Dim ws As Worksheet Dim riskCat(1 To 3, 1 To 2) As String Dim v As Variant Dim i As Integer Set mRegionValues = New Collection Set mRiskValues = New Collection 'Populate the risk categories riskCat(1, 1) = "Water": riskCat(1, 2) = "Water_Risk" riskCat(2, 1) = "Fire": riskCat(2, 2) = "Fire_Risk" riskCat(3, 1) = "Drought": riskCat(3, 2) = "Drought_Risk" cboxRiskCat.List = riskCat ' Populate the risk types and regions For i = 1 To UBound(riskCat, 1) Set ws = ThisWorkbook.Worksheets(riskCat(i, 2)) v = ws.Range("B3:C12") mRegionValues.Add v, riskCat(i, 2) v = ws.Range("B20:C24") mRiskValues.Add v, riskCat(i, 2) Next 'Populate the years in userform For i = 2016 To 2024 cboxYear.AddItem i Next cboxYear.ListIndex = 0 cboxRiskCat.ListIndex = 0 End Sub 所需的任何地方输入UserForm1.Show来显示此UserForm。可能在Workbook_Open事件中。

    我这里只放了三张风险单。如果您想要更多,请更改&#39; 3&#39;在这一行中,您可以使用多个工作表:Dim riskCat(1 To 3, 1 To 2) As String,并添加更多工作表,如此行下方的代码所示。