运行之后的效果如下,

图一

图二

主界面代码如下

unit Frm_Main;

interface

uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
CommCtrl,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
Vcl.Menus,
QPlugins,
qplugins_params,
qplugins_base,
Serv_Menu,
System.ImageList,
Vcl.ImgList,
Vcl.ExtCtrls,
Vcl.StdCtrls; type
// 定义一个菜单服务
TQMenuService = class(TQService, IQMenuService)
protected
// 注册, 在接口IQMenuService中定义,子类来实现它
function RegisterMenu(const APath: PWideChar; AOnEvent: IQNotify; ADelimitor: WideChar)
: IQMenuItem;
// 注销,在接口IQMenuService中定义,子类来实现它
procedure UnregisterMenu(const APath: PWideChar; AOnEvent: IQNotify; ADelimitor: WideChar);
public
constructor Create; overload;
destructor Destroy; override;
end; // 菜单服务的部分接口
TQMenuItem = class(TInterfacedObject, IQMenuItem)
private
protected
FMenuItem: TMenuItem;
FOnClick: IQNotify;
FName: string;
// 菜单接口
FParams: IQParams;
function GetCaption: PWideChar;
procedure SetCaption(const S: PWideChar);
function GetHint: PWideChar;
procedure SetHint(const S: PWideChar);
function SetImage(AHandle: HBITMAP): Boolean;
function getParams: IQParams;
procedure setParams(AParams: IQParams);
function GetParentMenu: IQMenuItem;
procedure DoClick(ASender: TObject);
public
constructor Create(AMenuItem: TMenuItem; AOnClick: IQNotify); overload;
property Name: string read FName write FName;
property Params: IQParams read getParams write setParams;
end; TForm_Main = class(TForm)
MainMenu1: TMainMenu;
ilMenus: TImageList;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{Private declarations}
public
{Public declarations}
end; var
Form_Main: TForm_Main; implementation uses
qstring; var
MainMenu: TMainMenu; // 全局菜单变量
{$R *.dfm}
{TMenuService} // 创建菜单服务
constructor TQMenuService.Create;
begin
inherited Create(IQMenuService, 'QMenuService');
end; // 释放
destructor TQMenuService.Destroy;
begin
inherited;
end; // 注册菜单
function TQMenuService.RegisterMenu(const APath: PWideChar; AOnEvent: IQNotify;
ADelimitor: WideChar): IQMenuItem;
var
p: PWideChar;
AName: QStringW;
AMenu, ANewMenu: TMenuItem;
AItem: IQMenuItem;
AChildMenu: TQMenuItem;
AIdx: Integer;
// 识别是哪个菜单
function IndexOfMenuName: Integer;
var
I: Integer;
AIntf: IQMenuItem;
begin
Result := -;
//
for I := to AMenu.Count - do
begin
// 将菜单保存的Tag转成菜单条目
AIntf := IQMenuItem(Pointer(AMenu.Items[I].Tag));
if (InstanceOf(AIntf) as TQMenuItem).Name = AName then
begin
Result := I;
Break;
end;
end;
end; begin
// 菜单
AMenu := Form_Main.MainMenu1.Items;
// 如果菜单文本存在
p := PWideChar(APath); // '/文件/ShowForm','/文件/Exit'
while p^ <> # do
begin
// 字符串分解
AName := DecodeTokenW(p, [ADelimitor], #, True); // 文件,ShowForm
// 存在
if Length(AName) > then
begin
// 菜单名
AIdx := IndexOfMenuName;
if AIdx = - then
begin
// 创建菜单
ANewMenu := TMenuItem.Create(MainMenu);
if p^ = # then
AChildMenu := TQMenuItem.Create(ANewMenu, AOnEvent)
else
begin
AChildMenu := TQMenuItem.Create(ANewMenu, nil);
end;
AChildMenu.Name := AName;
// 返回添加的子菜单
Result := AChildMenu;
Result._AddRef;
// 创建并添加子条目
ANewMenu.Tag := IntPtr(Pointer(Result));
ANewMenu.Caption := AName;
AMenu.Add(ANewMenu);
AMenu := ANewMenu;
end
else
begin
// 返回
Result := IQMenuItem(Pointer(AMenu.Items[AIdx].Tag));
AMenu := AMenu.Items[AIdx];
end;
end;
end;
end; procedure TQMenuService.UnregisterMenu(const APath: PWideChar; AOnEvent: IQNotify;
ADelimitor: WideChar);
begin
end; // 创建
procedure TForm_Main.FormCreate(Sender: TObject);
begin
// 定义的菜单赋值
MainMenu := MainMenu1;
// 注册一个菜单服务,TQMenuService有多个功能,但IQMenuService只有注册和注销
RegisterServices('/Services/Menus', [TQMenuService.Create(IQMenuService, 'MenuService')]);
// 通过服务接口ID获取服务接口实例
PluginsManager.ById(IQMenuService);
end;
{TQMenuItem} // 创建菜单
constructor TQMenuItem.Create(AMenuItem: TMenuItem; AOnClick: IQNotify);
var
ATemp: Pointer;
begin
inherited Create;
// 关联菜单的单击事件
FMenuItem := AMenuItem;
// 关联菜单单击事件
FMenuItem.OnClick := DoClick;
FOnClick := AOnClick;
end; // 菜单点击事件
procedure TQMenuItem.DoClick(ASender: TObject);
var
AFireNext: Boolean;
begin
AFireNext := True;
//
if Assigned(FOnClick) then
begin
FOnClick.Notify(MN_CLICK, Params, AFireNext); // 演示版本,不传递参数
end;
end; // 菜单接口_读取标题
function TQMenuItem.GetCaption: PWideChar;
begin
Result := PWideChar(FMenuItem.Caption);
end; // 菜单接口_读取Hint
function TQMenuItem.GetHint: PWideChar;
begin
Result := PWideChar(FMenuItem.Hint);
end; // 菜单接口_读取参数
function TQMenuItem.getParams: IQParams;
begin
Result := FParams;
end; // 菜单接口_读取父菜单
function TQMenuItem.GetParentMenu: IQMenuItem;
begin
if Assigned(FMenuItem.Parent) then
Result := IQMenuItem(FMenuItem.Parent.Tag)
else
Result := nil;
end; // 菜单接口_设置标题
procedure TQMenuItem.SetCaption(const S: PWideChar);
begin
FMenuItem.Caption := S;
end; // 菜单接口_设置Hint
procedure TQMenuItem.SetHint(const S: PWideChar);
begin
FMenuItem.Hint := S;
end; // 菜单接口_设置图片
function TQMenuItem.SetImage(AHandle: HBITMAP): Boolean;
var
ABitmap: TBitmap;
AIcon: TBitmap;
AImages: TCustomImageList;
begin
AImages := (FMenuItem.Owner as TMenu).Images;
AIcon := nil;
ABitmap := TBitmap.Create;
try
ABitmap.Handle := AHandle;
// 图标尺寸如果不对,则生成临时的位图,否则ImageList会添加失败
if (ABitmap.Width <> AImages.Width) or (ABitmap.Height <> AImages.Height) then
begin
// 图标
AIcon := TBitmap.Create;
AIcon.SetSize(AImages.Width, AImages.Height);
AIcon.Canvas.Brush.Color := ABitmap.TransparentColor;
AIcon.Canvas.FillRect(Rect(, , AImages.Width, AImages.Height));
AIcon.Canvas.Draw((AImages.Width - ABitmap.Width) shr , (AImages.Height - ABitmap.Height)
shr , ABitmap);
AIcon.Transparent := True;
FMenuItem.ImageIndex := AImages.AddMasked(AIcon, ABitmap.TransparentColor);
end
else
FMenuItem.ImageIndex := AImages.AddMasked(ABitmap, ABitmap.TransparentColor);
finally
FreeAndNil(AIcon);
FreeAndNil(ABitmap);
end;
Result := FMenuItem.ImageIndex <> -;
end; // 菜单接口_设置参数
procedure TQMenuItem.setParams(AParams: IQParams);
begin
FParams := AParams;
end; // 关闭窗口
procedure TForm_Main.FormDestroy(Sender: TObject);
// 销毁菜单
procedure DestoryMenus(AParent: TMenuItem);
var
I: Integer;
AMenu: TMenuItem;
begin
for I := to AParent.Count - do
begin
AMenu := AParent.Items[I];
// 只要菜单Tag存在,这释放销毁
if AMenu.Tag <> then
begin
IQMenuItem(Pointer(AMenu.Tag))._Release;
end;
DestoryMenus(AMenu);
end;
end; begin
// 销毁菜单
DestoryMenus(MainMenu.Items);
end; end.

Serv_Menu代码如下

unit Serv_Menu;

interface

uses
windows,
menus,
QPlugins,
qplugins_base,
qplugins_params; const
// 传递的参数
MN_CLICK = ; type
// 这里只实现了菜单服务的部分接口,如果要实现更多的接口,请自己扩展实现
IQMenuItem = interface
['{83323919-93DE-4D40-87FB-7266AE804D6C}']
function GetCaption: PWideChar;
procedure SetCaption(const S: PWideChar);
function GetHint: PWideChar;
procedure SetHint(const S: PWideChar);
function GetParams: IQParams;
procedure SetParams(AParams: IQParams);
function SetImage(AHandle: HBITMAP): Boolean;
function GetParentMenu: IQMenuItem;
// 菜单的四个属性,标题/Hint/父菜单/参数
property Caption: PWideChar read GetCaption write SetCaption;
property Hint: PWideChar read GetHint write SetHint;
property ParentMenu: IQMenuItem read GetParentMenu;
property Params: IQParams read GetParams write SetParams;
end; IQMenuService = interface
['{667BD198-2F9A-445C-8A7D-B85C4B222DFC}']
// 注册, 在接口中定义,子类来实现它
function RegisterMenu(const APath: PWideChar; AOnEvent: IQNotify; ADelimitor: WideChar = '/'): IQMenuItem;
// 注销, 在接口中定义,子类来实现它
procedure UnregisterMenu(const APath: PWideChar; AOnEvent: IQNotify; ADelimitor: WideChar = '/');
end; implementation end.

菜单窗口代码如下

unit Frm_Menu;

interface

uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
qplugins,
qplugins_params,
qplugins_base,
Serv_Menu,
Vcl.StdCtrls,
Vcl.ExtCtrls; type
TForm_Menu = class(TForm)
Label1: TLabel;
Image1: TImage;
Memo1: TMemo;
private
{Private declarations}
public
{Public declarations}
end; var
Form_Menu: TForm_Menu; implementation {$R *.dfm} type
// 通知响应接口
TShowFormAction = class(TQInterfacedObject, IQNotify)
protected
procedure Notify(const AId: Cardinal; AParams: IQParams; var AFireNext: Boolean); stdcall;
end;
{TShowFormAction} // 通知响应接口
procedure TShowFormAction.Notify(const AId: Cardinal; AParams: IQParams; var AFireNext: Boolean);
var
F: TForm_Menu;
I: Integer;
AName: string;
begin
// 如果有传过来参数,参数名字为'Exit'者退出程序
if Assigned(AParams) and (ParamAsString(AParams.ByName('Name')) = 'Exit') then
Application.Terminate
else
begin
// 创建窗口
F := TForm_Menu.Create(Application);
// Memo输出
with F.Memo1.Lines do
begin
BeginUpdate;
try
// 输出传过来的参数
for I := to AParams.Count - do
begin
// 窗口输出参数
Add(IntToStr(I) + ': ' + AParams[I].Name + '=' + ParamAsString(AParams[I]));
end;
finally
EndUpdate;
end;
end;
// 显示窗口
F.ShowModal;
F.Free;
end;
end; var
// 通知响应接口,关注某个通知时,应实现IQNotify接口,以便接收相关的通知
AShowForm: IQNotify; // 添加菜单相关内容
procedure DoMenuServiceReady(const AService: IQService); stdcall;
var
F: TForm_Menu;
begin
// 菜单回调函数
with AService as IQMenuService do
begin
// 通知响应接口
AShowForm := TShowFormAction.Create;
// 注册菜单
with RegisterMenu('/文件/ShowForm', AShowForm) do
begin
// 窗体信息
Caption := '显示窗体(&S)';
F := TForm_Menu.Create(nil);
// 设置图标
SetImage(TBitmap(F.Image1.Picture.Graphic).Handle);
Params := NewParams([, 'Hello,world']);
F.Free;
end;
// 注册第二个菜单
with RegisterMenu('/文件/Exit', AShowForm) do
begin
Caption := '退出(&X)';
Params := NewParams([]);
// 参数名字为Exit
Params.Add('Name', ptUnicodeString).AsString := NewString('Exit');
end;
end;
end; // initialization在单元中放在文件结尾前,包含用来初始化单元的代码,它在主程序运行前运行并且只运行一次。
initialization // 通知响应接口
AShowForm := nil;
// 等待指定的服务注册,DoMenuServiceReady为服务注册完成时的通知回调
PluginsManager.WaitService(IQMenuService, DoMenuServiceReady); // 在单元中放在 initialization 和 end. 之间,包含了单元退出时的代码。在程序退出时运行并且只运行一次。
finalization // 检查菜单接口是否存在,存在则释放菜单功能
if Assigned(AShowForm) then
begin
// 释放菜单功能
with PluginsManager as IQMenuService do
begin
UnregisterMenu('/File/ShowForm', AShowForm);
end;
AShowForm := nil;
end; end.

002.Delphi插件之QPlugins,菜单插件的更多相关文章

  1. 007.Delphi插件之QPlugins,插件的卸载和重新加载

    效果图如下,可以反复卸载和重新加载.QPlugins这个插件,还没弄明白,摸索着跟着DEMO写 主窗口代码如下 unit Frm_Main; interface uses Winapi.Windows ...

  2. 网站开发常用jQuery插件总结(十)菜单插件superfish

    网站对于菜单的依赖其实并不是很大,我们完全可以不使用菜单来设计网站,显示网站内容.但是如果网站的分类太多,“也许”使用菜单作为网站导航可以使 用户 更方便的寻找内容.superfish插件就是用于实现 ...

  3. 『练手』004 Laura.SqlForever如何扩展 导航栏 工具栏 右键菜单 插件

    004 Laura.SqlForever如何扩展 导航栏 工具栏 右键菜单 插件 导航栏 插件扩展 比如下图的    窗口 > 关闭所有文档    这个导航栏: 在 任何程序集,任何命名空间,任 ...

  4. 003.Delphi插件之QPlugins,菜单插件加强

    相比上一篇的菜单插件,这个在创建和销毁时候,做了增强,同时做了2个菜单对应的窗口 unit MenuSvc; interface uses windows, classes, SysUtils, Gr ...

  5. 014.Delphi插件之QPlugins,MDI窗口

    不知道为什么,这个DEMO编译出来报错,运行不了,在QDAC群里问了一下也没人响应. 效果如下 主程序代码如下 unit Frm_Main; interface uses Winapi.Windows ...

  6. jQuery Wheel 环形菜单插件5种效果演示

    很酷的菜单-jQuery Wheel 环形菜单插件5种效果演示在线预览 下载地址 实例代码 <div class="container"> <!-- Top Na ...

  7. 一款效果精致的 jQuery 多层滑出菜单插件

    想要以用户友好的方式呈现多级菜单是件不容易的事情,而且还要跨浏览器兼容就更难了.Multi-Level Push Menu 这款 jQuery 插件提供了呈现这种菜单的解决方案,能够让你无限制的展示菜 ...

  8. Smint – 用于单页网站制作的 jQuery 导航菜单插件

    Smint 是一款用于实现单页风格网站的 jQuery 导航插件,包含两部分:固定在页面顶部的精美导航条和能够在你点击的时候自动滚动到对应内容的菜单按钮.Smint 使用非常简单,只有一个参数用于设置 ...

  9. 开发一个jQuery插件——多级联动菜单

    引言 开发中,有好多地方用到联动菜单,以前每次遇到联动菜单的时候都去重新写,代码重用率很低,前几天又遇到联动菜单的问题,总结了下,发现可以开发一个联动菜单的功能,以后想用的时候就方便多了.项目中每个页 ...

  10. jQuery插件——多级联动菜单

    jQuery插件——多级联动菜单 引言 开发中,有好多地方用到联动菜单,以前每次遇到联动菜单的时候都去重新写,代码重用率很低,前几天又遇到联动菜单的问题,总结了下,发现可以开发一个联动菜单的功能,以后 ...

随机推荐

  1. Linux下清空文件的3种方法

    1.echo -n > test.log #-n选项可以去掉空行 2.cat /dev/null > test.log 3.truncate -s 0 test.log

  2. 二十 Struts2的标签库,数据回显(基于值栈)

    通用标签库 判断标签:<s:if>.<s:elseif>.<s:else> 循环标签:<s:iterator> 其他常用标签: <s:proper ...

  3. eclipse 快速隐藏所有方法的代码块

    java开发中往往一个class文件中会有很多方法,一个方法的实现代码有时候特别长 我个人就喜欢把无关的方法代码全部隐藏起来,只打开当前需要编辑的代码就行了(不喜欢看导航视图,容易头晕) Ctrl+s ...

  4. 【剑指Offer面试编程题】题目1506:求1+2+3+...+n--九度OJ

    题目描述: 求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 输入: 输入可能包含多个测试样例. 对于每个 ...

  5. kafka 副本同步细节

    图片来源:咕泡学院

  6. text-align:center;在ie7下,父级加上会让block状态的子元素居中。

    text-align:center:在ie7下,父级加上会让block状态的子元素居中.ie8以上不会.

  7. Day11 - M - Nim or not Nim? HDU - 3032

    Nim is a two-player mathematic game of strategy in which players take turns removing objects from di ...

  8. 数字对象NSNumber的使用

    先简述下关于NSNumber的信息 NSNumber的存在就相当于java中的装箱与拆箱.只不过java中的装箱拆箱过程,使用的是对应的类型,比如基本数据类型是int.double类型,装箱时就得对应 ...

  9. 「HNOI2003」消防局的设立

    题目 [内存限制:$256 MiB$][时间限制:$1000 ms$] [标准输入输出][题目类型:传统][评测方式:文本比较] 题目描述 2020 年,人类在火星上建立了一个庞大的基地群,总共有 $ ...

  10. windows系统 安装与配置zabbix-agent

    1.下载安装包 http://192.168.130.150/zabbix/zabbix_agent-4.0.12-win-amd64-openssl.msi 下载包的链接地址 windowszabb ...