delphi Windows 编程[12] - 菜单与菜单资源(1-3)


本文整理自网络,侵删。

 
Windows 编程[12] - 菜单与菜单资源(一)
假如我们用 TMainMenu 控件设计如下菜单:

该菜单在窗体源文件中是这样存储的:
object MainMenu1: TMainMenu
Left = 160
Top = 104
object File1: TMenuItem
  Caption = '&File'
  object New1: TMenuItem
    Caption = '&New'
  end
  object Open1: TMenuItem
    Caption = '&Open'
  end
  object Save1: TMenuItem
    Caption = '&Save'
  end
  object SaveAs1: TMenuItem
    Caption = 'Save &As'
  end
  object N1: TMenuItem
    Caption = '-'
  end
  object Exit1: TMenuItem
    Caption = 'E&xit'
  end
end
object Edit1: TMenuItem
  Caption = '&Edit'
  object Cut1: TMenuItem
    Caption = 'Cu&t'
  end
  object Copy1: TMenuItem
    Caption = '&Copy'
  end
  object Paste1: TMenuItem
    Caption = '&Paste'
  end
end
object Help1: TMenuItem
  Caption = '&Help'
  object About1: TMenuItem
    Caption = '&About'
  end
end

在 VC 中, 菜单设计是要保存在资源源文件(*.rc)中, 不管是 VC 还是 Delphi 用什么临时储存方式, 但最终都会被编译成相同的资源文件(*.res).

在 Delphi 中如果想要更灵活地使用菜单, 也回避不了使用资源文件.

原来在学习资源文件时曾经简单接触过菜单资源(参考以前的内容), 不过这次要站在 Windows 的角度, 涉及更多内容!

下面是和上例对应的资源文件.

建立步骤:

1、新建控制台工程, 保存! 然后再建立资源文件;

2、建立资源文件: File -> New -> Other... -> Text , 写入下面资源文件代码, 和工程保存在相同文件夹, 保存为 *.rc, 我这里使用的是: TestRes.rc;

3、然后从工程中导入资源文件(Project Manager 窗口, 右键点击工程名 - Add);
此时工程代码会自动增加一行: {$R 'TestRes.res' 'TestRes.rc'}

4、注意: 在次以后在工程中修改资源名, 工程代码会随之改变; 修改资源代码后需要先保存, 编译时才有效!

5、另外, 菜单资源的写法还是挺复杂的、功能也很强大; 后面的例子再说.
MyMenu1 Menu
Begin
  Popup "&File"
  Begin
    MenuItem "&New"
    MenuItem "&Open"
    MenuItem "&Save"
    MenuItem Separator
    MenuItem "E&xit"
  End

  Popup "&Edit"
  Begin
    MenuItem "Cu&t"
    MenuItem "&Copy"
    MenuItem "&Paste"
  End

  Popup "&Help"
  Begin
    MenuItem "&About"
  End
End

调用代码:

和原来的程序只有两行不同:

1、{$R 'TestRes.res' 'TestRes.rc'} --- 这是插入资源文件
2、cls.lpszMenuName := 'MyMenu1'; --- 这是指定菜单
program Project1;

{$R 'TestRes.res' 'TestRes.rc'}

uses
  Windows,
  Messages,
  SysUtils;

function WndProc(wnd: HWND; msg: UINT; wParam: Integer; lParam: Integer): Integer; stdcall;
begin
  Result := 0;
  case msg of
    WM_DESTROY : PostQuitMessage(0);
  else
    Result := DefWindowProc(wnd, msg, wParam, lParam);
  end;
end;

function RegMyWndClass: Boolean;
var
  cls: TWndClass;
begin
  cls.style         := CS_HREDRAW or CS_VREDRAW;
  cls.lpfnWndProc   := @WndProc;
  cls.cbClsExtra    := 0;
  cls.cbWndExtra    := 0;
  cls.hInstance     := HInstance;
  cls.hIcon         := 0;
  cls.hCursor       := LoadCursor(0, IDC_ARROW);
  cls.hbrBackground := HBRUSH(COLOR_WINDOW + 1);
  cls.lpszMenuName  := 'MyMenu1'; {在这里指定菜单资源, MyMenu1 是编辑资源文件时命名的}
  cls.lpszClassName := 'MyWnd';
  Result := RegisterClass(cls) <> 0;
