首页 文章

查找Excel工作簿的工作表名称和行号

提问于
浏览
0

我正在使用包含三个数据工作表的工作簿 . 每个工作表都有一个 Contract 编号列 . 必须排除某些 Contract 并在单独的工作表中注明 .

我想创建Excel VBA宏:

  • 提示用户输入要排除的特定 Contract 号

  • 存储 Contract 号

  • 在 Contract 编号中搜索所有三个工作表的 Contract 列

  • 注意已创建的"summary"工作表中不需要的 Contract 详细信息

  • 完全删除不需要的 Contract 行

对于用户输入的'n'个 Contract ,宏应循环执行此过程 .

Public contString As String
Public x As Variant
Public xCount As Variant

Sub find()
contString = InputBox(Prompt:="Enter contract numbers to exclude(Comma Delimited).      Cancel to include all contracts.", _
      Title:="Exclude Contracts", Default:="1715478")
  x = Split(contString, ",")
  xCount = UBound(x) 'Number of contracts entered by user
End Sub

Sub SearchWS1()
Sheets("WS1").Activate
Columns("I:I").Select 'Contract Number Column
Selection.find(What:=x(i), After:=ActiveCell, LookIn:=xlValues, _
    LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
    MatchCase:=False, SearchFormat:=False).Activate
BKWS = ActiveCell.Worksheet.Name
BKRow = ActiveCell.Row
If BKRow > 0 Then
   Cname = Range("G" & BKRow)
   Cnumber = Range("I" & BKRow)
   Cvalue = Range("K" & BKRow)
   'Summarize Excluded Contract Info on Summary WS
   Range("Summary!B25").Value = "Exclusions:"
   Range("Summary!B26").Value = Cnumber
   Range("Summary!C26").Value = Cname
   Range("Summary!D26").Value = Cvalue
   'Select and Delete Contract
   Rows(ActiveCell.Row).Select
   Rows(BKRow).EntireRow.Delete
Else
   Call SearchWS2 'SearchWS2 is essentially the same as SearchWS1 and Calls SearchWS3 if contract isn't found. 
End If
End Sub

如果第一个WS中不存在 Contract 号,则会收到“对象变量或未设置块”等错误 . 一旦我可以修复此错误,我将需要通过循环为用户输入的每个 Contract 号运行此过程 . 任何有关调试错误或为此设置循环的帮助将非常感激 .

谢谢!

