Excel异常行为:如果应用程序不可见,则隐藏功能区还会隐藏TITLE栏

时间:2018-08-03 12:21:55

标签: excel excel-vba

我正在创建一个自定义应用程序,它将在其自己的单独Excel实例(新应用程序)中启动。

默认情况下,新创建的实例不可见,因此需要手动使它们可见。我想让我的应用程序在所有设置完成后才可见–以避免屏幕闪烁。与关闭和打开ScreenUpdating相比,我认为这是一个更时尚的解决方案:即,如果该应用程序仍然不可见,则无需切换ScreenUpdating。您可以在准备就绪之前将其保持不可见状态。

但是,在隐藏功能区方面,我遇到了一些异常行为。以下命令:

Application.ExecuteExcel4Macro ("SHOW.TOOLBAR(" & Chr(34) & "Ribbon" & Chr(34) & ",False)")

...用于隐藏功能区。如果仅将命令粘贴到“立即”窗口中,则可以看到此效果。它隐藏了功能区:但是,应用程序窗口不受影响。但是...如果在执行此命令时不可见该应用程序,则似乎不仅功能区被隐藏了,而且窗口的整个标题栏也被隐藏了!在以下子代码中对此进行测试:

Sub TestVisible()
    Dim xl As Excel.Application
    Set xl = New Excel.Application
    xl.Workbooks.Add ' Required
    ' xl.Visible = True ' Un-comment out this line to preserve the Title bar
    ' If the following command is executed while Visible=False, then the TITLE bar will ALSO be hidden alongside the ribbon.
    xl.ExecuteExcel4Macro ("SHOW.TOOLBAR(" & Chr(34) & "Ribbon" & Chr(34) & ",False)") ' Hide the ribbon
    xl.Visible = True
End Sub

所以我的问题是:有人对此行为有解释吗?是虫子吗?如果是这样,是否知道?如果不是,那有解决方案吗? (除了解决方法,即在隐藏功能区之前先使应用程序可见,然后在需要的地方设置ScreenUpdating。)

1 个答案:

答案 0 :(得分:2)

我在此方面花费的时间比我本来应该花的时间还多:),但是这里有一些解决方法,也是我认为是完整的解决方案,因为它可以将“标题”栏还原到新的Excel实例。

我可以推测“ ExecuteExcel4Macro为什么不能在这里真正正常工作:

ExecuteExcel4Macro确实可以追溯到1995年以前的Excel。它并不经常使用,我收集到的这些功能中有很多已不再起作用。使用它来隐藏Ribbon菜单栏-直到2007年才出现,它代表了完全暴露于VBA的旧版Application.CommandBars的范例转变(示例:您不能做{{1 }}就像在Excel <= 2003 中一​​样)-似乎存在一些Microsoft可能永远不会解决的错误。

我通过使用各种WinAPI函数的进一步调查发现了一件有趣的事情,即Application.CommandBars("some command bar name").Visible = False函数在Excel不可见时返回0。我可能做错了什么,但如果没有做错,那我想这可能会助长您观察到的行为。

解决方法(如果您不熟悉WinAPI)

这是一种相似方法,但是功能区只是最小化,用户仍然可以切换它:

FindWindow

采用原始代码的另一种可能的解决方法是强制使新实例最小化(并且可见),以避免出现标题栏的错误行为:

Sub TestAlternate()
    Dim xl As Excel.Application
    Set xl = New Excel.Application
    xl.Workbooks.Add ' Required
    xl.Application.CommandBars.ExecuteMso ("HideRibbon")
    xl.Visible = True
End Sub

关于OP的评论中提到的第三个可能的解决方案,我建议重构代码以从模板添加新工作簿。该模板将包含CustomUI XML规范和适当的VBA回调函数,以隐藏功能区上的项目。我不确定100%是否可以使用此方法完全复制您的设计意图,对于初学者来说,这不是一件容易的事,但这可能值得探索。


通过WinAPI调用隐藏功能区

我发现this solution很有前途,但实现起来比我预期的要复杂,但是我认为我有一些对您有用的东西,并经过上述修改。

在我进行的调试中,功能区和标题栏似乎链接在一起,您会发现我根本不使用Sub TestVisible() Dim xl As Excel.Application Set xl = New Excel.Application xl.Workbooks.Add ' Required xl.Application.WindowState = xlMinimized xl.Visible = True ' Un-comment out this line to preserve the Title bar ' If the following command is executed while Visible=False, then the TITLE bar will ALSO be hidden alongside the ribbon. xl.ExecuteExcel4Macro ("SHOW.TOOLBAR(" & Chr(34) & "Ribbon" & Chr(34) & ",False)") ' Hide the ribbon xl.Visible = False xl.Application.WindowState = xlNormal ' any additional code required to set up/configure the new instance/application xl.Visible = True End Sub ,因为此函数调用可以处理丝带。我怀疑这种联系可能会导致您原来的问题!

