没有选择的excel vba冻结窗格

Posted

技术标签:

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

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

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

有什么建议吗?

【问题讨论】:

【参考方案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,请参阅***.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 中可见时才成立。

关于此的其他几点:

    使用 Application.goto 不一定会放置您所使用的任何单元格 正试图去左上角 使用.goto时放在左上角的单元格可以依赖 关于 excel 窗口的大小、当前的缩放级别等(相当随意) 可以将拆分放置以便您看不到 它们甚至在可见窗口中滚动(如果 .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 没有完成任何操作,但我很好奇这是否真的是真的。

以上是关于没有选择的excel vba冻结窗格的主要内容,如果未能解决你的问题,请参考以下文章

excel如何冻结首行?

如何冻结输出工作表的窗格

excel如何冻结首行或首列及首行首列同时冻结

EXCEL表格中间有一条灰色的线,求解决!

使用poi导出的excel怎么设置表头不动

如何冻结excel表格前二列