在Excel VBA用户窗体中隐藏“关闭”按钮以显示我的进度条。

14
我创建了一个用户表单,在宏仍在导入工作表时显示进度条。问题是,用户可以按下红色[X]按钮,关闭并中断处理过程。有没有办法隐藏这个“末日红色按钮”,使潜在用户在运行时不会有任何混淆的按钮可点击。
编辑: 我尝试过这个。
'Find the userform's Window
Private Declare Function FindWindow Lib "user32" _
        Alias "FindWindowA" ( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Long

'Get the current window style
Private Declare Function GetWindowLong Lib "user32" _
        Alias "GetWindowLongA" ( _
        ByVal hWnd As Long, _
        ByVal nIndex As Long) As Long

'Set the new window style
Private Declare Function SetWindowLong Lib "user32" _
        Alias "SetWindowLongA" ( _
        ByVal hWnd As Long, _
        ByVal nIndex As Long, _
        ByVal dwNewLong As Long) As Long

Const GWL_STYLE = -16
Const WS_SYSMENU = &H80000

我在 userform_initialize 中使用了这个

   Dim hWnd As Long, lStyle As Long

   'Which type of userform
   If Val(Application.Version) >= 9 Then
      hWnd = FindWindow("ThunderDFrame", Me.Caption)
   Else
      hWnd = FindWindow("ThunderXFrame", Me.Caption)
   End If

   'Get the current window style and turn off the Close button
   lStyle = GetWindowLong(hWnd, GWL_STYLE)
   SetWindowLong hWnd, GWL_STYLE, (lStyle And Not WS_SYSMENU)

我收到了这个错误信息 enter image description here

这段代码来自 此处链接。我不知道哪里出了问题,而且我已经删除了注释。这是我找到的最简单的代码,所以我想将其集成到我的用户表单中。任何帮助都将不胜感激。


这个问题的答案出现在我的搜索结果中。你尝试过什么吗? - JustinJDavies
1
更新了一些更多的信息 - forums
如果这是您的意图,您能否澄清并将“禁用”一词更改为“隐藏和禁用”?禁用功能相对简单。而隐藏它似乎不是那么容易。 - JustinJDavies
@论坛:您尝试过下面的代码吗? - Peter Albert
为了解决你在截图中展示的问题:将所有API声明移动到模块顶部! - Peter Albert
显示剩余2条评论
6个回答

16
以下是您可以这样调用的例程:
subRemoveCloseButton MyForm

或者从您的表单内部:
subRemoveCloseButton Me 

这是你需要的代码:

Private Const mcGWL_STYLE = (-16)
Private Const mcWS_SYSMENU = &H80000

'Windows API calls to handle windows
#If VBA7 Then
    Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
#Else
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
#End If

#If VBA7 Then
    Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
#Else
    Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
#End If

#If VBA7 Then
    Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
#Else
    Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
#End If


Public Sub subRemoveCloseButton(frm As Object)
    Dim lngStyle As Long
    Dim lngHWnd As Long

    lngHWnd = FindWindow(vbNullString, frm.Caption)
    lngStyle = GetWindowLong(lngHWnd, mcGWL_STYLE)

    If lngStyle And mcWS_SYSMENU > 0 Then
        SetWindowLong lngHWnd, mcGWL_STYLE, (lngStyle And Not mcWS_SYSMENU)
    End If

End Sub

1
我需要添加一个 Call DrawMenuBar(HWnd) 和它的声明。此外,这些声明混乱不堪,并且无法与 Office 64 兼容,请参见 http://www.jkp-ads.com/articles/apideclarations.asp。 - Wolfgang Kuehn
@amadeus:你需要DrawMenuBar做什么?哪个声明出错了?(我手头没有64位的Excel来测试...)谢谢! - Peter Albert
1
如果您在表单已经呈现时(即不是从Userform_Initialize()中调用)调用subRemoveCloseButton(),则菜单不会更改。在这种情况下,您必须调用DrawMenuBar()进行重新呈现。至于64位支持,所有表示指针的Long值都必须替换为LongPtr。例如,FindWindow()返回的是LongPtr而不是Long。hwnd参数也是如此。 - Wolfgang Kuehn
1
注意:使用FindWindow(vbNullString, frm.Caption)调用时,如果您的用户窗体与其他程序窗口具有相同的标题,则可能会删除其[x]按钮而不是用户窗体的[x]按钮。我在下面发布了一个改进的函数,将FindWindow绑定到用户窗体并包括x64安全的API调用。 - Pᴇʜ

10
你可以从以下片段中解决问题:
选择cmdClose按钮 在菜单栏上,选择视图 | 代码 光标闪烁的位置输入以下代码:
Private Sub cmdClose_Click()
  Unload Me
End Sub

在菜单栏上选择 View | Object,返回到用户窗体。 允许用户通过按 Esc 键关闭窗体: 选择 cmdClose 按钮,在属性窗口中将 Cancel 属性改为 True 防止用户通过点击 X 按钮关闭窗体 当打开 UserForm 时,右上角有一个 X。除了使用“关闭窗体”按钮外,人们也可以使用 X 来关闭窗体。如果您想要防止这种情况,请按照以下步骤操作。
右键单击用户窗体的空白部分 选择 View | Code 从顶部右侧的过程下拉列表中选择 QueryClose 在光标闪烁的位置,粘贴以下示例中突出显示的代码。
Private Sub UserForm_QueryClose(Cancel As Integer, _
  CloseMode As Integer)
  If CloseMode = vbFormControlMenu Then
    Cancel = True
    MsgBox "Please use the Close Form button!"
  End If
End Sub

在菜单栏上选择查看|对象,返回到用户窗体。现在,如果有人点击用户窗体中的X,他们将会看到您的消息。
来自http://www.contextures.com/xlUserForm01.html

1
我需要关闭按钮完全消失。虽然在Google上搜索可以得到普遍的答案,但我需要一种方法来隐藏[X]标记。 - forums
1
隐藏按钮纯粹是为了美观。虽然这个答案没有隐藏按钮,但它禁用了它,并且是最容易实现的(没有 API 的东西)。这是我最喜欢的解决方案。但是,“不要点击这里!”消息框是过度设计。在 UserForm_QueryClose 过程中所需的最小编码只是 Cancel = True - johny why
以上评论有误:最少的代码应该是 If CloseMode = vbFormControlMenu Then Cancel = True。如果您能解释一下 CloseMode,那么答案会更好。 - johny why

6

这是对 @Peter Albert 上面答案的改进

  • 现在的 Windows API 调用是 Office x64 安全的
  • 改进了 FindWindow 的调用,只能找到 Excel 用户窗体。原始答案中的函数搜索每个窗口类别(例如,资源管理器窗口和其他程序的窗口)。因此,当它们的名称与 UserForm 相同时,可能会删除其他程序或资源管理器窗口的 [x] 按钮。

Private Const mcGWL_STYLE = (-16)
Private Const mcWS_SYSMENU = &H80000

'Windows API calls to handle windows
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
    ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
  
#If Win64 Then
    Private Declare PtrSafe Function GetWindowLongPtr Lib "user32" Alias "GetWindowLongPtrA" ( _
        ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr
    Private Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrA" ( _
        ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Else
    Private Declare PtrSafe Function GetWindowLongPtr Lib "user32" Alias "GetWindowLongA" ( _
        ByVal hwnd As LongPtr, ByVal nIndex As Long) As LongPtr
    Private Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongA" ( _
        ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#End If

Public Sub RemoveCloseButton(objForm As Object)
    Dim lngStyle As LongPtr
    Dim lngHWnd As LongPtr
    
    Dim lpClassName As String
    lpClassName = vbNullString
    If Val(Application.Version) >= 9 Then
       lpClassName = "ThunderDFrame"
    Else
       lpClassName = "ThunderXFrame"
    End If
    
    lngHWnd = FindWindow(lpClassName, objForm.Caption)
    lngStyle = GetWindowLongPtr(lngHWnd, mcGWL_STYLE)

    If lngStyle And mcWS_SYSMENU > 0 Then
        SetWindowLongPtr lngHWnd, mcGWL_STYLE, (lngStyle And Not mcWS_SYSMENU)
    End If
End Sub

ThunderDFrame是什么?
Excel中的用户窗体实际上属于Windows类ThunderDFrame,这是2002年之后Microsoft Office应用程序中所有UserForms的类。在此之前,它是ThunderXFrame


3
一种有用的禁用按钮的方法是执行以下操作:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = 0 Then Cancel = True
End Sub

尽管这不能完全消除按钮,但它确实使得点击按钮没有任何作用。

2
我知道这是一个旧问题,但针对OP提到的用户表单类型,您不必移除、隐藏或禁用关闭按钮。有一个更简单的方法;)
对于任何没有用户互动元素(按钮等)且完成其目的后会自动关闭的用户表单,只需禁用该表单即可。
要禁用用户表单: 在用户表单的属性中,将Enabled设为False。用户表单将显示,直到其代码告诉它隐藏。用户将无法对表单进行任何操作(无法关闭、无法移动等)。
同时还需要注意,您是否希望用户在用户表单仍在显示时能够在主窗口执行其他操作,这将决定是否设置ShowModal。

2

询问用户是否要关闭表单并丢失编辑内容(说)。 基于Justin和Peter的想法。

Private Sub UserForm_QueryClose(Cancel As Integer, _
                            CloseMode As Integer)
Dim ans
If CloseMode = vbFormControlMenu Then
    Cancel = True
    ans = Msgbox("Cancel edit?", vbQuestion + vbYesNo)
    If ans = vbYes Then
       Me.Hide
    End if
End If
End Sub

编辑:实际上我意识到这有些离题,因为楼主想要删除X选项 - 但我仍然觉得这对交互式表单很有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接