VB 版 (精华区)

发信人: bloom (├┝┞┟┠┢┣), 信区: VB
标  题: VB API教程(王国荣版)(九)(转载)
发信站: 哈工大紫丁香 (2000年09月07日18:42:51 星期四), 转信

【 以下文字转载自 cnTemp 讨论区 】
【 原文由 bloom 所发表 】
发信人: Love1976 (狄飞惊), 信区: VisualBasic       
发信站: BBS 水木清华站 (Thu Apr  6 04:22:48 2000)

发信人: coolknight (酷骑士~找工作中), 信区: VB
标  题: VB 與 Windows API 講座(九)
发信站: 武汉白云黄鹤站 (Tue Nov  9 20:15:43 1999), 站内信件

VB 與 Windows API 講座(九)
命令鈕及功能表之特異功能集
 
http://www.kj.com.tw
王國榮
 
命令鈕及功能表的使用,在 VB 程式設計中算是最初階的問題,相信難不倒讀者們
,不
過 VB 所提供的命令鈕及功能表都十分制式化,因此本期筆者將藉助 API 讓命令
鈕及功
能表多一點變化。
 
------------------------------------------------------------------------
----
----
製作多媒體命令鈕
------------------------------------------------------------------------
----
----
 
在 VB 4.0 版以前,讀者常問的問題是「如何製作圖形命令鈕?」,到了 5.0 版
以後,
我們只要將命令鈕的 Picture 屬性設定成某一圖片,則命令鈕即自動變成圖形命
令鈕,
現在常問的問題則是「如何製作多媒體命令鈕?」
現在常問的問題則是「如何製作多媒體命令鈕?」
 
所謂多媒體命令鈕可能包含以下功能:(1) 按下命令鈕時,發出聲音 (2) 滑鼠移
到命令
鈕上面時,發出聲音 (3) 滑鼠移到命令鈕上面時,將命令鈕變得更醒目 (4) 不規
則形
狀的命令鈕…等。
 
按下命令鈕時,發出聲音
 
這個問題最簡單,只要在命令鈕的Click事件程序中呼叫播放聲音的
sndPlaySound API
函數,即可達到目的,假設我們想在按下 Command1 命令鈕時播放 c:
\windows\Ding.w
av 聲音檔,則所撰寫的程式如下:(完整範例請參閱 Sound.vbp 專案)(註:本文
所介紹
之範例請至網站下載)
 
Private Sub Command1_Click()
sndPlaySound "c:\windows\Ding.wav", SND_ASYNC
End Sub
 
滑鼠移到命令鈕上面時,發出聲音
 
此一問題的初步構想是在命令鈕的 MouseMove 事件程序中呼叫 sndPlaySound 播
放聲音
,不過這個想法並不理想,因為我們只希望在滑鼠移到命令鈕上面時播放聲音,而
如果
在 MouseMove 事件程序中播放聲音,則每當滑鼠在命令鈕上面動一下,就會播音
一次,
結果將十分惱人。但問題是 VB 沒有提供MouseIn 及 MouseOut之類的事件,以致
我們無
結果將十分惱人。但問題是 VB 沒有提供MouseIn 及 MouseOut之類的事件,以致
我們無
法判斷滑鼠是剛剛移到命令鈕上面,或只是在命令鈕上面移動。
 
要區分滑鼠是移到命令鈕上面或只是在命令鈕上面移動,必須知道滑鼠是否曾經移
出命
令鈕,而為了監視滑鼠是否移出命令鈕,最常使用的技巧的在第一次的 MouseMove
 事件
程序中呼叫 SetCapture API 函數,呼叫例如下:
 
SetCapture Command1.hWnd
 
以上敘述的作用是把所有的滑鼠事件(訊息)都設定給 Command1 命令鈕,接著不管
滑鼠
移到什麼位置(即使移到命令鈕之外),Command1 命令鈕都會收到 MouseMove 事件
,因
此我們可以利用以下判斷式判斷滑鼠是否移出了命令鈕:
 