end;

{程序入口}
const
  tit = 'New Form';
  ws = WS_OVERLAPPEDWINDOW;
  x = 100; y = 100; w = 300; h = 180;
var
  hWnd: THandle;
  Msg : TMsg;
begin
  RegMyWndClass;
  hWnd := CreateWindow('MyWnd', tit, ws, x, y, w, h, 0, 0, HInstance, nil);
  ShowWindow(hWnd, SW_SHOWNORMAL);
  UpdateWindow(hWnd);

  while(GetMessage(Msg, 0, 0, 0)) do
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
end.



我们可以把上例中的资源文件修改成这样:
MyMenu1 MENUEX
BEGIN
  POPUP "&File"        ,65535
  BEGIN
    MENUITEM "&New"    ,101
    MENUITEM "&Open"   ,102
    MENUITEM "&Save"   ,103
    MENUITEM SEPARATOR
    MENUITEM "E&xit"   ,104
  END
  POPUP "&Edit"        ,65535
  BEGIN
    MENUITEM "Cu&t"    ,201
    MENUITEM "&Copy"   ,202
    MENUITEM "&Paste"  ,203
  END
  POPUP "&Help"        ,65535
  BEGIN
    MENUITEM "&About"  ,301
  END
END

解释一下:

1. 以前是使用 MENU 标识菜单资源的; 用 MENUEX 会有更多功能.

2、就像 Delphi 一样, 它们使用 BEGIN 与 END 划分语句块, 也可以用 C/C++ 语言的方式: { ... }; 还有它的注释也是 C 语言格式的.

3、MyMenu1 MENUEX 中的 MyMenu1 是自定义的该菜单的标识.

4、MyMenu1 MENUEX 后面可以有关于载入和释放的控制符: PRELOAD、MOVEABLE PURE、DISCARDABLE(这个我没找到权威资料, 有谁知道, 万望告诉我; DISCARDABLE 用的最多, 可能是该资源没被使用可以忽略的意思).

5、POPUP 表示这是一个弹出菜单项, 也就是: 它有下级菜单; 大家习惯用 65535 做标识.

6、MENUITEM 表示一个菜单项, MENUITEM SEPARATOR 表示一个菜单分隔符.

7、MENUITEM 的第一个参数是要显示的文本, & 用来指定加速键.

8、MENUITEM 的第二个参数(参数用 , 号隔开)是该菜单项的标识, 是一个整数值(1-65535); 当用户点击菜单时, 会送出 WM_COMMAND 消息, 消息的 wParam 参数就是这个标识, 从而让我们知道点击的是哪一个菜单项; 这个标识有点像句柄, 一般在操作这个菜单项时也需要这个标识.

现在要解决一个问题: 这些 MENUITEM 的标识 101、102、103 等等不好记忆, 如果定义成有字面意义的常量就好了.
在 Delphi 中使用 const 定义常量, 但资源文件是系统级的, 需要用 C/C++ 的方法来定义常量. 这样我们可以修改如下:
#define IDM_New   101
#define IDM_Open  102
#define IDM_Save  103
#define IDM_Exit  104
#define IDM_Cut   201
#define IDM_Copy  202
#define IDM_Paste 203
#define IDM_About 301

MYMENU1 MENUEX
BEGIN
  POPUP "&File"        ,65535
  BEGIN
    MENUITEM "&New"    ,IDM_New
    MENUITEM "&Open"   ,IDM_Open
    MENUITEM "&Save"   ,IDM_Save
    MENUITEM SEPARATOR
    MENUITEM "E&xit"   ,IDM_Exit
  END
  POPUP "&Edit"        ,65535
  BEGIN
    MENUITEM "Cu&t"    ,IDM_Cut
    MENUITEM "&Copy"   ,IDM_Copy
    MENUITEM "&Paste"  ,IDM_Paste
  END
  POPUP "&Help"        ,65535
  BEGIN
    MENUITEM "&About"  ,IDM_About
  END
END

