UserForm.Top值从分配更改

我很久以前用这个网站来find我的问题的答案,但我找不到任何关于这个问题。 如果我错过了任何事情,请提前道歉。

所以我有一个工作簿(Office 2013,VBA 7.1),我试图使用一个用户窗体作为一个菜单,它将保持静止在页面上,并随着工作簿一起移动。 我使用来自http://www.cpearson.com/excel/SetParent.aspx的代码的组合来locking窗体和http://www.oaltd.co.uk/Excel/Default.htm(FormFun .zip)从表单中删除标题,并防止它在页面上移动。

这段代码工作的很好,但是我一直遇到一个奇怪的bug,插入的表单“.Top”的值与我在代码中分配的不同。 我也有一个同事运行代码,并得到同样的问题。 我将列出下面的代码的相关部分。

我在模块(Module1)中有以下代码:

Sub CallFormTestA() With UserForm1 .Show vbModal = False .StartUpPosition = 0 .Left = 17 .Top = 147 End With End Sub 

并且在UserForm(UserForm1)中有以下代码:

 Option Explicit 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 FindWindow Lib "user32" Alias "FindWindowA" _ (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _ (ByVal hWnd1 As Long, _ ByVal hWnd2 As Long, _ ByVal lpsz1 As String, _ ByVal lpsz2 As String) As Long Private Declare Function SetParent Lib "user32" _ (ByVal hWndChild As Long, _ ByVal hWndNewParent As Long) As Long Private Const GWL_STYLE As Long = (-16) 'The offset of a window's style Private Const WS_CAPTION As Long = &HC00000 'Style to add a titlebar Private Sub SetBit(ByRef lStyle As Long, ByVal lBit As Long, ByVal bOn As Boolean) If bOn Then lStyle = lStyle Or lBit Else lStyle = lStyle And Not lBit End If End Sub Private Sub Userform_Initialize() Dim MeHWnd, ApphWnd, DeskhWnd, WindowhWnd, Res, lStyle As Long 'Get the window handle of the main Excel application window. ApphWnd = Application.hwnd If ApphWnd > 0 Then 'Get the window handle of the Excel desktop. DeskhWnd = FindWindowEx(ApphWnd, 0&, "XLDESK", vbNullString) If DeskhWnd > 0 Then 'Get the window handle of the ActiveWindow. WindowhWnd = FindWindowEx(DeskhWnd, 0&, "EXCEL7", ActiveWindow.Caption) If WindowhWnd > 0 Then 'OK Else MsgBox "Unable to get the window handle of the ActiveWindow." End If Else MsgBox "Unable to get the window handle of the Excel Desktop." End If Else MsgBox "Unable to get the window handle of the Excel application." End If MeHWnd = FindWindow("ThunderDFrame", Me.Caption) If MeHWnd = 0 Then Exit Sub lStyle = GetWindowLong(MeHWnd, GWL_STYLE) SetBit lStyle, WS_CAPTION, False SetWindowLong MeHWnd, GWL_STYLE, lStyle If (MeHWnd > 0) And (WindowhWnd > 0) Then Res = SetParent(MeHWnd, WindowhWnd) If Res = 0 Then MsgBox "The call to SetParent failed." End If End If End Sub 

正如我所说的,这段代码正确地创build了表单,但是当我在即时窗口运行msgbox userform1.top时,它会返回一个不同的值,在多次尝试中不一致,但通常在250-300的范围内,通常带有一个小数点.25,.5或.75。

我不了解Stephen Bullen和Chip Pearson使用的大部分代码,但是看起来好像不会影响userform1.top对我的价值。 任何人都可以确定是否有问题,我使用的代码将改变userform1.top值? 有没有可能这是一个错误呢?

这是我第一次在这里问一个问题,所以请让我知道,如果有任何额外的信息,我应该包括(或省略)。

谢谢!

Edit1:基于Scott Holtzman的一些反馈,我已经尝试在代码中join一些debug.print行来标识代码中每个点的.top值。 我的调查结果如下,尽pipe斯科特在跑这个时得到了不同的数字。 这全部包含在module1的子CallFormTestA()中。 我还发现,如果我再次运行模块而不重置项目,我会得到不同的结果。 如果我在第二次之后再次运行模块,它保持了第二次得到的结果。

 With UserForm1 Debug.Print .Top 'Returns 139.5 then 286.5 .Show vbModal = False .StartUpPosition = 0 .Left = 17 .Top = 147 Debug.Print .Top 'Returns 286.5 then 286.5 End With With UserForm1 Debug.Print .Top '139.5 then 286.5 .Show vbModal = False .StartUpPosition = 0 .Left = 17 .Top = .Top - .Top 'Changed Debug.Print .Top '139.5 then 139.5 .Top = 147 Debug.Print .Top '286.5 then 286.5 End With With UserForm1 Debug.Print .Top 'Returns 139.5 then 286.5 .Show vbModal = False .StartUpPosition = 0 .Left = 17 .Top = -.Top 'Changed Debug.Print .Top 'Returns -372 then -147 .Top = 147 Debug.Print .Top 'Returns 286.5 then 286.5 End With With UserForm1 Debug.Print .Top '139.5 then 286.5 .Show vbModal = False .StartUpPosition = 0 .Left = 17 .Top = Abs(-.Top) 'Changed Debug.Print .Top '511.5 then 286.5 .Top = 147 Debug.Print .Top '286.5 then 286.5 End With With UserForm1 Debug.Print .Top '286.5 then 286.5 .Show vbModal = False .StartUpPosition = 0 .Left = 17 .Top = 0 'Changed Debug.Print .Top '139.5 then 139.5 .Top = 147 Debug.Print .Top '286.5 then 286.5 End With Dim n As Long 'Tried using an integer to store the .top value With UserForm1 Debug.Print .Top '139.5 then 286.5 .Show vbModal = False .StartUpPosition = 0 .Left = 17 n = .Top 'This drops the decimal, but I don't care about that. Debug.Print .Top & ", " & n '511.5, 512 then 286.5, 286 .Top = .Top - n Debug.Print .Top '138.75 then 140.25 .Top = 147 Debug.Print .Top '286.5 then 286.5 End With 

编辑2:我已经做了一些更多的游戏,特别是隔离代码的某些部分。 我发现如果我从UserForm1代码注释掉下面的行,.Top属性设置正确。

 If (MeHWnd > 0) And (WindowhWnd > 0) Then Res = SetParent(MeHWnd, WindowhWnd) If Res = 0 Then MsgBox "The call to SetParent failed." End If End If 

为了澄清,在这里重复SetParent函数:

 Private Declare Function SetParent Lib "user32" _ (ByVal hWndChild As Long, _ ByVal hWndNewParent As Long) As Long 

我仍然不知道这些线会如何影响form.top属性,但我不知道问题可能在哪里。 我要继续研究这个,但是想要更新这个以防有人在看这个问题。

编辑3:我能够摔跤这个代码,并最终得到它做我想要的,但我仍然不知道为什么。 我发布了我的更新代码作为答案,但如果任何人都可以提供更多的了解发生在这里的事情,我将不胜感激的input。

我仍然不了解这里所发生的一切,但是我想我应该发表这个答案,以防某些未来的人有类似的问题。

所有这一切的关键,正如我在第二次编辑中发现的,是SetParent函数:

 Private Declare Function SetParent Lib "user32" _ (ByVal hWndChild As Long, _ ByVal hWndNewParent As Long) As Long 

这导致Excel添加一个特定的值,比如说XMod,(可以根据用户和硬件的不同而有所不同),也可以是另外一个值,YMod,属于.left属性,我没有看到这个案例。 最终,如果将.top和.left设置为0,则XMod和YMod将使窗体的顶部与列标题的顶部alignment,窗体的左侧与行标题的左边。 我设置的任何数字都会为最终结果添加适当的修饰符。 但这个数字往往会有一个小的变化,以符合屏幕分辨率,这就是为什么我最初认为这是随机的。

但是,这确实引入了另一个问题,因为无论何时设置.top或.left,Excel都会将修饰符添加到.top和.left。 意思是,如果我有以下代码:

 With UserForm1 .Show vbModal = False .StartUpPosition = 0 .Left = 17 .Top = 147 End With 

Excel将设置.top = YMod + 147.left = XMod + XMod + 17 。 在我最初的代码中,XMod是0,所以我没有注意到它添加了两次。 我通过给YMod设置一个variables,然后在稍后设置.Top时减去该variables来解决这个问题,如下所示:

 With Navigation .Top = 0 t = .Top .Show vbModal = False .StartUpPosition = 0 .Top = 13 - t .Left = 19 End With 

这给了我需要的正确结果。 如果其他人有任何问题得到这个工作,我希望这将有助于。 如果我能看到其他人以一种更有意义的方式来回答这个问题,那么我肯定会把他们标记为答案。