表單縮小到右下角
來源:cww 更改王國榮 Run PC 49期的文章
基本上, 表單縮小後只能在「開始」功能表右邊的工作列, 要縮到右下角要花功夫;
右下角的圖示都不是表單或程式, 對 Windows 來說, 它只是一個圖示, 而想建立此
一圖示, 方法是呼叫Shell_NotifyIconA API 函數
Dim nid As NOTIFYICONDATA
Call Shell_NotifyIconA(NIM_ADD, nid)
呼叫 Shell_NotifyIconA 之前, 必須間填好 NOTIFYICONDATA 資料結構(如以上的
nid 變數)的內容, 而 NOTIFYICONDATA 各資料成員的意義如下:
1. cbSize:需填入 NOTIFYICONDATA 資料結構的長度。固定Len(nid)
2. hWnd:handle of window, 指被縮小進來的Window。
3. uID:使用者為圖示所設定的 ID。可自訂
4. uFlags:用來設定以下三個參數(uCallbackMessage、hIcon、szTip)是否
有效, 通常設定成 (NIF_MESSAGE + NIF_ICON + NIF_TIP) 表示全部有
效。NIF_MESSAGE :表示在Icon上面按Mouse時,會將訊息傳給hWnd數所
指的Window Procedure,NIF_ICON表示要有ICON於右下方,NIF_TIP
表示Mouse移過去時會有Tip出現
5. uCallbackMessage:將來使用者在圖示上按下滑鼠時, Windows 會以訊息
通知視窗程序, 而此一參數為訊息之編號。
6. hIcon:圖示。
7. szTip:提示訊息。
該Function有一個傳回值,如果呼叫成功傳回1,否則傳回 0
我使用一個NIcon的Class來解決這件事,另外還有一個NIcon.Bas要共同搭配,如果說
您不想讓程式在執行AddNIcon時,仍顯示表單不隱藏,那您得修改一下AddNIcon,而若
真如此,可能您也不需要有NIF_MESSAGE的功能,也就不用那一段SubClass的功能了。
NIcon 的Initial設定
m_MsgNo = WM_USER
m_ID = 9999 註:使用者為圖示所設定的 ID
m_tip = Trim(Screen.ActiveForm.Caption) 表單的Caption
Set m_Form = Screen.ActiveForm
m_hIcon = m_Form.Icon.Handle 表單的Icon
AddNIcon(para_Form as Form) 來新增一個Icon於右下角
para_Form指的是要被縮小的Form,當執行Shell_NotifyIconA(NIM_ADD, nid)成
功時,表示已有圖示於其上,所以我們將Form 隱藏,而什麼時候讓Form再度出現呢?
當我們在Icon上按一下Mouse左鍵時會出現,而這應該如做呢?方才說過,因NIF_MESSAGE
的設定,使得在Icon上的Mosue Click等會傳送nid.uCallbackMessage的訊息給
nid.hWnd所在的Window Procedure,所以我們使用SubClass的動作,在NICON.BAS
中的WndProcForIcon中,Check是否有我們定義的訊息出現,而進一步加以處理。
DelNIcon 來移除Icon,並取消SubClass的動作
ModNIcon 更動由AddNIcon所做出Icon的圖示與Tip
MsgNo 設定自WM_USER 起的第幾個自訂訊息,因我們程式說不定有其他自訂訊息,
所以我們可以設定MsgNo,以便不和其他訊息混淆,MsgNo自0算起;指的是
nid..uCallbackMessage
Tip 設定Tip
ID 設定Icon一個唯一的ID以便和其他Icon區分,設定一次後,不要再更改
ICON 設定Icon的圖示
這裡有幾個重要的地方要提出來,首先,
這個程式原本同時只允許一個NIcon的產生,也就是一個程式不能有兩個Icon縮於右下方
,這是因為用到SubClass的關係,如果我用們告PreWinProc為.Bas中的public變數來存
原先的Window Procedure的位址,如果現在有兩個Icon想縮進右下方,則第二個縮進去
時,會把第一個所記錄的PreWinProc給蓋掉,那麼,等一下一定會當機。所以一個程式
想要有兩個Icon進去,就必需是PrWinProc是屬於NIcon.Cls的區域變數,所以每一個NIcon
的Instance有自己的PreWndProc變數,但這裡有一個問題,在.BAS中又需要PreWndProc
當做參數傳給CallWindowProc以呼叫原來的Window Procdure,而們又沒有辦法傳
NIcon.Cls的PreWndProc進入.Bas的 Function WndProcForIcon(因這個Function的格式
固定),此時,我們先在NIcon.Cls中定一個區域變數PreWndProc,再使用一個VB使用者
很少用到的技巧:在NIcon.AddNicon中有一行,用以記錄這PreWndProc
ret = SetWindowLong(m_Form.hWnd, GWL_USERDATA, preWndProc)
32位元的程式設計中,每個Window都會保留32Bits給Application運用,在此記錄
preWndProc的值,而在.Bas中再以
prevWndProcForIcon = GetWindowLong(hWnd, GWL_USERDATA)
取出原先存的值,或許會問,Form不是有個 Tag嗎?是的,但是,我們在NIcon.Bas中,
能取得的只有hWnd,而得不到Form的Reference(如果我們同一程式有兩個Form成Icon於
右下方,因二者都是用Function WndProcForIcon,這個CallBack Function,唯一能分別
由是Form1或Form2所引起的呼叫的,只有其上的hWnd)。
另外,使用這個Class,最怕的就是使用了setWindowLong更動原先Window Procedure的
位址,如果程式結束前沒有將之設定回來,那程式不會正常結束,而要讓程式能正常結
束,則一定要讓DelNIcon能執行到,除了使用者直接呼叫之外,也可以不用管它,讓
NIcon正常結束時,執行Terminate的程式,那裡便會自動去呼叫DelNIcon,千萬不要在呼
叫AddNicon(me)之後,未執行到DelNicon便下End 指令,那麼,程式一定會當,若要結束
,請用unload Form的方式來結束。最後,如果同一個程式有一個以上的Form要縮成Icon
,那所有的MsgNo要相同(這一點要求不會太過份),原因由.Bas的程式很清楚可見,IconMsg
是一個公用變數,為各個NIcon的Instance所共用,不允許隨意更動。
以下文章在NIcon.cls
Option Explicit
Private m_tip As String
Private m_MsgNo As Long
Private m_ID As Long
Private m_hIcon As Long
Private m_Icon As IPictureDisp
Private m_Form As Form
Private nid As NOTIFYICONDATA
Private HadAdd As Boolean
Private preWndProc As Long
Public Property Get Tip() As String
Tip = m_tip
End Property
'設定Mouse移至Icon時所show出之Tip
Public Property Let Tip(ByVal vNewValue As String)
m_tip = vNewValue
End Property
Public Property Get MsgNo() As Long
MsgNo = m_MsgNo - WM_USER
End Property
'設定Mosue Click於Icon時,所送出之訊息編號
Public Property Let MsgNo(ByVal vNewValue As Long)
m_MsgNo = vNewValue + WM_USER
End Property
'設定ID
Public Property Get ID() As Long
ID = m_ID
End Property
Public Property Let ID(ByVal vNewValue As Long)
m_ID = vNewValue
End Property
'設定Icon的圖示
Public Property Set Icon(ByVal vNewValue As IPictureDisp)
Set m_Icon = vNewValue
m_hIcon = m_Icon.Handle
End Property
'將原先的Form隱藏,並在右下方加入一個Icon,傳入的是待處理的Form
Public Function AddNIcon(ByVal para_form As Form) As Boolean
Dim ret As Long
AddNIcon = False
If Not HadAdd Then
Call Shell_NotifyIconA(NIM_DELETE, nid)
Set m_Form = para_form
nid.cbSize = Len(nid)
nid.hWnd = m_Form.hWnd
nid.uID = m_ID
nid.uFlags = NIF_ICON + NIF_TIP + NIF_MESSAGE
nid.hIcon = m_hIcon
nid.szTip = m_tip + Chr(0)
nid.uCallbackMessage = m_MsgNo
Dim i As Integer
i = Shell_NotifyIconA(NIM_ADD, nid)
If i = 1 Then '新增成功
IconMsg = m_MsgNo
preWndProc = GetWindowLong(m_Form.hWnd, GWL_WNDPROC)
'記錄原先window procedure的Addr於Window的extra 32 bits,每個Window都會保留
'32Bits給Application運用,在此記錄preWndProc的值
ret = SetWindowLong(m_Form.hWnd, GWL_USERDATA, preWndProc)
ret = SetWindowLong(m_Form.hWnd, GWL_WNDPROC, AddressOf WndProcForIcon)
m_Form.Hide '如果不想加入時就隱藏form,這行請Mark,並在您的程式中自行決定何時Hide form
AddNIcon = True
HadAdd = True
End If
End If
End Function
'刪除於右下方的Icon
Public Sub DelNIcon()
Dim ret As Long
If preWndProc <> 0 Then
ret = SetWindowLong(m_Form.hWnd, GWL_WNDPROC, preWndProc)
preWndProc = 0
End If
If HadAdd Then
Call Shell_NotifyIconA(NIM_DELETE, nid)
HadAdd = False
Set m_Form = Nothing
End If
End Sub
'修改Icon的設定,能改的只有Icon的圖與Icon的Tip
Public Function ModNIcon() As Boolean
ModNIcon = False
If HadAdd Then
nid.hIcon = m_hIcon
nid.szTip = m_tip + Chr(0)
Dim i
i = Shell_NotifyIconA(NIM_MODIFY, nid)
If i = 1 Then
ModNIcon = True
End If
End If
End Function
Private Sub Class_Initialize()
m_MsgNo = WM_USER
m_ID = 9999
m_tip = Trim(Screen.ActiveForm.Caption)
Set m_Form = Screen.ActiveForm
m_hIcon = m_Form.Icon.Handle
HadAdd = False
End Sub
Private Sub Class_Terminate()
Call DelNIcon
End Sub
|
Public Const WM_USER = &H400
Public Const GWL_WNDPROC = (-4)
Public Const GWL_USERDATA = (-21)
Public Const WM_LBUTTONDOWN = &H201
Public Const NIM_ADD = 0
Public Const NIM_MODIFY = 1
Public Const NIM_DELETE = 2
Public Const NIF_MESSAGE = 1
Public Const NIF_ICON = 2
Public Const NIF_TIP = 4
Public Type NOTIFYICONDATA
cbSize As Long
hWnd As Long
uID As Long
uFlags As Long
uCallbackMessage As Long
hIcon As Long
szTip As String * 64
End Type
Public IconMsg As Long
Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Declare Function Shell_NotifyIconA Lib "SHELL32" (ByVal dwMessage As Long, lpData As NOTIFYICONDATA) As Integer
Function WndProcForIcon(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim prevWndProcForIcon As Long
'取回前一個Window procdure所在的位置,這個值是在Nicon.AddNicon中放進去的
prevWndProcForIcon = GetWindowLong(hWnd, GWL_USERDATA)
If Msg = IconMsg Then
If lParam = WM_LBUTTONDOWN Then
Dim mForm As Form
For Each mForm In Forms
If mForm.hWnd = hWnd Then
mForm.Show
End If
Next
End If
'若您按Mosue右鍵或Double Click等,要執行什麼事,請在這裡加進來
End If
WndProcForIcon = CallWindowProc(prevWndProcForIcon, hWnd, Msg, wParam, lParam)
End Function
|
'執行時,請不要用強制結束的方式結束,要讓NICON Class有機會執行到Terminate的Code
Private nid As New NIcon
Private Sub Command1_Click()
nid.Tip = "HaHa!!"
nid.ID = 9998 '若沒設,會使用內訂值9999
nid.MsgNo = 2 '若沒設,內訂0
Call nid.AddNIcon(Me)
End Sub
Private Sub Command2_Click()
nid.DelNIcon
End Sub
Private Sub Command3_Click()
'以下這一行可以修改成您所要的Icon後再unmark
'Set nid.Icon = LoadPicture("f:\vbprg\nicon\technlgy.ico")
nid.Tip = "Another Tip"
Call nid.ModNIcon
End Sub
'不要在Command1沒有按下之前就按Command4
Private Sub Command4_Click()
Me.Hide '按了Command1後使之產生Icon於右下方,再於Icon處mouse Click
'form會再出現,要讓Form再度不見時,請直接執行me.hide
End Sub
|