Excel 中从右到左的用户窗体 - VBA

2024-03-14

请看下面的代码并测试一下:

Private Sub CommandButton1_Click()
   MsgBox "This window converted Right to Left!", vbMsgBoxRtlReading
End Sub

此代码将消息窗口从右转换为左。当关闭按钮移动到窗口左侧时。我该如何为用户表单执行此操作? (希望T.M.,马蒂厄·金登并且......没有说:“你的问题有问题。请阅读链接......”)

就像下面的图片(当然照片是Photoshop的!):


模拟从右到左的显示,如下所示MsgBox

有必要使用一些API*) 获得所需布局的函数独立的默认情况下,从使用从右到左功能的语言设置。

  1. 识别用户表单handle访问更多 API 方法
  2. Remove用户表单的标题栏
  3. 更换它例如使用显示标题的标签控件并为其提供拖动功能来移动用户窗体(此处:Label1).
  4. 使用另一个控件(此处:Label2)来模拟系统转义“x”。

    *) API——应用程序编程接口

一个简单的用户窗体代码示例

您所需要的只是提供 2 个标签控件,其中Label1替换标题栏并接收用户窗体的标题和Label2模拟系统转义“x”。此外,这个例子使用了Type方便处理用户表单的声明handle对于需要它来执行进一步 API 操作的多个事件过程。

► 截至 2018 年 10 月 22 日的第二次编辑注意事项

由于窗口句柄被声明为LongPtr在 Office 2010 或更高版本中以及Long在之前的版本中,需要通过条件编译常量来区分不同版本(例如#If VBA7 Then ... #Else ... #End If;参见第二节。还使用Win64常数来识别actually安装的 64 位 Office 系统 - 请注意,Office 通常默认安装为 32 位)。

Option Explicit                 ' declaration head of userform code module

#If VBA7 Then                   ' compile constant for Office 2010 and higher
    Private Type TThis          ' Type declaratation
        frmHandle As LongPtr    ' receives form window handle 64bit to identify this userform
    End Type
#Else                           ' older versions
    Private Type TThis          ' Type declaratation
        frmHandle As Long       ' receives form window handle 32bit to identify this userform
    End Type
#End If
Dim this As TThis               ' this - used by all procedures within this module

Private Sub UserForm_Initialize()
' ~~~~~~~~~~~~~~~~~~~~~~~
' [1] get Form Handle
' ~~~~~~~~~~~~~~~~~~~~~~~
  this.frmHandle = Identify(Me) ' get UserForm handle via API call (Long)
' ~~~~~~~~~~~~~~~~~~~~~~~
' [2] remove System Title Bar
' ~~~~~~~~~~~~~~~~~~~~~~~
  HideTitleBar (this.frmHandle) ' hide title bar via API call
End Sub

Private Sub Label1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
' Purpose: Replaces System Title Bar (after removal via API) and receives dragging functionality
   ' ~~~~~~~~~~~~~~~~~~~~~~~~~~
   ' [3] allow to move UserForm
   ' ~~~~~~~~~~~~~~~~~~~~~~~~~~
     If Button = 1 Then DragForm this.frmHandle
End Sub

Private Sub Label2_Click()
' Purpose:  Label "x" replaces System Escape (after removal in step [2])and hides UserForm
' ~~~~~~~~~~~~~~~~~
' [4] hide UserForm
' ~~~~~~~~~~~~~~~~~
  Me.Hide
End Sub

Private Sub UserForm_Layout()
  Me.RightToLeft = True
' Simulated Escape Icon
  Me.Label2.Caption = " x"
  Me.Label2.BackColor = vbWhite
  Me.Label2.Top = 0
  Me.Label2.Left = 0
  Me.Label2.Width = 18: Me.Label2.Height = 18
' Simulated UserForm Caption
  Me.Label1.Caption = Me.Caption
  Me.Label1.TextAlign = fmTextAlignRight    ' <~~ assign right to left property
  Me.Label1.BackColor = vbWhite
  Me.Label1.Top = 0: Me.Label1.Left = Me.Label2.Width: Me.Label1.Height = Me.Label2.Height
  Me.Label1.Width = Me.Width - Me.Label2.Width - 4
End Sub

二. API 函数的单独代码模块

a) 带有常量和特殊 API 声明的声明头

有必要提供不同的应用程序版本,因为某些参数的代码声明有所不同(例如 PtrSafe)。 64 位声明开始如下:Private Declare PtrSafe ...

还要注意通过以下方式进行正确的声明#If, #Else and #End If允许版本相关的编译。

前缀&H常量中使用的 代表十六进制值。

Option Explicit