继续研究: 每一个菜单项后面可以有很多参数来指定不同的功能和显示效果.
譬如: MFT_STRING(是字符串)、MFS_CHECKED(选择)、MFS_GRAYED(禁用)、MFT_RIGHTJUSTIFY(显示在右边)等等, 示例如下:
#define IDM_New   101
#define IDM_Open  102
#define IDM_Save  103
#define IDM_Exit  104
#define IDM_Cut   201
#define IDM_Copy  202
#define IDM_Paste 203
#define IDM_About 301

MYMENU1 MENUEX
BEGIN
  POPUP "&File"        ,65535
  BEGIN
    MENUITEM "&New"    ,IDM_New   ,MFT_STRING, MFS_CHECKED
    MENUITEM "&Open"   ,IDM_Open  ,MFT_STRING, MFS_GRAYED
    MENUITEM "&Save"   ,IDM_Save
    MENUITEM SEPARATOR
    MENUITEM "E&xit"   ,IDM_Exit
  END
  POPUP "&Edit"        ,65535
  BEGIN
    MENUITEM "Cu&t"    ,IDM_Cut
    MENUITEM "&Copy"   ,IDM_Copy
    MENUITEM "&Paste"  ,IDM_Paste
  END
  POPUP "&Help"        ,65535     ,MFT_RIGHTJUSTIFY
  BEGIN
    MENUITEM "&About"  ,IDM_About
  END
END

调用后的效果图:



又如: MFT_MENUBREAK(分列)、MFT_MENUBARBREAK(分列并带分割线), 示例如下:
#define IDM_New   101
#define IDM_Open  102
#define IDM_Save  103
#define IDM_Exit  104
#define IDM_Cut   201
#define IDM_Copy  202
#define IDM_Paste 203
#define IDM_About 301

MYMENU1 MENUEX
BEGIN
  POPUP "&File"        ,65535
  BEGIN
    MENUITEM "&New"    ,IDM_New
    MENUITEM "&Open"   ,IDM_Open  ,MFT_MENUBREAK
    MENUITEM "&Save"   ,IDM_Save
    MENUITEM SEPARATOR
    MENUITEM "E&xit"   ,IDM_Exit
  END
  POPUP "&Edit"        ,65535
  BEGIN
    MENUITEM "Cu&t"    ,IDM_Cut
    MENUITEM "&Copy"   ,IDM_Copy  ,MFT_MENUBARBREAK
    MENUITEM "&Paste"  ,IDM_Paste
  END
  POPUP "&Help"        ,65535     ,MFT_RIGHTJUSTIFY
  BEGIN
    MENUITEM "&About"  ,IDM_About
  END
END

调用后的效果图:



再如: MFT_RIGHTORDER(右对齐)示例如下:
#define IDM_New   101
#define IDM_Open  102
#define IDM_Save  103
#define IDM_Exit  104
#define IDM_Cut   201
#define IDM_Copy  202
#define IDM_Paste 203
#define IDM_About 301

MYMENU1 MENUEX
BEGIN
  POPUP "&File"        ,65535
  BEGIN
    MENUITEM "&New"    ,IDM_New  ,MFT_RIGHTORDER ,MFS_CHECKED
    MENUITEM "&Open"   ,IDM_Open
    MENUITEM "&Save"   ,IDM_Save
    MENUITEM SEPARATOR
    MENUITEM "E&xit"   ,IDM_Exit
  END
  POPUP "&Edit"        ,65535
  BEGIN
    MENUITEM "Cu&t"    ,IDM_Cut
    MENUITEM "&Copy"   ,IDM_Copy
    MENUITEM "&Paste"  ,IDM_Paste
  END
  POPUP "&Help"        ,65535
  BEGIN
    MENUITEM "&About"  ,IDM_About
  END
END

调用后的效果图:



还是 MFT_RIGHTORDER(右对齐) 的例子:
#define IDM_New   101
#define IDM_Open  102
#define IDM_Save  103
#define IDM_Exit  104
#define IDM_Cut   201
#define IDM_Copy  202
#define IDM_Paste 203
#define IDM_About 301

MYMENU1 MENUEX
BEGIN
  POPUP "&File"        ,65535    ,MFT_RIGHTORDER
  BEGIN
    MENUITEM "&New"    ,IDM_New
    MENUITEM "&Open"   ,IDM_Open
    MENUITEM "&Save"   ,IDM_Save
    MENUITEM SEPARATOR
    MENUITEM "E&xit"   ,IDM_Exit
  END
  POPUP "&Edit"        ,65535
  BEGIN
    MENUITEM "Cu&t"    ,IDM_Cut
    MENUITEM "&Copy"   ,IDM_Copy
    MENUITEM "&Paste"  ,IDM_Paste
  END
  POPUP "&Help"        ,65535
  BEGIN
    MENUITEM "&About"  ,IDM_About
  END
