【发布时间】:2015-12-04 17:52:23
【问题描述】:
我在 Excel 中有一个 VBA 脚本,它可以冻结 Excel 工作表的窗格,但我很想知道在不先选择范围的情况下这是否可行。下面是冻结第 1 行到第 7 行的代码:
ActiveSheet.Range("A8").Select
ActiveWindow.FreezePanes = True
有什么建议吗?
【问题讨论】:
我在 Excel 中有一个 VBA 脚本,它可以冻结 Excel 工作表的窗格,但我很想知道在不先选择范围的情况下这是否可行。下面是冻结第 1 行到第 7 行的代码:
ActiveSheet.Range("A8").Select
ActiveWindow.FreezePanes = True
有什么建议吗?
【问题讨论】:
使用 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 属性都应该为您完成。
【讨论】:
With ActiveWindow .SplitColumn = 0 .SplitRow = 7 End With ActiveWindow.FreezePanes = True
If .FreezePanes Then .FreezePanes = False 这样的东西可能适合作为With ActiveWindow 块内的第一行。
Application.Windows() 错误并导致Run-time error '9' Subscript out of range,请参阅stackoverflow.com/a/47177498/1026
冻结窗格有很多问题。我添加了我自己的答案,所以我会在这里找到它,并且下次不必重新发明它。
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
Application.Windows (Windows(Thisworkbook.Name)) 不会导致错误崩溃(名称将是“MyWorkbook [Repaired]”)【讨论】:
我发现以前的答案仅适用于 looping 到 tabs 时的某些工作表。我发现以下代码在每个tab 和looped 上都有效(目标是单个workbook),尽管workbook 是activeworkbook。
简而言之:
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
希望这会在未来节省一些研究时间。
【讨论】:
我需要能够正确地重新冻结窗格(尤其是在创建新窗口时),而不会丢失活动单元或弄乱可见范围。花了很多时间,但我认为我有一些可靠的东西:
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
【讨论】:
我知道这很旧,但我发现了这个可能有用的花絮...... 正如 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 Worksheets(1).Range("A1"), True
With ActiveWindow
.SplitColumn = 100
.SplitRow = 100
.FreezePanes = True
End With
CETAB 可能会在他们的回答中处理这个问题。
【讨论】:
是的,如果您的可见窗口不包括单元格 A1,则 ActiveWindow.ScrollRow = 1 和 ActivWindow.ScrollColumn = 1 是 FreezePanes 必须的。
如果您通过选择第 4 行或单元格 A4 以 1:3 冻结行,并且单元格 A3 不可见,则 FreezePanes 函数会将窗口冻结在可见窗口的中心。
此外,如果选择了单元格 B4,并且 A 列不可见,则只有 1:3 行将被冻结(A 列不会被冻结)。同样,如果 1:3 行不可见,则只有 A 列将被冻结。如果 A 列和 1:3 行都不可见,FreezePanes 函数会将窗口冻结在可见窗口的中心。
【讨论】:
拆分的问题是,如果用户解冻窗格,窗格将保持拆分状态。 (我找不到在保持窗格冻结的同时关闭拆分的方法)
这可能太明显/太简单了,但是如果简单地保存当前选择然后重新选择呢?
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
【讨论】:
这是我用的...
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
【讨论】:
我使用 .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 没有完成任何操作,但我很好奇这是否真的是真的。