Private Const WM_NCLBUTTONDOWN = &HA1&
Private Const HTCAPTION = 2&
Private Const GWL_STYLE = (-16)
Private Const WS_BORDER = &H800000
Private Const WS_DLGFRAME = &H400000
Private Const WS_CAPTION = WS_BORDER Or WS_DLGFRAME

#If VBA7 Then                                               ' True if you're using Office 2010 or higher
    ' [0] ReleaseCapture
    Private Declare PtrSafe Sub ReleaseCapture Lib "User32" ()
    ' [1] SendMessage
    Private Declare PtrSafe Function SendMessage Lib "User32" _
      Alias "SendMessageA" _
      (ByVal hWnd As LongPtr, ByVal wMsg As Long, _
      ByVal wParam As LongPtr, lParam As Any) As LongPtr    ' << arg's hWnd, wParam + function type: LongPtr
    ' [2] FindWindow
    Private Declare PtrSafe Function FindWindow Lib "User32" _
            Alias "FindWindowA" _
           (ByVal lpClassName As String, _
            ByVal lpWindowName As String) As LongPtr        ' << function type: LongPtr
    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ' Two API functions requiring the Win64 compile constant for 64bit Office installations
    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #If Win64 Then                                          ' true if Office explicitly installed as 64bit
      ' [3a] Note that GetWindowLong has been replaced by GetWindowLongPtr
        Private Declare PtrSafe Function GetWindowLongPtr Lib "User32" _
            Alias "GetWindowLongPtrA" _
           (ByVal hWnd As LongPtr, _
            ByVal nIndex As Long) As LongPtr
      ' [3b] Note that GetWindowLong has been replaced by GetWindowLongPtr
      '      Changes an attribute of the specified window.
      '      The function also sets a value at the specified offset in the extra window memory.
        Private Declare PtrSafe Function SetWindowLongPtr Lib "User32" _
            Alias "SetWindowLongPtrA" _
           (ByVal hWnd As LongPtr, _
            ByVal nIndex As Long, _
            ByVal dwNewLong As LongPtr) As LongPtr
    #Else                                                   ' true if Office install defaults 32bit
      ' [3aa] Note that GetWindowLong has been replaced by GetWindowLongPtr Alias GetWindowLongA !
        Private Declare PtrSafe Function GetWindowLongPtr Lib "User32" _
            Alias "GetWindowLongA" _
           (ByVal hWnd As LongPtr, _
            ByVal nIndex As Long) As LongPtr
      ' [3bb] Note that GetWindowLong has been replaced by GetWindowLongPtr Alias SetWindowLongA !
        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

    ' [4] DrawMenuBar
    Private Declare PtrSafe Function DrawMenuBar Lib "User32" _
           (ByVal hWnd As LongPtr) As Long                  ' << arg hWnd: LongPtr

#Else                                                       ' True if you're using Office before 2010 ('97)

    Private Declare Sub ReleaseCapture Lib "User32" ()
    Private Declare Function SendMessage Lib "User32" _
          Alias "SendMessageA" _
          (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

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


    Private Declare Function GetWindowLong Lib "User32" _
            Alias "GetWindowLongA" _
           (ByVal hWnd As Long, _
            ByVal nIndex As Long) 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 DrawMenuBar Lib "User32" _
           (ByVal hWnd As Long) As Long
#End If

b) 以下程序(a 节之后)

' ~~~~~~~~~~~~~~~~~~~~~~
' 3 Procedures using API
' ~~~~~~~~~~~~~~~~~~~~~~

#If VBA7 Then                               ' Office 2010 and higher
    Public Function Identify(frm As Object) As LongPtr
    ' Purpose: [1] return window handle of form
    ' Note:    vbNullString instead of ThunderXFrame (97) and class names of later versions
      Identify = FindWindow(vbNullString, frm.Caption)
    End Function

    Public Sub HideTitleBar(hWnd As LongPtr)
    ' Purpose: [2] remove Userform title bar
      SetWindowLongPtr hWnd, GWL_STYLE, GetWindowLongPtr(hWnd, GWL_STYLE) And Not WS_CAPTION
    End Sub
        Public Sub ShowTitleBar(hWnd As LongPtr)
        ' Purpose: show Userform title bar
          SetWindowLongPtr hWnd, GWL_STYLE, GetWindowLongPtr(hWnd, GWL_STYLE) Or WS_CAPTION
        End Sub

    Public Sub DragForm(hWnd As LongPtr)
    ' Purpose: [3] allow to drag & move userform via control (here via e.g.: Label1)
      Call ReleaseCapture
      Call SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&)
    End Sub

