-- 1
將菜單項移到菜單欄的最右邊
在一些應用程序中,常把一些特殊的菜單項放在菜單欄的最右邊(如WPS2000
中的"定制界面"菜單,一些應用程序的幫助菜單),這些菜單項放在菜單欄
(menu bar)的右邊比較好。Delphi中雖然沒有直接提供(呵呵,也許是我沒找
到)把菜單放在菜單欄右邊的函數,但是Delphi的Windows單元封裝了可以實現這
種效果的Windows API函數(ModifyMenu、GetMenuItemInfo)。因此,我們在
Delphi中可以直接使這些函數來實現這種效果。
使用ModifyMenu和GetMenuItemInfo都可以將菜單項移到菜單欄的最右邊,那
么選那個較好呢?據 Windows SDK 文檔:
ModifyMenu函數可以改變一個已經存在的菜單項的設置,這個函數用於指定
一個菜單的標題、顯示外觀等項目,不過,ModifyMenu函數現在已經被功能更強
大的SetMenuItemInfo函數取代。雖然如此,如果你的程序不需要
SetMenuItemInfo函數提供的擴展功能,你仍舊可以繼續使用ModifyMenu函數。
ModifyMenu函數支持 Windows 32、9x、NT/2000。
SetMenuItemInfo函數支持 Windows 9x、NT/2000,不支持windows 32(較早
版本也不支持NT/2000)。
另外,SetMenuItemInfo函數功能比ModifyMenu函數強大,不過使用函數
SetMenuItemInfo需要的參數也比ModifyMenu函數復雜的多。所以這里我依然使用
比較簡單的ModifyMenu函數來實現。
ModifyMenu函數原型為:
BOOL ModifyMenu(
HMENU hMnu, // handle of menu
UINT uPosition, // menu item to modify
UINT uFlags, // menu item flags
UINT uIDNewItem, // menu item identifier or handle of drop-down
menu or submenu
LPCTSTR lpNewItem // menu item content
);
第一個參數hMENU 指的是要修改的菜單的句柄(如:MainMenu1.Handle);
第二個參數uPosition 指的是要修改的菜單項索引值(菜單欄左邊第一個其
值為零,從左向右遞增,最大值為菜單項的總數減一);
第三個參數uFlags指的是(第二個參數所指的菜單項修改后的)新菜單項的
狀態;
第四個參數uIDNewItem 是指向新菜單的句柄;
第五個參數lpNewItem指的是新菜單的標題。
舉例如下:
創建一個帶有菜單的新工程,設其MainMenu組件的名字為MainMenu1,其中要
移到菜單欄的最右邊的那個菜單的名字為Help1。
在窗體的OnCreate時間的處理程序中加入以下代碼
procedure TForm1.FormCreate(Sender: TObject);
begin
ModifyMenu(MainMenu1.Handle,2,
MF_BYPOSITION or MF_HELP,
Help1.Handle,Pchar(Help1.Caption));
//其中:MF_HELP 參數決定菜單項(Help1)在窗口的最右端顯示。
end;
然后運行,看看效果如何
-- 2
定制系統菜單
常見的應用程序的主窗體中,利用鼠標左鍵點擊左上角的圖標,會彈出一個
系統菜單,這個菜單一定程度上給了我們很大方便,問題是那些都是缺省的系統
菜單命令,對於我們來說沒有太大的幫助。當然,我們可以自己定制系統菜單,
加上我們需要的東西,在Delphi 中,窗體組件並沒有提供系統菜單對應的組件,
所以定制系統菜單時就有我們自己動手用Windows API函數來實現。Windows API
函數中,常用於對菜單操作的函數有:
AppendMenu 在現有的菜單項尾部插入一個新菜單項;
DeleteMenu 刪除菜單中一個現有的菜單項,並清除該項;
RemoveMenu 在現有的菜單項中刪除某一項;
InsertMenu 在現有的菜單項中插入一個新項;
ModifyMenu 修改一個現有菜單項;
這些函數用法都是類似的上面已經說了ModifyMenu函數的用法,下面僅用
AppendMenu函數在系統菜單中添加一個關於菜單來說明,雖然它本身是沒有什么
意義的,但希望可以起到拋磚引玉的作用。
AppendMenu 函數原型:
BOOL AppendMenu(
HMENU hMenu, // 要定制的菜單句柄
UINT uFlags, // 怎樣定制菜單項
UINT uIDNewItem, // 要定制的菜單項標識或子菜單句柄
LPCTSTR lpNewItem // 要定制的菜單項(字串)
);
在 AppendMenu 函數里,lpNewItem 和 uIDNewItem 參數依賴 uFlags 的不
同標志而有所變化。
唯一限制:系統菜單中添加的菜單項的ID值(uIDNewItem)必須小於 $F000(
十進制:61440);否則會與Windows系統菜單命令所使用的ID值相沖突。還要記住
當為這些新菜單在窗口過程中處理WM_SYSCOMMAND消息時,必須把其它的
WM_SYSCOMMAND消息發給DefWindowsProc(在Delphi中秩序在事件處理程序最后加
入一句 Inherited;),否則,嘿嘿,你試試就知道了。
首先創建一個新工程,因為要用到Menus單元中的內容,故在文件Unit1.pas的
uses語句中加入Menus。
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, Menus;
然后在窗體Form1的OnCreate事件處理過程加入以下代碼:
procedure TForm1.FormCreate(Sender: TObject);
var hSysMenu:hMENU;
begin
hSysMenu:=GetSystemMenu(Handle,False);//得到系統菜單句柄
AppendMenu(hSysMenu,MF_SEPARATOR,0,'');//添加一個分隔符
AppendMenu(hSysMenu,MF_STRING,3,'關於(&A)');
end;
在TForm1的類型定義中,添加系統菜單中新建菜單項的onClick事件的處理過
程的聲明(即對消息WM_SYSCOMMAND的處理聲明):
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
procedure SysMenuCommand(var Msg:TWMMENUSELECT);
message WM_SYSCOMMAND;
{ Private declarations }
public
{ Public declarations }
end;
接着按Shift+Ctrl+C完成類聲明。在其中填入以下代碼:
procedure TForm1.SysMenuCommand(var Msg: TWMMENUSELECT);
begin
if Msg.IDItem=3 then
MessageBox(0,'A Poor-Person''s Menu Program'+#13+
' Copyright Skyey ,2000','Skyey',
MB_OK+MB_ICONINFORMATION);
Inherited;
end;
編譯、運行程序,測試其效果。
附:uFlags 一些定義值://選譯自Delphi 5帶的Windows SDK 幫助
MF_BITMAP 指明該菜單項是一位圖,在 lpNewItem 參數代表位圖句柄
MF_CHECKED 在菜單項的前面放上一個"選中"標記
MF_DISABLED 屏蔽該菜單項,但不使它變成灰色
MF_ENABLED 使該菜單項有效,與 MF_DISABLED 相反
MF_GRAYED 除了有 MF_DISABLED 的作用以外,還把該菜單項變灰
MF_MENUBREAK 把該菜單與現有菜單並排放在一起
MF_MENUBARBREAK 與MF_MENUBREAK 相同,除了在中間放一條豎線外
MF_OWNERDRAW 表明該菜單項為自繪菜單項,還必須處理一切的顯示、更新問
題
MF_POPUP 該菜單項為一子菜單,uIDNewItem 參數代表其句柄
MF_SEPARATOR 在菜單項畫上一分割線
MF_STRING 該菜單項是一文本字串,lpNewItem 是其內容
MF_UNCHECKED 取消該菜單項前面的"選中"標記
以下幾組標志中每組的標志不能同時使用:
< 1> MF_DISABLED, MF_ENABLED, and MF_GRAYED
< 2> MF_BITMAP, MF_STRING, and MF_OWNERDRAW
< 3> MF_MENUBARBREAK and MF_MENUBREAK
< 4> MF_CHECKED and MF_UNCHECKED
-- 3
為菜單動態定義快捷鍵
其實就是改變菜單的ShortCut屬性,在程序中加入一個HotKey組件,然后把
其HotKey屬性賦給菜單的ShortCut屬性即可。如:
procedure TForm1.BtnChange1Click(Sender: TObject);
begin
New1.ShortCut:=Hotkey1.HotKey;
end;
如果你只需要程序自行更改有限的幾個快捷鍵而不需要用戶自己改變,那么
也可以不用HotKey組件,直接把一個TShortCut類型的數值賦給菜單的ShortCut屬
性即可。如:
procedure TForm1.BtnChange1Click(Sender: TObject);
begin
New1.ShortCut:= 32833;//就是Alt+A
end;
一個快捷鍵的值可以按下面的方法在設計時得到:先在設計時改變菜單的
ShortCut屬性的為你需要的快捷鍵,然后在窗體上-> 右鍵-> View as Text,在
DFM文件中找到那個組件,那個ShortCut后面的就是。如:需要Ctrl+Alt+N
object New1: TMenuItem
Caption = '&New'
ShortCut = 49230 //就是這個
onClick = New1Click
end
-- 4
動態改變菜單
就是改變菜單項的Enabled屬性和Visible屬性。這兩個屬性既可以在設計時
改變,也可以在運行時改變。
如果將Enabled屬性設置為False,則菜單項呈灰色狀態,不可用;如果設置
為True,則菜單項處於正常能用的狀態。
如果將Visible屬性設置為False,則該菜單項在運行時不顯示;如果設置為
True,則該菜單項顯示。
如:New1.Enabled:=False;//New菜單項變成灰色,菜單現在不可用
New1.Enabled:=True; //New菜單項現在可以使用
New1.Visible:=False //New菜單項現在不顯示
New1.Visible:=True; //New菜單項現在顯示
唯一需要注意的是:Visible和Enabled屬性時兩個互相獨立的屬性,二者互
不干擾,如果一個菜單擁有一個快捷鍵且其Enabled屬性為True,即使其Visible
屬性為False(即運行時不可見),仍可以用快捷鍵來訪問。
在程序運行期間,修改菜單項的這兩個屬性,即可以動態地改變菜單顯示。
-- 5
獲取用戶錯誤按鍵信息
如果用戶按Alt和一個與菜單項不匹配的字符時,或者當顯示彈出式菜單而用
戶按下一個與彈出式菜單里的項不匹配的字符鍵時。我們怎么知道用戶按錯了鍵
,又怎么知道用戶按了那個鍵呢?
事實上,當發生上面假設的情況時,Windows會發出一個WM_MENUCHAR消息,
通常我們不需要處理這個消息,Windows程序通常會把它傳給DefWindowProc,它一
般給Windows返回0,即使Windows發出一個短的蜂鳴聲。如果我們需要處理上面假
設的情況時,只要攔截這個消息就可以了。
讓我們先了解一下這個消息的參數:
WM_MENUCHAR
LOWORD(wParam); //即用戶按鍵的ASCII碼
HIWORD(wParam); //選擇碼
lParam; // 菜單句柄
選擇碼含義:
0: 不顯示彈出菜單;
MF_POPUP: 顯示下拉菜單、子菜單、快捷菜單(彈出式菜單)
MF_SYSMENU: 顯示系統彈出式菜單
消息返回值:
0: 通知Windows丟棄用戶按的這個字符並發出一個短的蜂鳴聲;
1: 通知Windows應該關閉當前活動菜單;
2: 通知Windows返回一個相對於菜單項的零基點的低位字,它由Windows決
定。
了解這一點后,讓我們具體做一下:
創建一個帶有菜單的新工程,在窗體上放一個Memo組件(Name:Memo1)在窗
體的公有變量后加入消息的聲明部分:
public
procedure MMenuChar(var Msg:TMessage);message WM_MENUCHAR;
{ Public declarations }
end;
按Shift+Ctrl+C完成類聲明。在其中填入以下代碼:
procedure TForm1.MMenuChar(var Msg: TMessage);
begin
Memo1.Lines.Add(Chr(LOWORD(Msg.WParam)));
Memo1.Lines.Add(IntToStr(HIWORD(Msg.WParam)));
Msg.Result:=0;
end;
編譯、鏈接並運行程序,測試其效果。