📄 感悟visualbasic(6).txt
字号:
感悟VisualBasic(6)
--------------------------------------------------------------------------------
张鸿 时间:2003-11-28 10:06:29
第八话父与子
在开始这一话之前,不知各位读者有没有使用过MDIForm呢?看看图1,这是一个标准的MDIForm和其中一个子窗体在标准和最大化情况下的外观。不过别误会,我不是想讲MDI,你再看看图2,我只是想让你区别图2的窗体不是MDIForm。图2的两个窗体都是一般的窗体,从最大化的外观就可以看出区别了。是不是觉得很有意思?其实也没有什么秘密。
我说过Windows中多数东西都是一种窗口,比如按钮。一般情况下我们看到的按钮都是在一个窗体的里面,这是因为窗体和按钮有一种父与子的关系。当一个窗口成为另一个窗口的子窗口(Child),那么它的位置的变化就只发生在另一个窗口里,另一个窗口就是这个窗口的父窗口(Parent)。平时我们建立的窗体都是相互独立的,与其他的窗体没有关系,但我们可以通过API使它们建立起父与子的关系。这要用到SetParent:
Private Declare FunctionSet Parent Lib"user32"(ByValh WndChild As Long,ByVal hWndNewParent As Long)As Long
SetParent接收两个参数,第一个是将成为子窗口的窗口句柄,第二个是将成为父窗口的窗口句柄。它的使用很简单,比如想把Form2作为Form1的子窗口,只需这样使用:
SetParent Form2.hWnd,Form1.hWnd
Windows会自动把Form2在新的父窗口中的位置调整为原父窗口的位置(即使是桌面,也是一个父窗口)。即是说,假如原来在桌面的Form2,位置为10,10,则它在新的父窗口中的位置也为10,10。但这个新的10,10是以新父窗口为参照物的,无论怎么变化,都是在新父窗口中。
不过应该注意,并不是所有东西都适合当父窗口。因为每一种窗口都有为自己设计的行为,比如当画面重画时要画什么,如果我们为它添加了新的子窗口,那么它们将可能产生冲突,因为父窗口在设计时并没有考虑出现意外的子窗口的情况。为了说明这个问题,我做了一个示例,参照图3。当我把按钮作为ListBox的子窗口时,你会看到由于ListBox在选择项目时进行了画面的重画,导致按钮显示变得不正常,但当我按了一下按钮时,又因为按钮的重画,显示又正常了。
值得一提的是,当我们把Form1中的一个子窗口(比如按钮)放置到Form2中,而我们又在Form1中为这个子窗口的某个事件写了执行代码,那么它还会被执行吗?Form2又需不需要为这个新的子窗口做特别处理呢?假如我的处理代码都是写在Form1中的,而所有控件都被我放到Form2中时(如图4),它们的点击事件的代码仍然能被执行。由于无法得知实际上VB内部是如何处理控件的消息循环的,所以我也无法对此中秘密进行解释,特别是一个应该注意的问题——当你把按钮(这里以按钮为例,但其实其他东西也一样)放到Form2中后,如果这个按钮在Form2中获得了焦点,那么你就无法从Form2切换回Form1,除非这时你可以让Form1中某个控件重新获得焦点——比如通过使某个控件从Form2中成为Form1的子窗口,或者使用SetFocus让Form1的某个控件获得焦点。所以,实际应用中应该避免这种情况的发生。如果新的父窗口不是由VB所建立的窗体,那么这种事就不会发生,不过这已不是本话的内容了。
在我写的示例源程序里,还有一个GetParent的API这里没有讲到,我用它判断当前的子窗口是哪个窗体的子窗口。它的作用是返回指定子窗口的父窗口的句柄。
第九话寻找子窗口
这里又是一个特别的例子,各位读者先看看图5,虽说图像处理我还会两下,不过这可不是处理来的,而是真实的抓图。我把开始按钮移到这里来了。再看看图6,怎么样?有意思吧?
这里我要介绍几个API:
Private Declare Function Find Window Lib"user32"Alias"FindWindowA"(ByVal lpClassName As String,ByVal lpWindowName As String)As Long
Private Declare Function GetWindowLib"user32"(ByVal hwnd As Long,ByVal wCmdA sLong)As Long
Private Declare Function GetClassNameLib"user32"Alias"GetClassNameA"(ByVal hwnd As Long,ByVallp ClassName As String,ByVal nMaxCount As Long)As Long
首先是FindWindow。FindWindow可以根据所给的条件,从桌面上寻找一个窗口,lpClassName是窗口的类名,而lpWindowName是窗口的标题。我们可以传递lpClassName,让它找符合的类名的窗口,或传递lpWindowName,让它找符合的标题的窗口,如果我们不需要两个条件都符合,则另一个参数可以传递vbNullString,让它忽略。它的返回值就是找到的窗口的句柄。那么什么是类名?避开C++的相关术语来说,其实Windows的窗口都是某种类中的一种,这个“类”可以是Textbox、Combobox,也可以是由用户来定义的,这个窗口是属于哪一类的,它的类名就是什么。GetWindow也可以用来寻找某个窗口并返回其句柄,但它只限于在某个窗口中寻找子窗口,因此它需要传递hWnd以表示在哪个窗口里寻找。而wCmd用来描述要找的子窗口与父窗口的关系。它的值如下:
GW_CHILD:寻找第一个子窗口
GW_HWNDFIRST:寻找第一个同级窗口,或寻找第一个顶级窗口
GW_HWNDLAST:寻找最后一个同级窗口,或寻找最后一个顶级窗口
GW_HWNDNEXT:寻找下一个同级窗口
GW_HWNDPREV:寻找前一个同级窗口
GW_OWNER:寻找窗口的所有者(即父窗口)
我们先来理解什么是同级窗口和顶级窗口。打个比方,如果一个窗口有三个子窗口,则这三个窗口都是同一级的,互为同级窗口。如果我们从没寻找过一个子窗口,那么API不知道我们要找的是和哪个窗口同级,那么此时它找的是顶级窗口,顶级窗口即是子窗口,但这个子的关系是直接的,而不会是子窗口的子窗口(即孙子,别笑,这里的术语不是我自己造的)。最后一个GetClassName和以前讲过的几个字符串相关的API用法差不多,hWnd是窗口句柄,lpClassName是用来接收窗口类名的缓冲区,nMaxCount则是说明缓冲区的大小。
那么接下来我是如何用它们的呢?看这里:
Dim hTaskbar As Long,hStartbutton As Long
Dim sClass As String*250
hTaskbar=FindWindow("Shell_traywnd",vbNullString)
hStartbutton=GetWindow(hTaskbar,GW_CHILD)
Do
GetClassName hStartbutton,sClass,250
If LCase(Left$(sClass,6))="button"ThenExitDo
hStartbutton=GetWindow(hStartbutton,GW_HWNDNEXT)
Loop
我使用FindWindow从桌面上找到了一个类名为“Shell_traywnd”的窗口,它就是任务栏(不要问我是怎么知道它的类名的)。然后我又用GetWindow函数,从任务栏找到第一个子窗口。接下来,我用一个Do…Loop结构的循环为上一次找到的子窗口检查其类名,如果类名是button,则说明是个按钮,一般来说,任务栏上只有一个是button类的,所以一找到,它势必就是“开始”按钮了。如果没找到,则仍使用GetWindow,但这次和第一次不同,我传递的不是任务栏的句柄,而是上一次找到的子窗口的句柄,为的是找下一个同级窗口,就这样一次次循环直到找到开始按钮。
那么,开始按钮就被我这么找到了,然后我就可以像对待其他窗口一样对待它:比如将它移动。不要忘了上一期所讲的内容,SetWindowPos将在这里产生作用,你可以移动它,或者为最后一个参数组合上SWP_HIDEWINDOW,让开始按钮变得不可见,或者组合SWP_SHOWWINDOW重新显示……
接下来轮到任务栏了,你从图6中可以看到在开始按钮的位置有另一个“厉害”的按钮取代它,这是上一话的内容:SetParent。我用SetParent为原本在Form1上的按钮指定了新的父窗口——任务栏。如果你查看我的示例源程序,你会发现在此按钮的GotFocus事件中,我把焦点转移给了另一个按钮,原因在上一话已经说了。
在示例源程序中,我还演示了隐藏和显示任务栏,仍然是SetWindowPos的功劳,提醒一下,为了不改变窗口的一些属性,要在最后一个参数组合上合适的值。
好了,这一期的内容就这么多,我想这一次你应该好好研究我的源程序,里面的东西涉及到上一期和本期的内容,把它消化下去吧。源程序下载地址是:http://www.cfan.net.cn/qikan/cxg/0206gwv.zip。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -