寫在前面
這是PB案例學習筆記系列文章的第28篇,該系列文章適合具有一定PB基礎的讀者。
通過一個個由淺入深的編程實戰案例學習,提高編程技巧,以保證小伙伴們能應付公司的各種開發需求。
文章中設計到的源碼,小凡都上傳到了gitee代碼倉庫https://gitee.com/xiezhr/pb-project-example.git
需要源代碼的小伙伴們可以自行下載查看,后續文章涉及到的案例代碼也都會提交到這個倉庫【pb-project-example】
如果對小伙伴有所幫助,希望能給一個小星星?支持一下小凡。
一、小目標
基本上所有的應用程序在點擊鼠標右鍵之后都會彈出一個菜單,本案例我們將使用PB實現這個功能。通過鼠標右鍵,彈出一個命令菜單,
菜單上包含“剪切”、“復制”、“粘貼”、“加粗”等操作標識。這在日常開發中是一個非常常見的功能,一定要學會哈。
最終效果如下所示
二、實現思路
我們將通過引用user32.dll
中的GetMenu
、GetSubMenu
、TrackPopupMenu
、GetSystemMetrics
等窗體操作函數和LoadImageA
、
SetMenuItemBitmaps
等圖形顯示函數來實現相關功能。具體函數功能如下
① GetMenu
函數
用于獲取與特定窗口關聯的菜單句柄
函數原型:
HMENU GetMenu(HWND hWnd);
參數說明:
hWnd
: 一個窗口句柄,指定了要查詢其菜單的窗口。
返回值:
成功時,返回窗口的菜單句柄(HMENU
)。如果沒有菜單關聯到該窗口,則返回NULL。
② GetSubMenu
函數
用于從主菜單中獲取指定位置的子菜單
函數原型:
HMENU GetSubMenu(HMENU hMenu, int nPos);
參數說明:
hMenu
: 主菜單的句柄,即之前通過GetMenu
或其他方式獲得的菜單句柄。nPos
: 一個整數,表示要獲取的子菜單在其父菜單中的位置索引,其中0通常是第一個子菜單(頂級菜單項)。
返回值
成功時,返回指定位置的子菜單句柄(HMENU
)。如果索引無效或沒有子菜單,則返回NULL。
③ TrackPopupMenu
函數
用于在一個指定的位置顯示一個彈出式菜單,并跟蹤用戶的選擇。
函數原型:
BOOL TrackPopupMenu(HMENU hMenu,UINT uFlags,int x,int y,int nReserved,HWND hWnd,CONST RECT* prcRect
);
參數說明:
hMenu
: 要顯示的彈出菜單的句柄。uFlags
: 控制菜單顯示方式的標志,如TPM_LEFTALIGN
、TPM_RIGHTBUTTON
等。x
,y
: 菜單左上角的屏幕坐標,相對于屏幕原點或指定窗口客戶區。nReserved
: 在舊版本中保留,應設為0。hWnd
: 與菜單顯示相關的窗口句柄,用于消息處理。prcRect
(舊版)/lptpm
(新版,包含x
,y
,flags
,rect
等更詳細信息的結構體): 用于指定額外的顯示參數或限制區域。
返回值:
如果用戶選擇了菜單項并成功處理,返回非零值;否則,返回0。通常需要檢查GetLastError
來確定失敗原因。
④ GetSystemMetrics
函數
用于獲取有關當前系統的各種度量信息和配置設置。這些信息涵蓋了顯示器分辨率、顏色深度、鼠標和鍵盤狀態、操作系統版本特性等多個方面。
函數原型:
int GetSystemMetrics(int nIndex);
參數說明:
nIndex
: 一個整型參數,作為索引值,指定了想要獲取的系統度量信息類型。不同的索引值對應不同的系統配置或狀態信息。
返回值:
函數根據nIndex
所指定的索引值,返回相應的系統度量信息值。返回值類型通常是整數。
⑤ LoadImageA
函數
用于加載光標、圖標、位圖或圖元文件資源。
函數原型:
HANDLE LoadImageA(HINSTANCE hinst,LPCSTR lpszName,UINT uType,int cxDesired,int cyDesired,UINT fuLoad
);
參數說明:
hinst
: 一個模塊實例句柄,通常為NULL以加載系統資源,或指定的DLL句柄來加載該DLL中的資源。lpszName
: 指向資源名稱(文件名或資源ID,如圖標ID)的指針,可以是字符串或整數資源ID(需要轉換為LPCTSTR)。uType
: 指定要加載的圖像類型,可以是IMAGE_BITMAP
,IMAGE_ICON
,IMAGE_CURSOR
, 或IMAGE_ENHMETAFILE
。cxDesired
,cyDesired
: 指定希望加載圖像的寬度和高度(以像素為單位)。如果為0,則使用圖像的實際大小。fuLoad
: 加載標志,如LR_CREATEDIBSECTION
,LR_LOADFROMFILE
,LR_DEFAULTSIZE
等,用于控制加載行為。
返回值:
成功時返回圖像句柄(HBITMAP
, HCURSOR
, HICON
, 或 HENHMETAFILE
),失敗則返回NULL。
⑥ SetMenuItemBitmaps
函數
用于設置菜單項的位圖圖像的API,可以為菜單項的正常狀態和選中(或按下)狀態指定不同的位圖
函數原型:
BOOL SetMenuItemBitmaps(HMENU hMenu,UINT uPosition,UINT uFlags,HBITMAP hBitmapUnchecked,HBITMAP hBitmapChecked
);
參數說明:
hMenu
: 要修改的菜單的句柄。uPosition
: 要設置位圖的菜單項的位置索引,從0開始計數。uFlags
: 指定要設置哪一組位圖的標志,可以是MF_BYCOMMAND
(基于菜單項的ID查找)或MF_BYPOSITION
(直接使用位置索引)。hBitmapUnchecked
: 未選中狀態下菜單項的位圖句柄。hBitmapChecked
: 選中或按下狀態下菜單項的位圖句柄。
返回值:
函數執行成功返回非零值,失敗返回0。可以通過GetLastError
獲取詳細的錯誤信息
三、創建程序基本框架
① 新建examplework
工作區
② 新建exampleapp
應用
③ 新建w_main
窗口,將其Title
設置為"右鍵菜單"
由于文章篇幅原因,以上步驟不再贅述,如果忘記了的小伙伴可以翻一翻該系列第一篇文章復習一下
④ 新建w_popmenu
窗口
⑤ 在w_mian
窗口中布局控件
新建一個MultiLineEdit
控件和一個CommandButton
控件,名稱分別為mle_1
和cb_1
,調整控件布局,
并將cb_1
的Text
設置為"關閉"
⑥ 設置w_popmenu
窗口屬性
將w_popmenu
窗口縮小成一個小方塊,并在MenuName
屬性欄中添加菜單m_popmenu
⑦ 保存w_popmenu
窗口
⑧ 新建m_popmenu
菜單如下圖所示
四、編寫代碼
① 在w_main
窗口中定義實例變量,代碼如下
ulong il_popmenu_window_hwnd
② 在w_main
窗口中定義外部函數
FUNCTION ulong GetMenu(ulong hwnd) LIBRARY "user32.dll"FUNCTION ulong GetSubMenu(ulong hMenu,ulong nPos) LIBRARY "user32.dll"FUNCTION ulong TrackPopupMenu(ulong hMenu,ulong wFlags,ulong x,ulong y,ulong nReserved,ulong hwnd,ref Rect lprc) LIBRARY "user32.dll"
③ 在w_main
窗口的Open
中添加如下代碼
open(w_popmenu)il_popmenu_window_hwnd = handle(w_popmenu)
④ 在w_main
窗口的close
事件中添加如下代碼
close(w_popmenu)
⑤ 在mle_1
控件的rbuttondown
事件中輸入如下代碼
ulong hmenu,hsubmenu,hwnd
integer li_x,li_yRECT l_rect
l_rect.left = 0
l_rect.top = 0
l_rect.right = 0
l_rect.bottom = 0hmenu = getmenu(il_popmenu_window_hwnd)
hsubmenu = getsubmenu(hmenu, 0)li_x = (xpos + parent.x) / 5
li_y = (ypos + parent.y) / 5TrackPopupMenu(hsubMenu, 2, li_x, li_y, 0, il_popmenu_window_hwnd, l_rect)
⑥ 在cb_1
按鈕的clicked
中添加如下代碼
close(parent)return 0
⑦ 在w_popmenu
窗口中定義實例變量
//Win32
CONSTANT Integer IMAGE_BITMAP = 0
CONSTANT Integer LR_LOADFROMFILE = 16
CONSTANT Integer SM_CXMENUCHECK = 71
CONSTANT Integer SM_CYMENUCHECK = 72
CONSTANT Integer MF_BITMAP = 4
CONSTANT Integer MF_BYPOSITION = 1024
⑧ 在w_popmenu
窗口中定義外部函數
FUNCTION ulong LoadImageA(ulong hintance, string filename,uint utype,int x,int y,uint fload) LIBRARY "USER32.DLL"FUNCTION boolean SetMenuItemBitmaps(ulong hmenu,uint upos,uint flags,ulong handle_bm1,ulong handle_bm2) LIBRARY "USER32.DLL"FUNCTION int GetSystemMetrics( int nIndex ) LIBRARY "USER32.DLL"FUNCTION int GetSubMenu(ulong hMenu,int pos) LIBRARY "USER32.DLL"FUNCTION ulong GetMenu(ulong hWindow) LIBRARY "USER32.DLL"
⑨ 在w_popmenu
窗口的open
事件中添加如下代碼并準備圖片
**注:**圖片資源會一起推送到gitee倉庫,需要圖片資源的小伙伴克隆倉庫即可獲取
long ll_MainHandle
long ll_SubMenuHandle
long ll_X
long ll_Y
long ll_Bitmapcut
long ll_Bitmapcopy
long ll_Bitmappaste
long ll_Bitmapitl
long ll_bitmapcnt
long ll_bitmapunderlinethis.visible = falsell_MainHandle = GetMenu(Handle(this))ll_SubMenuHandle = GetSubMenu(ll_MainHandle,0)ll_x = GetSystemMetrics(SM_CXMENUCHECK)
ll_y = GetSystemMetrics(SM_CYMENUCHECK) ll_Bitmapcut = LoadImageA(0,'cut.bmp', IMAGE_BITMAP ,ll_x,ll_y,LR_LOADFROMFILE)
ll_Bitmapcopy = LoadImageA(0,'copy.bmp', IMAGE_BITMAP ,ll_x,ll_y,LR_LOADFROMFILE)
ll_Bitmappaste = LoadImageA(0,'paste.bmp', IMAGE_BITMAP ,ll_x,ll_y,LR_LOADFROMFILE)
ll_Bitmapitl = LoadImageA(0,'itl.bmp', IMAGE_BITMAP ,ll_x,ll_y,LR_LOADFROMFILE)
ll_Bitmapcnt = LoadImageA(0,'big.bmp', IMAGE_BITMAP ,ll_x,ll_y,LR_LOADFROMFILE)
ll_bitmapunderline = LoadImageA(0,'ul.bmp', IMAGE_BITMAP ,ll_x,ll_y,LR_LOADFROMFILE)SetMenuItemBitmaps(ll_SubMenuHandle,0,MF_BYPOSITION,ll_Bitmapcut,ll_Bitmapcut)
SetMenuItemBitmaps(ll_SubMenuHandle,1,MF_BYPOSITION,ll_Bitmapcopy,ll_Bitmapcopy)
SetMenuItemBitmaps(ll_SubMenuHandle,2,MF_BYPOSITION,ll_Bitmappaste,ll_Bitmappaste)
SetMenuItemBitmaps(ll_SubMenuHandle,4,MF_BYPOSITION,ll_Bitmapitl,ll_Bitmapitl)
SetMenuItemBitmaps(ll_SubMenuHandle,5,MF_BYPOSITION,ll_Bitmapcnt,ll_Bitmapcnt)
SetMenuItemBitmaps(ll_SubMenuHandle,6,MF_BYPOSITION,ll_Bitmapunderline,ll_Bitmapunderline)
⑩ 在開發界面左邊的SystemTree
窗口中雙擊exampleapp
應用對象,并在其open
事件中添加如下代碼
open(w_main)
五、運行程序
經過一波代碼編寫之后,我們來驗證下結果
本期內容到這兒就結束了★,°:.☆( ̄▽ ̄)/$:.°★ 。 希望對您有所幫助
我們下期再見 ヾ(?ω?`)o (●’?’●)