If X < 0 Or Y < 0 Or X > Command1.Width Or Y > Command1.Height Then
 
當然,在滑鼠移出命令鈕時,我們應該讓滑鼠恢復正常,如此其他視窗及控制元件
才能
夠繼續獲得滑鼠事件,此時應呼叫 ReleaseCapture API 函數,呼叫例如下:
 
ReleaseCapture
 
瞭解 SetCapture/ReleaseCapture 的功能之後,我們還需要一個旗標來記錄滑鼠
是否位
於命令鈕上面,筆者是採用 Tag 屬性來記錄,而為了達到「滑鼠移到命令鈕上面
時,發
出聲音」的目的,最後完成的程式如下:(完整範例請參閱 Capture.vbp 專案)
出聲音」的目的,最後完成的程式如下:(完整範例請參閱 Capture.vbp 專案)
 
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As
 Sin
gle, Y As Single)
If Command1.Tag = "In" Then ' 滑鼠原先是否位於命令鈕上面
If X < 0 Or Y < 0 Or X > Command1.Width Or Y > Command1.Height Then
' 滑鼠移出命令鈕
Command1.Tag = "Out"
ReleaseCapture
End If
Else ' 滑鼠原先不在命令鈕上面,且移到了命令鈕上面
SetCapture Command1.hwnd
Command1.Tag = "In"
sndPlaySound "c:\windows\Ding.wav", SND_ASYNC
End If
End Sub
 
Private Sub Command1_Click()
If Command1.Tag = "In" Then SetCapture Command1.hwnd
End Sub
 
Private Sub Command1_MouseUp(Button As Integer, Shift As Integer, X As 
Singl
e, Y As Single)
e, Y As Single)
If Command1.Tag = "In" Then SetCapture Command1.hwnd
End Sub
 
以上程式比較值得注意的地方是,當命令鈕被按下又放開時(也就是產生 Click 及
 Mou
seUp 事件時),命令鈕本身也會呼叫 ReleaseCapture,此時我們應該重新呼叫 
SetCap
ture 以監視滑鼠事件,程式如以上的 Command1_Click 及 Command1_MouseUp 事
件程序

 
滑鼠移到命令鈕上面時,將命令鈕變得更醒目
 
此一問題與上一個問題的解決方案大同小異,同樣要藉助 SetCapture 及 
ReleaseCapt
ure 函數,所不同的是如何將命令鈕變得更醒目,筆者先撰寫一個比較簡單的範例
 --
當滑鼠移到命令鈕上面時,將字型放大加粗:(完整範例請參考 enlarge.vbp 專案
)
 
圖-1 enlarge.vbp 專案的功能
 
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As
 Sin
gle, Y As Single)
If Command1.Tag = "In" Then
If X < 0 Or Y < 0 Or X > Command1.Width Or Y > Command1.Height Then
Command1.Tag = "Out"
ReleaseCapture
ReleaseCapture
Command1.Font.Size = 9
Command1.Font.Bold = False
End If
Else
Command1.Tag = "In"
SetCapture Command1.hwnd
Command1.Font.Size = 12
Command1.Font.Bold = True
End If
End Sub
 
以上程式除了利用 Command1.Font 來設定字型之外,與上一個程式完全相同。
 
純文字命令鈕比較單調,假設我們想把以上範例改成圖形命令鈕,則首先要準備兩
張圖
片(普通圖片及醒目圖片各一),平常顯示普通圖片,而當滑鼠移到命令鈕上面時,
改顯
示醒目圖片,參考筆者所提供的 gcommand.vbp 就是這樣的程式,其中在 
Command1_Mo
useMove 事件程序中顯示普通圖片及醒目圖片的程式分別如下:(註:此一程式在
設計階
段應將 Command1 的 Style 屬性設定成 "1-圖片外觀",否則 Picture 屬性在執
行階段
將無法設定)
 
Set Command1.Picture = pic1 ' 顯示普通圖片
Set Command1.Picture = pic2 ' 顯示醒目圖片
Set Command1.Picture = pic2 ' 顯示醒目圖片
 
而在 Form_Load 事件程序中則必須預先載入圖片至 pic1 及 pic2 物件變數中,
程式如
下:
 
Dim pic1 As Picture, pic2 As Picture
 
Private Sub Form_Load()
Set pic1 = LoadPicture(App.Path & "\ball.bmp") ' 普通圖片
Set pic2 = LoadPicture(App.Path & "\ball2.bmp") ' 醒目圖片
Set Command1.Picture = pic1
End Sub
 
圖-2 gcommand.vbp 專案的功能
 
gcommand.vbp 專案還是使用 VB 內建的命令鈕來製作圖形命令鈕,但實際上圖形
命令鈕
的製作並不一定要使用 VB 內建的命令鈕,若使用 PictureBox 來製作,反而更容
易與
背景圖片結合在一起,而達到多媒體命令鈕的效果。
 
不規則形狀的命令鈕
 
不規則形狀命令鈕的製作,通常是採用 PictureBox 或直接使用 Form 來顯示命令
鈕,
然後再利用 PtInRegion API 函數來檢驗滑鼠按下時的游標是否位於某一區域內,
筆者
然後再利用 PtInRegion API 函數來檢驗滑鼠按下時的游標是否位於某一區域內,
筆者
所提供的 Region.vbp 就是這樣的程式。本段落筆者將以 Region.vbp 為例,來說
明不
規則形狀命令鈕的製作方法。
 
圖-3 Region.vbp 專案
 
首先是 PtInRegion API 函數的用法,此一函數含有三個參數,參數一須傳入 
hRegion
(handle of Region,區域代碼)、參數二、三傳入位置 (X, Y),如果 (X, Y) 位
於 hR
egion 所定義的區域之內,則 PtInRegion 傳回 True,否則傳回 False,呼叫例
如下:

 
If PtInRegion(hRegion, X, Y) Then

End If
 
但呼叫 PtInRegion 之前,必須先建立好「區域」(Region),並且取得 hRegion,
此時
可以呼叫以下三種 API 函數:
 
CreateRectRgn 建立方形區域,並傳回 hRegion
CreateEllipticRgn 建立圓形(橢圓形)區域,並傳回 hRegion
CreatePolygonRgn 建立多邊形區域,並傳回 hRegion
 
其中建立方形區域須傳入方形對角的兩點,例如:
其中建立方形區域須傳入方形對角的兩點,例如:
 
hRegion(0) = CreateRectRgn(20, 22, 100, 72)
 
圖-4 呼叫 MakeRectRgn 所需傳入之參數
 
建立圓形區域須傳入「圍住圓形之最小方形的兩個對角」,例如:
 
hRegion(1) = CreateEllipticRgn(174, 15, 241, 81)
 
圖-5 呼叫 CreateEllipticRgn 所需傳入之參數
 
至於建立多邊形區域,則是先將構成多邊形的點存放在 POINTAPI 的陣列中,然後
再傳
入 CreatePolygonRgn 函數,例如:
 
Dim p(9) As POINTAPI ' 宣告 10 個點的陣列
' 一一指定好 10 個點
p(0).X = 135: p(0).Y = 66
p(1).X = 121: p(1).Y = 107
p(2).X = 80: p(2).Y = 107
p(3).X = 111: p(3).Y = 132
p(4).X = 97: p(4).Y = 175
p(5).X = 135: p(5).Y = 149
p(5).X = 135: p(5).Y = 149
p(6).X = 172: p(6).Y = 175
p(7).X = 158: p(7).Y = 132
p(8).X = 190: p(8).Y = 107
p(9).X = 149: p(9).Y = 107
' 參數一傳入陣列,參數二傳入點數,參數三指定成 ALTERNATE 常數
hRegion(2) = CreatePolygonRgn(p(0), 10, ALTERNATE)
 
圖-6 呼叫 CreatePolygonRgn 之前,應先決定好多邊形的每一個點
 
當然,區域的建立與我們顯示在 Form(或 PictureBox)上面的圖形有關,在筆者所
提供
的 Region.vbp 範例中,是把含有方形、圓形、及多邊形的圖片顯示在 Form 上面
,然
後分別建立這三種圖形的區域,並且傳回 hRegion(0)、hRegion(1)、
hRegion(2) 三個
區域代碼。
 
完成區域的建立之後,接著只要在 Form_MouseUp 事件程序中判斷滑鼠游標是否位
於某
一區域內,即可完成不規則形狀命令鈕的製作,程式如下:
 
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As 
Single, Y
 As Single)
If PtInRegion(hRegion(0), X, Y) Then
MsgBox "您按下的是方形鈕 ▅!"
ElseIf PtInRegion(hRegion(1), X, Y) Then
ElseIf PtInRegion(hRegion(1), X, Y) Then
MsgBox "您按下的是圓形鈕 ●!"
ElseIf PtInRegion(hRegion(2), X, Y) Then
MsgBox "您按下的是多邊形鈕 ★!"
End If
End Sub
 
------------------------------------------------------------------------
----
----
常見的功能表類型
------------------------------------------------------------------------
----
----
 
目前常見的功能表大致有幾種類型:
 
◆ 浮動式功能表:當滑鼠移到功能表上面時,文字或圖形周圍會顯示出立體外框
,移開
時,立體外框又會消失,典型代表有 IE4 及 Office97。此類功能表並非 Windows
 內建
的功能表,而是程式自己監視滑鼠的移動並且模擬功能表所製作出來,目前 VB 沒
有提
供這類功能表,如果您想自己製作,所需使用的 API 函數仍不脫本文所介紹的 
SetCap
ture 及 ReleaseCapture。
 
◆ 圖形工具列:也就是把最為常用的指令匯集在圖形工具列中,VB5 提供的 
ToolBar
控制元件就屬於此類功能表。
控制元件就屬於此類功能表。
 
◆ Windows 內建的功能表:一些老掉牙的應用程式,例如 NotePad(記事本),都
是使用
此類功能表,VB 提供的功能表也屬於此類功能表。
 
雖然 Windows 內建的功能表不如浮動式功能表來得炫,但仍有一項功能值得一提
,那就
是圖形功能表,請先執行筆者提供的 gmenu.vbp 專案以瞭解何謂圖形功能表,啟
動 gm
enu.vbp 專案之後,所看到的功能表是純文字的,接著請按下功能表的「設定成圖
形功
能表」即可將功能表設定成圖形的,如圖-7。
 

圖-7 gmenu.vbp 專案
 
製作圖形功能表
 
VB 內建的功能表來自 Windows,但為了讓功能表的設計更簡單,VB 對功能表進行
了物
件封裝的工作,以致 VB 的功能表與 Windows 原始的功能表在觀念上存在著若干
差異,
對 VB 而言,每一個功能表命令或子功能表都算是一個功能表物件,但是對 
Windows 而
言,會以一個 hMenu(handle of Menu) 代表整個功能表,而每一個子功能表則以
一個
hSubMenu(handle of SubMenu) 來代表,但每一個功能表命令則不再給予 handle
,而是
以 Menu ID 代表,有關 hMenu、hSubMenu、與 Menu ID 的關係請參閱圖-8。

 
圖-8 hMenu、hSubMenu、及 Menu ID 的關係
 
 
取得 Menu Handle 與 Menu ID
 
為了取得某一個 Form 的 hMenu,我們必須呼叫 GetMenu API 函數,呼叫例如下

 
hMenu = GetMenu(Me.hwnd)
 
至於取得 hSubMenu,則是呼叫 GetSubMenu API 函數,呼叫例如下:
 
hSubMenu = GetSubMenu(hMenu, 0)
 