#Else                                       ' vers. before Office 2010 (Office '97)
    Public Function Identify(frm As Object) As Long
    ' Purpose: [1] return window handle of form
    ' Note:    vbNullString instead of ThunderXFrame (97) and class names of later versions
      Identify = FindWindow(vbNullString, frm.Caption)
    End Function
    Public Sub HideTitleBar(hWnd As Long)
    ' Purpose: [2] remove Userform title bar
      SetWindowLong hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) And Not WS_CAPTION
    End Sub
    '    Public Sub ShowTitleBar(HWND As Long)
    '    ' Purpose: show Userform title bar
    '      SetWindowLong HWND, GWL_STYLE, GetWindowLong(HWND, GWL_STYLE) Or WS_CAPTION
    '    End Sub

    Public Sub DragForm(hWnd As Long)
    ' Purpose: [3] allow to drag & move userform via control (here via e.g.: Label1)
      Call ReleaseCapture
      Call SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&)
    End Sub


#End If

► 注意事项: API 声明未经测试actually在 Office 2010 或更高版本中安装了 64 位系统。 The 第二次编辑截至 2018 年 10 月 22 日试图纠正几个LongPtr声明(仅适用于指向 → 句柄或 → 内存位置的指针)并使用当前的 Get/SetWindowLongPtr 函数显式区分Win64 and Win32;参见还编辑过TypeUserForm 代码模块的声明头中的声明)。

也可以看看Office 2010 32 位和 64 位版本之间的兼容性 http://msdn.microsoft.com/en-us/library/ee691831(office.14).aspx and Office 2010 帮助文件:具有 64 位支持的 Win32API PtrSafe http://www.microsoft.com/en-us/download/confirmation.aspx?id=9970

附加说明

用户窗体是Windows,可以通过它们的窗口来识别handle。 用于此目的的 API 函数是FindWindow处理两个参数: 1) 一个字符串,给出需要查找的窗口类的名称;2) 一个字符串,给出caption它需要找到的窗口(用户窗体)的名称。

因此,人们经常区分版本 '97(UserForm 类名称“ThunderXFrame”)和更高版本(“ThunderDFrame”):

 If Val(Application.Version) < 9 Then 
    hWnd = FindWindow("ThunderXFrame", frm.Caption)   ' if used within Form: Me.Caption
 Else   ' later versions
    hWnd = FindWindow("ThunderDFrame", frm.Caption)   ' if used within Form: Me.Caption
 End If 

然而使用vbNullString (and独特的标题!)反而使编码变得更加容易:

 hWnd = FindWindow(vbNullString, frm.Caption)         ' if used within Form: Me.Caption

推荐进一步阅读

