⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 感悟visualbasic(5).txt

📁 学习(编程技巧_编程知识_程序代码),是学习编程不可多得的学习精验
💻 TXT
字号:
感悟VisualBasic(5)
 

--------------------------------------------------------------------------------
 
张鸿 时间:2003-11-28 10:05:42 
   
在前面几期的连载中我们学到了一些有用的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两个值。但有时我们不仅是要组合几个值,而且要把一个组里的某个值去除,所以还需要用另一种方法:AndNot(这里的And不是布尔运算的And,而是位运算的And)。比如把1和2进行Or运算后的值中的1去掉,则将其AndNot1。如果想知道是否含有一个值,可以用And,如If64And3 Then……这里只是提供一种方法让你可以使用,如果你想知道它们是如何工作的,我建议你参考位运算的相关书籍。
我说过窗体、按钮等许多东西都是一种窗口,那么这个函数也就理所当然的是针对所有窗口而设计的了,因此可供设置的风格非常多,并且新风格在新操作系统出现时也可能被增加,这里只能给出大部分最常用的,更多的风格请参考MSDN的WindowStyles部分。
WS_BORDER:窗口带有一个薄边框
WS_DLGFRAME:带有一般对话框的风格,但没有标题栏
WS_CAPTION:窗口带有一个标题栏,经测试,实际上等于(WS_BORDEROrWS_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:窗口带有一个垂直滚动条
好了,说了这么多,下面该动手了。看图2,这是在VB里BorderStyle设置为NONE的窗体,我在上面加了8个CheckBox,分别测试这些CheckBox上面所示的风格,当CheckBox按下时,表示具有该风格,弹起时表示不具有该风格。
实现效果看图3。
我把该示例所需的常量声明列在下面:
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,ByValnI ndex 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
好了,到这里已为你讲述了安全地为窗体更改风格的方法,你可以把你想要的风格(比如前面所列出的)应用于你的窗体。但是,它还是不够完美,当你改了风格之后,你会发现——虽然风格实际上已经改了,但外表完全没变,就好像窗体忘了刷新一样。
让它刷新?或许你会这么认为,不过这个可怜的窗体,无论你用什么方法去刷新,它都无动于衷……很长一段时间以来我都使用了一个折衷的方法——改变窗体的大小,再改回去。当窗体大小被改变之后,它就会刷新一下,这样就没事了。但是这种方法显得笨了一点,你也许希望就如发送消息一样方便地让它正常刷新,不过就如前面所说,它不领你的情。
但是这种情况也并非无法解决,下一话,我将告诉你一个更好的办法。
第七话位置与常居顶端
许多软件,特别是占桌面面积不是很大的软件(比如笔者的NaviEdit),通常都提供了一个常居顶端的功能(可能有的软件不是这么叫法,但作用是相同的),它的作用是保持窗口一直在其他窗口的上面,可以省去频繁切换窗口的动作。
如果你想这么做,有一个API可以实现:SetWindowPos,声明是这样的:
Private Declare Function SetWindowPos Lib"user32"Alias"SetWindowPos"(ByVal hwnd As Long,ByVal hWndInsertAfter As Long,ByValxAsLong,ByValy As Long,ByValcx As Long,ByValcy As Long,ByVal wFlags As Long)As Long
虽然参数很多,但实际用起来很简单。hwnd是窗口的句柄,x、y、cx、cy分别是窗口的x和y坐标、宽和高度。hWndInsertAfter用来指定窗口的Z位置(或称Z顺序)。如果你经常接触3D方面的软件,你就知道Z代表深度。这个参数接受5种值:HWND_BOTTOM、HWND_NOTOPMOST、HWND_TOP、HWND_TOPMOST或者另一个窗口的句柄。而wFlags用来指定附加的选项。
你可以用它改变窗口的位置和大小,而且它允许你同时改变Z位置(当然,在VB中不用API你也可以改变窗体大小和位置)。比如让窗口退到最下面,可以这么使用:
SetWindowPosMe.hWnd,HWND_BOTTOM,10&,10&,80&,120&,0&
想要常居顶端,只需把HWND_BOTTOM改为HWND_TOPMOST,而HWND_NOTOPMOST则是取消
常居顶端,HWND_TOP是把窗口的Z位置改为最前。如果这个参数传递的是另一个窗口的句柄,则是把该窗口的Z位置更改为在另一个窗口的下面。
非常简单的事情。不过如果像上面一样做,是不是单单改个Z位置也要计算窗口位置和大小?最后一个参数又是干什么用的呢?wFlags可以让SetWindowPos忽略或执行某种行为。这里给出一部分:
SWP_DRAWFRAME和SWP_FRAMECHANGED:强制发送WM_NCCALCSIZE消息给窗口
SWP_HIDEWINDOW:隐藏窗口
SWP_NOACTIVATE:不激活窗口
SWP_NOMOVE:保持当前位置(忽略x和y)
SWP_NOREDRAW:窗口不自动重画
SWP_NOSIZE:保持当前大小(忽略cx和cy)
SWP_NOZORDER:保持窗口在列表的当前位置(忽略hWndInsertAfter)
SWP_SHOWWINDOW:显示窗口
这些参数可以使用Or运算组合,所以如果你不希望改变窗口位置和大小,你只需要给最后一个参数传递(SWP_NOMOVEOrSWP_NOSIZE)即可。如下:
SetWindowPos Me.hWnd,HWND_TOPMOST,0&,0&,0&,0&,SWP_NOMOVEOrSWP_NOSIZE
这里的x、y、cx、cy的值将被忽略。其他值的组合,你可以自己去试试。
好了,这个看起来好像有点复杂的API已经变得很清晰,那么轮到上一话的收尾。
WM_NCCALCSIZE消息是在计算窗口的客户区大小时被发送的,它主要是让程序可以收到该消息后重新计算客户区的大小。我们先不管它是不是也能像许多以WM_开头的消息一样由我们发送给程序让它产生作用,但它使用起来的复杂程度让我宁可选择改变窗体大小再改回去。当我们改变窗口的大小时,很明显的就是它一定会重新计算客户区大小以调整外观。既然这个函数可以强制发送WM_NCCALCSIZE消息,那么我们就应该试一试。
SetWindowPos Me.hwnd,0&,0&,0&,0&,0&,SWP_NOSIZEOrSWP_NOZORDEROrSWP_NOMOVE Or SWP_FRAMECHANGED
为了不改变窗口大小、位置和Z顺序(就是要窗口保持原状),我使用SWP_NOSIZE Or SWP_NOZORDER Or SWP_NOMOVE,让它忽略前面所有的参数,最后加个Or SWP_FRAMECHANGED就是让它重新计算客户区大小。把上面的一行放到前一话中更改风格的那一句下面,再执行程序,成功了!它已经能够正常刷新!就是这样,问题马上变得这么简单。还有什么疑问吗?没有,好,那么我们下一期再见。 
 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -