运行之后的效果如下,

图一

图二

主界面代码如下

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. postman 使用post方式提交参数值

    参考:https://www.cnblogs.com/haoxuanchen2014/p/7771459.html

  2. 【PAT甲级】1031 Hello World for U (20 分)

    题意: 输入一个字符串长度为5~80,以'U'型输出,使得底端一行字符数量不小于侧面一列,左右两列长度相等. trick: 不把输出的数组全部赋值为空格为全部答案错误,可能不赋值数组里值为0,赋值后是 ...

  3. MySQL的默认隔离级别的实现依赖于MVCC和锁,准确点说就是一致性读和锁。

    MySQL的默认隔离级别的实现依赖于MVCC和锁,准确点说就是一致性读和锁.

  4. [aac @ ...] more samples than frame size (avcodec_encode_audio2)

    在用FFmpeg对音频进行编码的时候报如下错误: [aac @ 000001cfc2717200] more samples than frame size (avcodec_encode_audio ...

  5. redhat 7.6 apache 服务简单安装-01

    rpm -qa | grep httpd         //该命令查看apache是否安装,下面图片是已安装,未安装不会显示任何内容 yum install   httpd   -y        ...

  6. 获取美拍视频的链接--JS分析

    美拍链接:https://www.meipai.com/ 找到视频链接的标签,源代码中没有这个div 通过Fiddler抓包,找到class="mp-h5-player-layer-vide ...

  7. Dubbo+zookeeper部署到tomcat上注意事项,遇到的问题,闪退,运行报错等

    需要下载工具zookeeper-3.4.14.tar.gz,dubbo-2.5.x.zip,apache-tomcat-8.5.47-windows-x64.zip这些官网都可以先下载到 1.最新的z ...

  8. css 盒子模型应用

    盒子模型应用 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> < ...

  9. python设置编码

    import sys sys.getdefaultencoding() #看到默认编码是'ascii' #通常需要的是使用utf8编码,需要这样做: reload(sys) sys.setdefaul ...

  10. 「译」forEach循环中你不知道的3件事

    前言 本文925字,阅读大约需要7分钟. 总括: forEach循环中你不知道的3件事. 原文地址:3 things you didn't know about the forEach loop in ...