GetSubMenu 參數二是子功能表的順序,以圖-8 的功能表為例,「檔案、編輯、搜
尋、
說明」等子功能表的順序依序是 0、1、2、3,因此 GetSubMenu(hMenu, 0) 所得
到的是
「檔案」子功能表的 hSubMenu。
 
取得 Menu ID 所呼叫的 API 函數是 GetMenuItemID,呼叫例如下:
 
menuid = GetMenuItemID(hSubMenu, 5)
 
由於 Menu ID 乃附屬於 hSubMenu,所以參數一須傳入 hSubMenu,至於參數二則
是傳入
功能表命令的順序,但請注意分隔線也算一個功能表命令,以圖-8 的功能表為例
,「開
新檔案、開舊檔案、儲存檔案、另存新檔、分隔線、版面設定、列印、分隔線、結
束」
等功能表命令的順序依序是 0、1、2、3、4、5、6、7、8,假設 hSubMenu 代表的
是圖
等功能表命令的順序依序是 0、1、2、3、4、5、6、7、8,假設 hSubMenu 代表的
是圖
-8的「檔案」子功能表,則 GetMenuItemID(hSubMenu, 5) 所取得的是「版面設定
」功
能表命令的 Menu ID。
 
ModifyMenu 函數:設定功能表的標題或圖形
 
取得 hMenu、hSubMenu、及 Menu ID 之後,我們可以呼叫 ModifyMenu API 函數
改變某
一個功能表命令或子功能表的「標題、圖形、或 Menu ID」。ModifyMenu 幾個參
數的意
義如下:
 
◇ hMenu:傳入 hMenu 或 hSubMenu。
◇ nPosition:子功能表或功能表命令的順序。
◇ wFlags:如何修改功能表的旗標,如果想將功能表設定成圖形,應傳入「
MF_BITMAP
 Or MF_BYPOSITION」,如果想將功能表設定成文字標題,則應傳入「MF_STRING 
Or MF
_BYPOSITION」。
◇ wIDNewItem:修改後的 Menu ID,若不修改,則傳入原 Menu ID。
◇ lpString:文字標題或點陣圖的 handle(hBitmap)。
 
例如以下程式可將第 0 個子功能表的第 0 個命令設定成圖形的:
 
hMenu = GetMenu(Me.hwnd)
hSubMenu = GetSubMenu(hMenu, 0)
menuid = GetMenuItemID(hSubMenu, 0)
menuid = GetMenuItemID(hSubMenu, 0)
hBitmap = PictureToBitmap(picCircle)
ModifyMenu hSubMenu, 0, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
 
在以上程式中,最麻煩的事情是 hBitmap 的取得,由於 VB 只提供 Picture 及 
Image
 兩種與圖片有關的屬性,並沒有提供所謂的 Bitmap(點陣圖)屬性,為此筆者在 
gmenu
.vbp 專案中撰寫了一個可將 PictureBox 元件中的圖片轉換成 Bitmap的副程式 
-- Pi
ctureToBitmap,此一副程式須傳入 PictureBox 元件,而傳回的是 hBitmap,呼
叫例如
下:
 
hBitmap = PictureToBitmap(picCircle)
 
結果可將 picCircle 圖片盒之中的圖片轉換成 Bitmap,然後傳回 hBitmap,接著
若再
呼叫 ModifyMenu 函數,即可將 picCircle 圖片盒的圖片設定給功能表。
 
gmenu.vbp 專案的實作過程
 
以上所介紹的 Menu API 函數其實只佔 Menu API 函數的一小部分,但是對圖形功
能表
的製作而言已經綽綽有餘,在實作上,我們會先利用 VB 所提供的功能表編輯器先
編輯
好純文字功能表,接著再利用 Menu API 將某一(些)功能表命令或子功能表改變成
圖形
的,以 gmenu.vbp 專案為例,一開始在 VB 環境底下所編輯出來的功能表結構如
下:
 