UserForm 代码模块实际上是classes并且应该这样使用。所以我建议阅读 M. Guindon 的文章用户表单1.显示 https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/。 - 可能也有一些兴趣正确销毁无模式 UserForm 实例 https://stackoverflow.com/questions/47357708/vba-destroy-a-modeless-userform-instance-properly

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Excel 中从右到左的用户窗体 - VBA 的相关文章

  • 从受密码保护的 Excel 文件到 pandas DataFrame

    我可以使用以下命令打开受密码保护的 Excel 文件 import sys import win32com client xlApp win32com client Dispatch Excel Application print Exce
  • 你将如何开始自动化我的工作? - 第2部分

    后续这个问题 https stackoverflow com questions 2796128 how would you start automating my job 在经历了第一波进货 9 小时的复制 粘贴 后 我现在相信我已经满足
  • 使用 MemoryStream 创建 Open XML 电子表格时的 Excel 和“不可读内容”

    使用 Open XML SDK v2 0 创建 Excel 电子表格时 我们的 Excel 输出最初可以成功运行几个月 最近Excel 所有版本 开始抱怨 Excel在 zot xlsx 中发现不可读的内容 是否要恢复此工作簿的内容 我们正
  • Excel 宏与 Javascript

    我希望使用 Javascript 中的宏而不是默认的 VBA 来操作 Excel 电子表格 我可以使用以下 VBA 代码执行 javascript 代码 javascript to execute Dim b As String b fun
  • 如何使用 Excel.UriLink.16 更改 Excel URL 的文件关联?

    我正在尝试更改文件关联 以便在另一个浏览器中打开 Excel 单元格中的 URL 根据使用 CMD ftype命令与Excel UriLink 16 我应该能够使用以下命令从 powershell 通过 cmd 执行此操作 To chang
  • 在 VBA 中使用 getElementsByClassName

    我正在使用此代码从页面获取产品名称 页面代码是 div class product shop col sm 7 div class product name h1 Claro Glass 1 5 L Rectangular Air Tigh
  • 将 Python Selenium 输出写入 Excel

    我编写了一个脚本来从在线网站上抓取产品信息 目标是将这些信息写入 Excel 文件 由于我的Python知识有限 我只知道如何在Powershell中使用Out file导出 但结果是每个产品的信息都打印在不同的行上 我希望每种产品都有一条
  • 选择在 Excel 宏(VBA 中的范围对象)中具有值的列

    如何修改 VBA 中的这一行以仅选择具有值的列 Set rng Range A1 Range A65536 End xlUp SpecialCells xlCellTypeVisible 我不认为我做的事情是正确的CountLarge财产是
  • 使用 VBA 通过简单命令从非连续范围的并集获取值到数组中(无循环)

    我有以下任务 表面上很简单 使用 VBA 将电子表格上多个列的值复制到二维数组中 为了让生活更有趣 这些柱子并不相邻 但它们的长度都相同 显然 可以通过依次循环每个元素来做到这一点 但这看起来非常不优雅 我希望有一个更紧凑的解决方案 但我很
  • 将表行从 Word 文档复制到现有文档表特定单元格

    我正在寻找一个宏 它将内容从一个 Word 文档中的表格复制到另一个现有 Word 文档中的表格到特定单元格中 从第 5 行开始 复制后面的所有行并将其粘贴到现有文档中的第 5 行 这可能吗 在此输入图像描述 https i stack i
  • 如何在不滚动的情况下截取整个电子邮件正文?

    我正在使用 OL2010 想要制作整个电子邮件的屏幕截图 不仅仅是 屏幕 可以用VBA或者外部程序来完成吗 有一个类似的问题 https stackoverflow com questions 4176340关于如何使用 C 实现这一点 注
  • Excel VBA 过滤和复制粘贴数据

    给定一个数据集 假设有 10 列 在 A 列中我有日期 在 B 列中我有 我想仅过滤 A 列 2014 年的数据 B 列 ActiveSheet Range A 1 AR 1617 AutoFilter Field 5 Operator x
  • 在 VBA 中循环合并单元格

    是否可以循环遍历合并的单元格vba questions tagged vba 我的范围内有 6 个合并单元格B4 B40 我只需要这 6 个单元格中的值 6 次迭代 上面的答案看起来已经让你排序了 如果您不知道合并的单元格在哪里 那么您可以
  • VBA XML V6.0 如何让它等待页面加载?

    我一直在努力寻找答案 但似乎找不到任何有用的东西 基本上 我是从一个网站上拉取的 当您在该页面上时 该网站会加载更多项目 我希望我的代码在加载完成后提取最终数据 但不知道如何让 XML httprequest 等待 Edited Sub p
  • 字典、集合和数组的比较

    我正在尝试找出字典与集合和数组相比的相对优点和功能 我发现了一篇很棒的文章here http www experts exchange com articles 3391 Using the Dictionary Class in VBA
  • 我可以用文本框设置变量名称吗? excel

    我可以使用 TextBox Vba Excel 设置变量的名称吗 我必须以在文本框中写入组名称并单击命令按钮的方式输入新的产品组 代码必须从文本框中获取字符串 并将该字符串设置为新创建的数组的名称 我只想在运行时创建一个新变量 或数组 据信
  • 使用 pythoncom 在 Python 进程之间编组 COM 对象

    我希望有人可以帮助我从 Python 进行编组跨进程调用到 Excel 我有一个通过 Python 启动的 Excel 会话 我知道当需要从单独的 Python 进程访问它时 该会话将会启动并运行 我已经使用编组让一切按预期工作CoMars
  • Outlook 无法识别一个或多个姓名

    我有以下 vba 代码 它读取邮箱并向任何发送无效代码作为邮箱回复的用户发送回复 但有时会收到运行时错误 Outlook 无法识别一个或多个名称 我的问题是 创建新的 MAPI 配置文件是否可以解决该问题 或者我是否需要添加一个代码来解析地
  • 根据单元格值向用户窗体添加复选框

    我对 VBA 很陌生 只有 3 天 但我发现它非常有用且易于使用 但现在我面临一个问题 我需要制作一个具有不同复选框的用户窗体 但我需要根据工作表某一列中使用的信息自动添加它们 我相信我可以使用 For Each Next 但我真的不知道如
  • 如何使用VBA根据条件删除Excel中的行?

    我目前正在构建一个宏来格式化数据表并删除不适用的数据行 具体来说 我希望删除列 L ABC 的行以及删除列 AA DEF 的行 到目前为止 我已经实现了第一个目标 但还没有实现第二个目标 现有代码是 Dim LastRow As Integ

随机推荐