【问题标题】:excel vba freeze pane without select没有选择的excel vba冻结窗格
【发布时间】:2015-12-04 17:52:23
【问题描述】:

我在 Excel 中有一个 VBA 脚本,它可以冻结 Excel 工作表的窗格,但我很想知道在不先选择范围的情况下这是否可行。下面是冻结第 1 行到第 7 行的代码:

ActiveSheet.Range("A8").Select
ActiveWindow.FreezePanes = True

有什么建议吗?

【问题讨论】:

    标签: excel vba


    【解决方案1】:

    使用 View ► Freeze Panes ► Freeze Top Row 命令记录自己,这就是 .FreezePanes 获得的结果。

    With ActiveWindow
        If .FreezePanes Then .FreezePanes = False
        .SplitColumn = 0
        .SplitRow = 1
        .FreezePanes = True
    End With
    

    因此,无论ActiveCell property 是什么,修改.SplitColumn 和/或.SplitRow 属性都应该为您完成。

    【讨论】:

    • 干得好!对于其他所有人,请注意 SplitColumn/SplitRow 值表示拆分上方/左侧的最后一个单元格。因此,要冻结第 1 到 7 行并将它们与第 8 行分开,代码如下所示:With ActiveWindow .SplitColumn = 0 .SplitRow = 7 End With ActiveWindow.FreezePanes = True
    • 我在想像If .FreezePanes Then .FreezePanes = False 这样的东西可能适合作为With ActiveWindow 块内的第一行。
    • 如果您改为通过 Application.Windows("[window name]") 访问 Window 对象,则可以避免使用 ActiveWindow,其中 [window name] 是 Workbook.Name
    • @Loophole 按工作簿名称索引Application.Windows() 错误并导致Run-time error '9' Subscript out of range,请参阅stackoverflow.com/a/47177498/1026
    • 代码会导致错误,当您尝试冻结时,您是否在远离您打算冻结的位置激活了一个单元格。我打算冻结第一列和一些行,并且行被正确冻结,但是对于列,第一个可见列被冻结,这是任意的。如果我然后手动解冻,它仍然是分屏。我尝试在代码之前添加激活,但没有任何效果(可能是因为我关闭了 Screenupdate 以加速我的宏)......然后我尝试了 z32a7ul 发布的解决方案并且有效。我想不同的是添加了 Scroll。
    【解决方案2】:

    冻结窗格有很多问题。我添加了我自己的答案,所以我会在这里找到它,并且下次不必重新发明它。

    Public Sub FreezePanesAt(rngDataTopLeft As Range)
        Dim wndCurrent As Window: For Each wndCurrent In rngDataTopLeft.Worksheet.Parent.Windows
            With wndCurrent
                .FreezePanes = False
                If Not ((rngDataTopLeft.Row = 1) And (rngDataTopLeft.Column = 1)) Then
                    .ScrollRow = 1
                    .ScrollColumn = 1
                    .SplitRow = rngDataTopLeft.Row - 1
                    .SplitColumn = rngDataTopLeft.Column - 1
                    .FreezePanes = True
                End If
            End With
        Next
    End Sub
    

    示例用法:

    FreezePanesAt ThisWorkbook.Worksheets("Sheet1").Range("B3")
    FreezePanesAt ThisWorkbook.Names("Header").RefersToRange
    
    • 输入参数是右下窗格的左上角单元格;我认为这是最常见的用例:您知道要拆分的范围,而不关心它在哪个工作簿/工作表/窗口中
    • 如果输入参数在第一行/第一个单元格而不是A1,那么将只有两个窗格; A1 是一种特殊情况,但是 Excel 会在当前视图的中心拆分窗口,我阻止了这种情况,因为我想不出任何情况下会这样做
    • 它遍历所有附加到工作簿/工作表的窗口;如果同一工作簿有更多窗口(名称将是“MyWorkbook:1”)或 Excel 尝试(通常失败)修复工作簿后,索引到 Application.Windows (Windows(Thisworkbook.Name)) 不会导致错误崩溃(名称将是“MyWorkbook [Repaired]”)
    • 考虑到窗格可能已经被冻结并且用户/另一个宏可能已经滚动到工作簿中的某个位置,并且窗口中左上角的单元格不是 A1

    【讨论】:

      【解决方案3】:

      我发现以前的答案仅适用于 loopingtabs 时的某些工作表。我发现以下代码在每个tablooped 上都有效(目标是单个workbook),尽管workbookactiveworkbook

      简而言之:

      With Application.Windows(DataWKB.Name) 
          Application.Goto ws.Cells(4, 5)
          .SplitColumn = 4
          .SplitRow = 3
          .FreezePanes = True
      End With
      

      Sub 中的代码:(请注意,我在这个 sub 中进行了更多格式化,我试图将其去掉,只留下需要的代码)

      Sub Format_Final_Report()
      Dim DataWKB As Workbook
      Set DataWKB = Workbooks("Report.xlsx")
      Dim ws As Worksheet
      
      Dim tabCNT As Long
      Dim tabName As String
      tabCNT = DataWKB.Sheets.Count
      
      For i = 1 To tabCNT
          Set ws = DataWKB.Worksheets(i)
          tabName = ws.Name
      
      
          With Application.Windows(DataWKB.Name)
              Application.Goto ws.Cells(4, 5)
              .SplitColumn = 4
              .SplitRow = 3
              .FreezePanes = True
          End With
      
      Next i
      
      End Sub
      

      希望这会在未来节省一些研究时间。

      【讨论】:

        【解决方案4】:

        我需要能够正确地重新冻结窗格(尤其是在创建新窗口时),而不会丢失活动单元或弄乱可见范围。花了很多时间,但我认为我有一些可靠的东西:

        Sub FreezePanes(nbLignes As Integer, nbColonnes As Integer, Optional ByVal feuille As Worksheet)
            If feuille Is Nothing Then Set feuille = ActiveSheet Else feuille.Activate
            Error GoTo erreur
            With ActiveWindow
                If .View = xlNormalView Then
                    If .FreezePanes Then .FreezePanes = False
                    If .Split Then .Split = False
        
                    .SplitColumn = nbColonnes
                    .SplitRow = nbLignes
        
                    If .Panes.Count = 4 Then 'rows and columns frozen
                        .Panes(1).ScrollRow = 1
                        .Panes(1).ScrollColumn = 1
                        .Panes(2).ScrollRow = 1 'top right pane
                        .Panes(3).ScrollColumn = 1 'bottom left pane
                    ElseIf nbLignes > 0 Then .Panes(1).ScrollRow = 1
                    ElseIf nbColonnes > 0 Then .Panes(1).ScrollColumn = 1
                    Else: GoTo erreur
                    End If
        
                    .FreezePanes = True
                End If
            End With
            Exit Sub
        erreur:
            Debug.print "Erreur en exécutant le sub 'FreezePanes " & nbLignes & ", " & nbColonnes & ", '" & feuille.Name & "' : code #" & Err.Number & Err.Description
        End Sub
        

        【讨论】:

          【解决方案5】:

          我知道这很旧,但我发现了这个可能有用的花絮...... 正如 ChrisB 所说,SplitColumn/SplitRow 值表示当前可见窗口的拆分 BUT 上方/左侧的最后一个单元格。所以如果你碰巧有这样的代码:

          Application.Goto Worksheets(2).Range("A101"), True
          With ActiveWindow
           .SplitColumn = 0
           .SplitRow = 10
           .FreezePanes = True
          End With
          

          拆分将在第 110 行和第 111 行之间,而不是第 10 行和第 11 行。

          为澄清和添加更多信息而进行了编辑:
          我的观点是这些值是左上角单元格的偏移量,而不是单元格的地址。因此,ChrisB 在 2015 年 12 月 4 日 18:34 在主要答案下的评论仅在第 1 行在 Activewindow 中可见时才成立。

          关于此的其他几点:

          1. 使用 Application.goto 不一定会放置您所使用的任何单元格 正试图去左上角
          2. 使用.goto时放在左上角的单元格可以依赖 关于 excel 窗口的大小、当前的缩放级别等(相当随意)
          3. 可以将拆分放置以便您看不到 它们甚至在可见窗口中滚动(如果 .FreezePanes = 真的)。例如:
          Application.Goto Worksheets(1).Range("A1"), True  
          With ActiveWindow  
           .SplitColumn = 100  
           .SplitRow = 100  
           .FreezePanes = True  
          End With  
          

          CETAB 可能会在他们的回答中处理这个问题。

          【讨论】:

          • 没有意义。似乎工作正常,即使引用的窗口不活动。
          【解决方案6】:

          是的,如果您的可见窗口不包括单元格 A1,则 ActiveWindow.ScrollRow = 1ActivWindow.ScrollColumn = 1 是 FreezePanes 必须的。

          如果您通过选择第 4 行或单元格 A4 以 1:3 冻结行,并且单元格 A3 不可见,则 FreezePanes 函数会将窗口冻结在可见窗口的中心。

          此外,如果选择了单元格 B4,并且 A 列不可见,则只有 1:3 行将被冻结(A 列不会被冻结)。同样,如果 1:3 行不可见,则只有 A 列将被冻结。如果 A 列和 1:3 行都不可见,FreezePanes 函数会将窗口冻结在可见窗口的中心。

          【讨论】:

            【解决方案7】:

            拆分的问题是,如果用户解冻窗格,窗格将保持拆分状态。 (我找不到在保持窗格冻结的同时关闭拆分的方法)

            这可能太明显/太简单了,但是如果简单地保存当前选择然后重新选择呢?

            Sub FreezeTopRow()
                
                'First save the current selection to go back to it later
                Dim rngOriginalSelection As Range
                Set rngOriginalSelection = Selection
                
                'Change selection to A2 to make .FreezePanes work
                ActiveSheet.Range("A2").Select
                ActiveWindow.FreezePanes = True
            
                'Change selection back to original
                rngOriginalSelection.Select
            
            End Sub
            

            【讨论】:

            • 是的,这种技术最可靠,副作用最少。 (请参阅上面关于 user4039065 的帖子的评论。)但我也会在选择单元格 A2 之前设置 ActiveWindow.FreezePanes = False,以防冻结窗格已设置为一些其他状态,这将阻止此代码产生所需的效果。
            • 另外,请注意:代替 Range("A2").Select,可能会很想执行类似 Range("1:1") 的操作.SelectRows(1).Select,但这样做实际上会创建一个四窗口冻结窗格状态。 (没有明显的用处,所以可能是一个旧的 Excel 错误?)
            【解决方案8】:

            这是我用的...

            Public Sub FreezeTopRowPane(ByRef MyWs As Excel.Worksheet, _
                                        Optional ByVal AfterRowNr As Integer = 1)
            
            Dim SavedWS As Worksheet
            Dim SavedUpdating As Boolean
            
            SavedUpdating = Application.ScreenUpdating      'save current screen updating mode
            
            Set SavedWS = ActiveSheet                       'save current active sheet
            
            Application.ScreenUpdating = False              'turn off screen updating
            MyWs.Activate                                   'activate worksheet for panes freezing
            ActiveWindow.FreezePanes = False                'turn off freeze panes in case 
            With ActiveWindow
                .SplitColumn = 0                            'set no column to split
                .SplitRow = AfterRowNr                      'set the row to split, default = row 1
            End With
            ActiveWindow.FreezePanes = True                 'trigger the new pane freezing
            
            SavedWS.Activate                                'restore previous (saved) ws as active
            
            Application.ScreenUpdating = SavedUpdating      'restore previous (saved) updating mode
            
            End Sub
            

            【讨论】:

              【解决方案9】:

              我使用 .Select 与 .Activate 进行了冻结时间测试。这是代码

              Dim numLoops As Long
              Dim StartTime, LoopTime As Long
              numLoops = 1000
              
              
              Debug.Print ("Timing test of numloops:" & numLoops)
              
              StartTime = Timer
              
              For I = 0 To numLoops
                      targetSheet.Activate
                  With ActiveWindow
                  If .FreezePanes Then .FreezePanes = False
                      .SplitColumn = 2
                      .SplitRow = 1
                      .FreezePanes = True
                  End With
              
              Next I
              
              LoopTime = Timer
              Debug.Print ("Total time of activate method:" & Format((LoopTime - StartTime) / 86400, "hh:mm:ss"))
              StartTime = Timer
              
              For I = 0 To numLoops
                      targetSheet.Select
                      Application.Range("C2").Select
                      Application.ActiveWindow.FreezePanes = True
              Next I
              
              LoopTime = Timer
              Debug.Print ("Total time of select method:" & Format((LoopTime - StartTime) / 86400, "hh:mm:ss"))
              

              这是结果。

              Timing test of numloops:1000 
              Total time of activate method:00:00:39 
              Total time of select method:00:00:01
              

              如您所见,.Select 要快得多。

              【讨论】:

              • 为了更准确的比较,将窗格冻结在单独的工作表上,而不是在同一个工作表上一遍又一遍地冻结。在现实世界中多次使用冻结窗格都将涉及单独的工作表。 (我可以想到一个现实世界的例子:为不同的用户生成大量报告。)另外,您的 Select 方法不会首先取消冻结窗格,所以如果窗格已经被冻结,即使在不同的位置表,它没有做任何事情。我怀疑时间太快了,因为循环 2-1000 没有完成任何操作,但我很好奇这是否真的是真的。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-21
              • 2012-05-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多