以下是API挂钩:

ExecuteExcel4Macro

这是隐藏功能区的新子项,已在Excel 2016中进行了测试:

Option Explicit

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
    (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
    (ByVal hwnd As Long, ByVal nIndex As Long) As Long

Private Const GWL_STYLE = (-16)
Private Const WS_CAPTION = &HC00000
Private Const WS_MAXIMIZEBOX = &H10000
Private Const WS_MINIMIZEBOX = &H20000
Private Const WS_SYSMENU = &H80000

Private Declare Function SetWindowPos Lib "user32" _
    (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _
    ByVal x As Long, ByVal y As Long, ByVal cx As Long, _
    ByVal cy As Long, ByVal wFlags As Long) As Long

Private Enum ESetWindowPosStyles
    SWP_SHOWWINDOW = &H40
    SWP_HIDEWINDOW = &H80
    SWP_FRAMECHANGED = &H20
    SWP_NOACTIVATE = &H10
    SWP_NOCOPYBITS = &H100
    SWP_NOMOVE = &H2
    SWP_NOOWNERZORDER = &H200
    SWP_NOREDRAW = &H8
    SWP_NOREPOSITION = SWP_NOOWNERZORDER
    SWP_NOSIZE = &H1
    SWP_NOZORDER = &H4
    SWP_DRAWFRAME = SWP_FRAMECHANGED
    HWND_NOTOPMOST = -2
End Enum

Private Declare Function GetWindowRect Lib "user32" _
    (ByVal hwnd As Long, lpRect As RECT) As Long

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Sub ShowTitleBar(xlApp As Excel.Application, bShow As Boolean, Optional bCaptionOverride As Boolean = True)
Dim lStyle As Long
Dim tRect As RECT
'Dim sWndTitle As String
Dim xlhnd

'## I modified this function to receive an Application instance and
'## to use it's .Hwnd property rather than the FindWindow API call
xlhnd = xlApp.hwnd

'// Get the window's position:
GetWindowRect xlhnd, tRect

If Not bShow Then
    lStyle = GetWindowLong(xlhnd, GWL_STYLE)
    lStyle = lStyle And Not WS_SYSMENU
    lStyle = lStyle And Not WS_MAXIMIZEBOX
    lStyle = lStyle And Not WS_MINIMIZEBOX
    '## I added this logic to ensure the CAPTION may always displayed if bCaptionOverride
    If Not bCaptionOverride Then
        lStyle = lStyle And Not WS_CAPTION
    Else
        lStyle = lStyle Or WS_CAPTION
    End If
Else
    lStyle = GetWindowLong(xlhnd, GWL_STYLE)
    lStyle = lStyle Or WS_SYSMENU
    lStyle = lStyle Or WS_MAXIMIZEBOX
    lStyle = lStyle Or WS_MINIMIZEBOX
    lStyle = lStyle Or WS_CAPTION
End If

SetWindowLong xlhnd, GWL_STYLE, lStyle

xlApp.DisplayFullScreen = Not bShow

'// Ensure the style is set and makes the xlwindow the
'// same size, regardless of the title bar.
SetWindowPos xlhnd, 0, tRect.Left, tRect.Top, tRect.Right - tRect.Left, _
    tRect.Bottom - tRect.Top, SWP_NOREPOSITION Or SWP_NOZORDER Or SWP_FRAMECHANGED

End Sub

我在Sub Test() Dim xl As New Excel.Application xl.Workbooks.Add ShowTitleBar xl, False ' configure the application xl.Visible = True ' Re-enable the ribbon, or the user can double-click the title bar or Restore menu. ShowTitleBar xl, True End Sub 语句后中断并确认未显示功能区:

enter image description here

最后,在该过程结束时,我们可以恢复功能区(如果需要)

enter image description here


注意::我注意到菜单中的“还原”选项(在标题栏中右键单击Excel图标)不会还原功能区。使用Visible = True,但是如果您使用WinAPI调用,它会确实恢复功能区。

enter image description here

如果您只是设置环境,那么这无关紧要,但是我实现的方法不能完全复制该功能。可能可以通过WinAPI进行进一步配置(例如,我知道您可以使用ExecuteExcel4Macro

从标题栏中删除该菜单。
相关问题