2 回答

  • 3
    • 使用 InputBox 输入 Contract 号(比方说,逗号分隔) . 使用 Split 函数拆分结果 .

    • 将 Contract 号存储在您隐藏的单独工作表上( wks.visible=xlVeryHidden ,其中 wksworksheet 对象) .

    • 使用多维数组查找值以存储值 .

    • 使用 rFound=saArray 将二维数组打印到找到的工作表(其中 rFoundrange 对象, saArray 是二维数组 .

    大量使用录制宏来学习语法 .

    有关检索和打印到 cells 的快速方法,请参阅this example .

    Update:

    对不起,这很邋but但我只是把它扔在一起,显然,它还没有经过测试 . 希望这可以帮助 . 对不起,我也不应该让你使用这样的高级技术,但我很难回去 .

    dim j as integer, k as integer, m as long, iContractColumn as integer
    Dim x() as string, saResults() as string
    dim vData as variant
    dim wks(0 to 2) as worksheet
    
    iContractColumn=????
    
    set wks(0) = Worksheets("First")
    set wks(1) = Worksheets("Second")
    set wks(2) = Worksheets("Third")
    
    redim saresults(1 to 100, 1 to 2)
    m=0
    'Loop thru worksheets
    for j=0 to 2
      'Get data from worksheet
      vdata=wks(j).range(wks(j) _
        .cells(1,iContractColumn),wks(j).cells(rows.count,iContractColumn).end(xlup))
      'Loop through data
      for k=1 to ubound(vdata)
        'Loop through user criteria
        For i = 0 To UBound(x)
          'Compare user criteria to data
          if x(i)=cstr(vdata(k,1)) then
            'Capture the row and worksheet name
            m=m+1
            'If array is too small increase size
            if m>ubound(saresults) then 
              redim preserve saresults(1 to ubound(saresults)*2, 1 to 2)
            end if
            'Get name and row.
            saresults(m,1)=wks(j).name
            saresults(m, 2)=k
            exit for
          end if
        next i
      next k
    next j
    
    'Resize array to correct size
    redim preserve saresults(1 to m, 1 to 2)
    'Print results to a result page (you could also create hyperlinks here
    'that would make it so the person can click and go to the respective page.
    'You would have to do a loop for each result on the range.
    with worksheets("Result Page")
      .range(.cells(1,1),.cells(m,2))=saresults
    end with
    
  • 2

    我几乎没有添加Jon49的答案,这似乎涵盖了基础知识 . 但我希望我在VBA编程生涯中早些时候发现过Forms . 它们起初可能有点令人困惑,但是一旦掌握,它们就会极大地增加宏的可用性 .

    表单可用于从用户(而不是InputBox)获取值,也可用于向用户提供进度信息 . 我只会谈谈第二种用法 . 你的宏可能需要一些时间;用户有时间喝一杯咖啡还是会在5秒内完成?我讨厌坐在那里的节目说“请等待 - 这可能需要几分钟到几个小时” .

    以下代码将表单加载到内存中,将其显示给用户并在结尾处将其从内存中删除 . 如果您没有卸载表单,它将在宏结束后保留在屏幕上,如果您想为用户留言,这可能很有用 . 此表单显示为“无模式”,表示宏显示并继续 . 如果显示为“模态”,则宏将停止,直到用户输入表单所需的任何信息 .

    Load frmProgress
      Progress.Show vbModeless
      ' Main code of macro
      Unload frmProgress
    

    提供有关表单的教程的网站没有尽头,所以我将主要描述什么而不是如何 .

    在VB编辑器中,插入一个UserForm . 如果你想要它更大,拖动底部和右边缘 . 使用“属性”窗口将名称更改为frmProgress .

    从工具箱中拖动四个标签并将它们排成一行 . 将标签1的 Headers 设置为“工作表”,将标签3的 Headers 设置为“of” . 名称标签2“lblWSNumCrnt”和名称标签4“lblWSNumTotal” .

    在“for j = 0 to 2”周围添加以下内容

    frmProgress.lblWSNumTotal.Caption = 3
    for j = 0 to 2
      frmProgress.lblWSNumCrnt.Caption = j + 1
      DoEvents
    

    这意味着当宏预测时,用户将看到以下步骤,n从1步进3步:

    Worksheet    n  of     3
    

    为行号添加另外四个标签,以及围绕k循环的以下代码:

    frmProgress.lblRowNumTotal.Caption = ubound(vdata, 1)
    for k = 1 to ubound(vdata, 1)
      frmProgress.lblRowNumCrnt.Caption = k
      DoEvents
    

    现在用户将会看到类似的内容:

    Worksheet    2  of     3
          Row  1456 or  2450
    

    上面的技巧很简单,并且不涉及任何改进的Jon49 's code. The following technique, borrowed from Wrox'优秀的Excel VBA程序员参考,稍微复杂但是给你的宏更专业的外观 .

    创建一个贯穿整个表单的标签 . 将其命名为“lblToDo”并将其着色为白色 . 在顶部创建相同大小的另一个标签 . 将其命名为“lblDone”并将其涂成黑色 .

    创建代码副本以计算顶部每个工作表中的行数,以便在执行任何其他操作之前计算总行数“TotalRowsTotal” .

    创建一个新变量“TotalRowsCrnt”,将其初始化为零,并为每个工作表中的每一行添加一个变量 .

    内内循环,添加:

    frmProgress.lblToDo.Width =  _
            frmProgress.lblDone.Width * TotalRowsCrnt / TotalRowsTotal
    

    对于我工作的所有组织仍在使用的Excel 2003,这会给出一个进度条,其中黑色Done标签稳定地覆盖白色ToDo标签 . 更高版本的Excel可能会提供进度条控件作为标准 .

    我希望这能为您提供一些使您的宏对用户更具吸引力的想法 .

相关问题