功能表標題 子功能表或功能表命令的順序
功能表標題 子功能表或功能表命令的順序
繪圖 0
....圓形 ....0
....橢圓形 ....1
....方形 ....2
顏色 1
....紅色 ....0
....綠色 ....1
....藍色 ....2
設定成圖形功能表 2
 
接著為了設定圖片給「圓形、橢圓形、方形」及「紅色、綠色、藍色」等命令,筆
者在
 Form 上面分別佈置了這幾個命令所需之 PictureBox 元件,名稱分別設定為 
picCirc
le、picEllipse、picRect、picRed、picGreen、picBlue,最後再利用以下程式將
這些
圖片設定給功能表:
 
Dim hMenu As Long, hSubMenu As Long, menuid As Long, hBitmap As Long
hMenu = GetMenu(Me.hwnd)
hSubMenu = GetSubMenu(hMenu, 0)
 
hBitmap = PictureToBitmap(picCircle)
menuid = GetMenuItemID(hSubMenu, 0)
ModifyMenu hSubMenu, 0, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
ModifyMenu hSubMenu, 0, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
 
hBitmap = PictureToBitmap(picEllipse)
menuid = GetMenuItemID(hSubMenu, 1)
ModifyMenu hSubMenu, 1, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
 
hBitmap = PictureToBitmap(picRect)
menuid = GetMenuItemID(hSubMenu, 2)
ModifyMenu hSubMenu, 2, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
 
hSubMenu = GetSubMenu(hMenu, 1)
 
hBitmap = PictureToBitmap(picRed)
menuid = GetMenuItemID(hSubMenu, 0)
ModifyMenu hSubMenu, 0, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
 
hBitmap = PictureToBitmap(picGreen)
menuid = GetMenuItemID(hSubMenu, 1)
ModifyMenu hSubMenu, 1, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
 
hBitmap = PictureToBitmap(picBlue)
menuid = GetMenuItemID(hSubMenu, 2)
ModifyMenu hSubMenu, 2, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
ModifyMenu hSubMenu, 2, MF_BITMAP Or MF_BYPOSITION, menuid, hBitmap
 
------------------------------------------------------------------------
----
----
進一步的美化及改良
------------------------------------------------------------------------
----
----
 
本期所提供的範例旨在說明多媒體命令鈕及圖形功能表的製作原理,因此並沒有進
行美
化的工作,以 Region.vbp(不規則形狀的命令鈕)為例,您可以再美化的地方大致
有二:
(1) 當滑鼠移到命令鈕上面時,在命令鈕周圍顯示光影 (2) 在命令鈕上面按下滑
鼠時,
將命令鈕顯示成凹陷或平面狀,放開滑鼠時,再將命令鈕恢復成立體狀。
 
此外,gmenu.vbp(圖形功能表)亦有值得改良的地方,gmenu.vbp 的缺點是 Form 
上面佈

置了 6 個 PictureBox 元件,而 PictureBox 是所有控制元件中最浪費系統資源
的控制
元件,為了節省系統資源,可以改成只佈置一個 PictureBox,而當我們想設定某
一圖片
給功能表時,再從圖檔或資源檔中載入該圖片到此一 PictureBox,然後設定給功
能表,
如此便只要使用一個 PictureBox 元件。
------------------------------------------------------------------------
----
----
end
--
" The Matrix is everywhere, it's all around us, here even in this room.
 You
 
 can see it out your window, or  on your television. You feel it when 
you
 go to work, or go to church or pay your taxes. It is the world that has
 been
 pulled over your eyes to blind you from the truth... Unfortunately, 
no one
can be told what the Matrix is. You have to see it for yourself."
                                                                   
Morphe

※ 来源:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: 202.114.3.124]

--
我并不是在等待奇迹,因为我知道没有奇迹的。
有的,也只是爱情、意志和勇气。
是这些东西的重叠后,而成为奇迹的。
所以,我从未曾想过放弃。

--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: blo0m.bbs@smth.org]
--
※ 转载:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: 202.118.247.254]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:218.878毫秒