扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
感悟VB细水长流话API(五-1)
在前面几期的连载中我们学到了一些有用的API和相关知识,这段时间里有一些读者来信问了一些问题,我发现这些问题大部分都出在很小的错误上,比如变量的声明、API声明的函数名写错等,其实这些都是可以避免的。在一开始我就说明了让VB进行显式声明的重要性,还有API的声明是可以从API浏览器中直接复制的,不要自己去键入,不然容易出现难以发现的错误。
第六话 窗体和风格
在Windows中大部分东西都是一个窗口,窗体、菜单、工具栏、状态栏、按钮、文本框……不要觉得奇怪,它们都是窗口——Window(是否从一个侧面说明了这个操作系统为何叫Windows,加了复数的Window)。
从VB的IDE中你可以更改一个窗体的外观,图1是 IDE中各种外框风格的窗体。
你可以看到它们有的有边框,有的没有;有的有标题栏,有的没有;有的有最大最小化按钮,有的没有。这些窗体的边框风格都是在窗体被创建时就定下来的。我们在建立VB程序的窗体时,不需要自己写创建窗体的代码,省去了许多重复的工作,但我们也因此失去了解其中秘密的机会。许多情况下窗体风格是在运行时就一直不变的,但有时我们要求在运行时改变,然而,类似BorderStyle等许多设置外观的属性只能在设计时才有效,在这种情况下,我们的这项工作就无法完成。所幸的是,实际上窗体的风格是能够在运行时被改变的,用SetWindowLong,我们就能解决这个问题。
以前我写过子类的文章,用的也是SetWindowLong,但这次我们不是要用子类,它比子类简单得多。下面给出SetWindowLong的声明:
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
要改变窗体的风格,我们需要用一个常量来使 SetWindowLong知道我们对窗体进行风格设置:GWL_STYLE。
从API浏览器得到GWL_STYLE的值后,调用时,它是作为第二个参数传递出去的。那么第三个参数呢?这里显得有点复杂,因为它不是一个单一的参数,而是一组参数的组合。
就如上面我所说的,一个窗体可能有边框,可能有最大最小化按钮,可能有标题栏,但也有可能一部分或全部都没有,如果我们在这里只用一个参数为其设置风格,那么这么多风格就需要一种特殊方法,使该API能够知道我们包含了哪些风格在里面。这就是Or运算。Or运算是把两个数值进行或运算,而微软为了可以方便分离进行Or运算的值,对这些值都精心设计过,因此我们可以放心地将它们组合。如,把 1 和 2 进行Or运算,然后传递给函数,函数会自己分离出 1 和 2,就知道我们传递了 1 和 2 两个值。但有时我们不仅是要组合几个值,而且要把一个组里的某个值去除,所以还需要用另一种方法: And Not(这里的And 不是布尔运算的And,而是位运算的And)。比如把 1 和 2 进行 Or 运算后的值中的 1 去掉,则将其 And Not 1。如果想知道是否含有一个值,可以用And,如 If 64 And 3 Then ……这里只是提供一种方法让你可以使用,如果你想知道它们是如何工作的,我建议你参考位运算的相关书籍。
我说过窗体、按钮等许多东西都是一种窗口,那么这个函数也就理所当然的是针对所有窗口而设计的了,因此可供设置的风格非常多,并且新风格在新操作系统出现时也可能被增加,这里只能给出大部分最常用的,更多的风格请参考 MSDN的Window Styles部分。
WS_BORDER:窗口带有一个薄边框
WS_DLGFRAME:带有一般对话框的风格,但没有标题栏
WS_CAPTION:窗口带有一个标题栏,经测试,实际上等于 (WS_BORDER Or WS_DLGFRAME)
WS_SIZEBOX 和 WS_THICKFRAME:窗口带有一个可以调整窗口大小的边框(即VB里的Sizable,其他地方的边框均指不具调整大小功能的边框)
WS_HSCROLL:窗口带有一个水平滚动条
WS_MAXIMIZEBOX:窗口带有最大化按钮,该窗口必须具有 WS_CAPTION 风格
WS_MINIMIZEBOX:窗口带有最小化按钮,该窗口必须具有 WS_CAPTION 风格
WS_SYSMENU:在窗口的标题栏上增加一个系统菜单,该窗口必须具有 WS_CAPTION 风格(即WS_BORDER和WS_DLGFRAME)
WS_OVERLAPPED 和 WS_TILED:窗口是一个交迭式窗口。交迭式窗口带有一个标题栏和一个边框
WS_OVERLAPPEDWINDOW 和 WS_TILEDWINDOW:窗口是一个交迭式窗口,并且组合了 WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU,
WS_THICKFRAME, WS_MINIMIZEBOX 以及 WS_MAXIMIZEBOX 这些风格
WS_VSCROLL:窗口带有一个垂直滚动条
好了,说了这么多,下面该动手了。在VB 里BorderStyle设置为NONE的窗体,我在上面加了8个CheckBox ,分别测试这些CheckBox上面所示的风格,当CheckBox按下时,表示具有该风格,弹起时表示不具有该风格。
我把该示例所需的常量声明列在下面:
Private Const GWL_STYLE = (-16)
Private Const WS_BORDER = &H800000
Private Const WS_CAPTION = &HC00000 '' WS_BORDER Or WS_DLGFRAME
Private Const WS_DLGFRAME = &H400000
Private Const WS_SIZEBOX = &H40000
Private Const WS_MAXIMIZEBOX = &H10000
Private Const WS_MINIMIZEBOX = &H20000
Private Const WS_SYSMENU = &H80000
Private Const WS_HSCROLL = &H100000
Private Const WS_VSCROLL = &H200000
如果你要让窗体具有WS_SIZEBOX风格,可以这样写:
SetWindowLong Me.hwnd, GWL_STYLE, WS_SIZEBOX
但是这里仍有问题。这相当于只给窗体WS_SIZEBOX 风格,如果要其他风格我们就得一起加上,但如果我们想在保留窗体原有风格的基础上增加一个风格,还需要另一个API:
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
GetWindowLong的调用方法和SetWindowLong相似,只不过不需要第三个参数,因为这里的返回值是得到它的风格的组合。你可以先这样做:
Dim lStyle As Long
lStyle = GetWindowLong(Me.hwnd, GWL_STYLE)
然后你就可以放心地使用了。
SetWindowLong Me.hwnd, GWL_STYLE, lStyle Or WS_SIZEBOX
为窗体增加一个WS_SIZEBOX风格而无需担心其他风格会丢失。如果想去掉WS_SIZEBOX,则使用:
SetWindowLong Me.hwnd, GWL_STYLE, lStyle And Not WS_SIZEBOX
好了,到这里已为你讲述了安全地为窗体更改风格的方法,你可以把你想要的风格(比如前面所列出的)应用于你的窗体。但是,它还是不够完美,当你改了风格之后,你会发现——虽然风格实际上已经改了,但外表完全没变,就好像窗体忘了刷新一样。
让它刷新?或许你会这么认为,不过这个可怜的窗体,无论你用什么方法去刷新,它都无动于衷……很长一段时间以来我都使用了一个折衷的方法——改变窗体的大小,再改回去。当窗体大小被改变之后,它就会刷新一下,这样就没事了。但是这种方法显得笨了一点,你也许希望就如发送消息一样方便地让它正常刷新,不过就如前面所说,它不领你的情。
但是这种情况也并非无法解决,下一话,我将告诉你一个更好的办法。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者