END


第三部分:
前面我们分别使用过 MENU 和 MENUEX 来指定菜单资源, 我发现用 MENU 不必注意大小写; 但 MENUEX 在使用参数时需要注意大小写! 另外, 指定相同的功能时, 它们使用的参数和格式也有很大的区别. 我想还是多了解 MENUEX 吧, 因为它的参数同时也是 Win32API 菜单函数们 需要的!

MENUEX 有四个参数, 分别是: 菜单文本, 菜单标识(整数), 菜单格式(常数: MFT_*), 菜单样式(常数: MFS_*).
后两个参数都有系统预定义的常量, 列表如下:
MFT_STRING       = 0;       {菜单项是字符串}
MFT_BITMAP       = 4;       {菜单项用一个图片来代替(暂没测试)}
MFT_MENUBARBREAK = $20;     {换列并有分列线}
MFT_MENUBREAK    = $40;     {换列}
MFT_OWNERDRAW    = $100;    {指定为物主菜单; 通过 WM_DRAWITEM 消息的申请可以绘制更复杂的菜单}
MFT_RADIOCHECK   = $200;    {圆点代替 √ 来表示选择
MFT_SEPARATOR    = $800;    {指定为分割线, 会忽略前面指定的菜单文本}
MFT_RIGHTORDER   = $2000;   {右对齐}
MFT_RIGHTJUSTIFY = $4000;   {显示在右边(帮助位置)}

MFS_GRAYED        = 3;          {禁止使用, 灰度显示}
MFS_DISABLED      = MFS_GRAYED; {同上}
MFS_CHECKED       = 8;          {选择}
MFS_HILITE        = $80;        {反色凸现}
MFS_ENABLED       = 0;          {不禁止, 这是默认的, 一般用于程序中对禁止的反操作}
MFS_UNCHECKED     = 0;          {不选择}
MFS_UNHILITE      = 0;          {不反色}
MFS_DEFAULT       = $1000;      {指定为缺省, 应该类似与缺省按钮(还没试过), 会加粗显示菜单文本}

以上同类参数可以通过 "或" 运算来同时指定功能, 当然这里不能使用 Delphi 的 "or", 是用 C/C++ 的 "|" 运算符. 再举个例子:
#define IDM_New   101
#define IDM_Open  102
#define IDM_Save  103
#define IDM_Exit  104
#define IDM_Cut   201
#define IDM_Copy  202
#define IDM_Paste 203
#define IDM_About 301

MYMENU1 MENUEX
BEGIN
  POPUP "&File"        ,65535
  BEGIN
    MENUITEM "&New"    ,IDM_New  ,MFT_STRING | MFT_RIGHTORDER ,MFS_DEFAULT | MFS_CHECKED | MFS_GRAYED
    MENUITEM "&Open"   ,IDM_Open ,MFT_STRING, MFS_HILITE
    MENUITEM "&Save"   ,IDM_Save
    MENUITEM SEPARATOR
    MENUITEM "E&xit"   ,IDM_Exit
  END
  POPUP "&Edit"        ,65535
  BEGIN
    MENUITEM "Cu&t"    ,IDM_Cut
    MENUITEM "&Copy"   ,IDM_Copy
    MENUITEM "&Paste"  ,IDM_Paste
  END
  POPUP "&Help"        ,65535
  BEGIN
    MENUITEM "&About"  ,IDM_About
  END
END

相关阅读 >>

Delphi 中使用微软全文翻译的小例子

Delphi 让窗体自适应屏幕显示

Delphi mscomm 实时串口通讯

Delphi 极速字符串替换函数

Delphi twebbrowser 设置焦点

Delphi 随机函数单元urandomutils

Delphi injectmemexe

Delphi 简单判断图片类型

Delphi access存储过程是带参数的查询语句

Delphi 中 unicode 转汉字 函数

更多相关阅读请进入《Delphi》频道 >>



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...