
公告 |
四川理工学院信息管理与信息系统专业学生,男,83年5月生。
职业:自由开发者
为人座右铭:做人要厚道
QQ:61951565
VFP群:4001858(已满员),17966472
E-mail:lugreen@163.com
QQ空间:Greeeeeeeeeeeeeen
本博客文章未经作者同意不得转载 |
Blog信息 |
blog名称:Green1Vfp 原创空间 日志总数:35 评论数量:101 留言数量:3 访问次数:443337 建立时间:2004年12月7日 |

| |
[高级技巧]通过 MENUHIT 和 MENUCONTEXT 扩展 VFP 9 IDE(转载) 网上资源, 软件技术
Green1 发表于 2006/2/16 18:03:29 VFP QQ群:4001858 VFP UC团体:4363524 欢迎您的加入 |
通过 MENUHIT 和 MENUCONTEXT 扩展 VFP 9 IDE
发布日期: 12/23/2004 | 更新日期: 12/23/2004
Doug Hennig
VFP 9 中的关键主题之一是可扩展性。您可以通过报告事件扩展报表设计器,以及通过新的 ReportListener 基类扩展报告引擎。而现在,您甚至可以通过捕获系统和快捷菜单点击来扩展 IDE。本月,Doug Hennig 说明了如何用您从未想到过的方式自定义 VFP 9 IDE。
500)this.width=500'>
本页内容
500)this.width=500'>
MENUHIT
500)this.width=500'>
MENUHIT 前景光明
500)this.width=500'>
它有什么好处呢?
500)this.width=500'>
MENUCONTEXT
500)this.width=500'>
替换 Project Manager 快捷菜单
500)this.width=500'>
小结
VFP 长期以来提供了挂钩到交互式开发环境 (IDE) 各方面的方式,并且在每个版本中都得到了改善。FoxPro 2.x 允许我们通过更改系统变量(例如,_GENSCRN 和 _GENMENU)所指向的应用程序的名称,来替换某些 Xbase 组件的功能。VFP 3 为我们提供了生成器,它使我们可以自动执行或简化类和窗体的处理。
VFP 6 添加了项目钩子,从而使我们可以在用户在 Project Manager 中执行某些操作(例如,添加或删除文件)时接收事件。智能感知是 VFP 7 中的主要新功能之一,我们可以通过数据驱动的方法轻松地对该功能进行扩展。VFP 8 添加了可自定义的工具箱和其他有用的 IDE 增强功能。
但是,在 VFP 9 中,Microsoft 已经开启了 IDE 的先例。因为报表设计器可引发报告事件,所以我们可以完全更改其对话框的外观和行为。通过新的 ReportListener 基类,我们可以在报表运行时接收事件,并且可以提供许多功能,例如,标签旋转、动态格式化以及自定义呈现对象(如图形)。不久,我们还能够通过对 BINDEVENT() 进行的增强来响应 Windows 事件;该功能在本文撰写之际尚不可用,但是将为我们提供对 Windows OS 事件(包含那些与 VFP IDE 有关且涉及到具有 hWnd 的窗口的事件)的更好访问方式。
但是,本文讨论的重点是挂钩到系统菜单点击(即,当用户从 VFP 系统菜单栏选择项时)和系统快捷菜单(那些通过右键单击各种位置 — 例如,“Properties”窗口或数据库设计器 — 而显示的菜单)中。我将首先分析如何完成该工作,然后给出一些实际的示例。
MENUHIT
要捕获系统菜单点击,需要在智能感知表中用 TYPE = "S"(对于 "script")、ABBREV = "MENUHIT" 以及要在 DATA memo 字段中执行的代码创建一个脚本记录。(智能感知表由 _FOXCODE 系统变量指示;默认情况下,它是 HOME(7) 目录中的 FOXCODE.DBF。)该代码应当接受一个对象作为参数。该对象具有几个属性,但是与 MENUHIT 有关的重要属性则显示在表 1 中。
表 1. 传递到 MENUHIT 脚本的参数对象的属性提供了有关菜单点击的信息。
属性
说明
UserTyped
菜单点击所源自的面板的提示。
MenuItem
用户选择的栏的提示。
ValueType
提供给 VFP 的返回值 — 空值意味着继续执行默认行为,而“V”或“L”意味着禁止默认行为(类似于使用类代码中的 NODEFAULT)。
下面是一个简单示例(取自 SimpleMenuHit.PRG,它包含在本月的下载文件中):text to lcCode noshow
lparameters toParameter
wait window 'Pad: ' + toParameter.UserTyped + ;
chr(13) + 'Bar: ' + toParameter.MenuItem
endtext
delete from (_foxcode) where TYPE = 'S' and ;
ABBREV = 'MENUHIT'
insert into (_foxcode) ;
(TYPE, ABBREV, DATA) ;
values ;
('S', 'MENUHIT', lcCode)
上述代码只是显示所选菜单项的面板提示和栏提示,然后继续执行该项的默认行为。这对于测试很适用,但是在“实际的”代码中,您可能希望禁用默认行为,并将其替换为您自己的行为。在这种情况下,请将参数对象的 ValueType 属性设置为“V”或“L”。(如果您想知道为什么有两个可能的值,这是因为这些值由智能感知管理器用于其他目的,并且 VFP 团队决定对 MENUHIT 使用相同的机制。您具体使用哪一个值并没有什么关系。)
下面这个示例取自 DisableExit.PRG,它禁用了“File”菜单中的“Exit”功能(您也许可以使用它来对某位同事开一个愚人节玩笑):text to lcCode noshow
lparameters toParameter
if toParameter.UserTyped = 'File' and ;
toParameter.MenuItem = 'Exit'
toParameter.ValueType = 'V'
endif toParameter.UserTyped = 'File' ...
endtext
delete from (_foxcode) where TYPE = 'S' and ;
ABBREV = 'MENUHIT'
insert into (_foxcode) ;
(TYPE, ABBREV, DATA) ;
values ;
('S', 'MENUHIT', lcCode)
尽管这看起来是正确的,但当您从“File”菜单中选择“Exit”时,VFP 无论如何都将退出。经研究发现,除了将 ValueType 设置为“V”或“L”以外,某些函数还要求 MENUHIT 代码返回 .T. 以阻止默认行为。在 ENDIF 语句之前添加 RETURN .T. 可防止“Exit”关闭 VFP。
另一个 MENUHIT 问题如下所述:如果您要使用 VFP 的本地化版本(例如,德语版本),该怎么办呢?在这种情况下,检查“File”和“Exit”时不会正常工作,这是因为它们不是出现在菜单中的提示。如果您要将 MENUHIT 脚本分发给其他 VFP 开发人员,则您必须知道这个问题,或者必须让您的目标受众知道这个问题。
500)this.width=500'>返回页首
MENUHIT 前景光明
尽管我们正在讨论将 MENUHIT 脚本分发给其他人的主题,但如果您具有一个能够出色完成任务的脚本,而其他某个人也具有这样的脚本,并且您希望同时使用这两个脚本,那么该怎么办呢?问题在于只能有一个 MENUHIT 记录(如果存在一个以上的记录,则 VFP 将使用智能感知表中的第一个记录)。因此,我认为更好的做法是让 MENUHIT 记录委托给其他某个对象,而不是直接执行所需的 IDE 自定义。
完成该任务的最简单方式是向智能感知表中添加其他记录,并且让 MENUHIT 记录使用它们来执行实际的任务。尽管可以任意选择,但我觉得带有 TYPE = “M” 的记录应该适合该需要,因为 “M” 代表“菜单”,并且不是该表中当前使用的记录类型。例如,要处理 Exit 功能,可以添加一个带有 TYPE = “M”、ABBREV = “Exit” 的记录,以及要在 DATA 中执行的代码。
要使该方式可行,我们需要一个“标准的”MENUHIT 记录。与该记录对应的代码在表中查找带有 TYPE = “M” 并且将 ABBREV 设置为用户所选栏的提示的记录,如果该记录存在,则在 DATA memo 中执行代码。以下为处理该操作的代码:lparameters toParameter
local lnSelect, ;
lcCode, ;
llReturn
try
lnSelect = select()
select 0
use (_foxcode) again shared order 1
if seek('M' + padr(upper(toParameter.MenuItem), ;
len(ABBREV)))
lcCode = DATA
endif seek('M' ...
use
select (lnSelect)
if not empty(lcCode)
llReturn = execscript(lcCode, toParameter)
if llReturn
toParameter.ValueType = 'V'
endif llReturn
endif not empty(lcCode)
catch
endtry
return llReturn
运行 StandardMenuHit.PRG 以将该代码安装到智能感知表中。关于该代码,有几个有趣的事情。首先,我通常使用 ORDER <tag name> (而不是 ORDER <N>)来设置表的顺序。但是,智能感知表不同寻常:如果您打开该表并使用 ATAGINFO() 来检索有关该表索引的信息,则将看到有两个标记,并且它们都标记为主标记且不具有标记名称。因此,您必须使用 ORDER 1 或 ORDER 2 来设置该表的顺序。
第二件事情是,将代码包装在 TRY 结构中以防止任何错误,例如,在打开表时出现的问题或者可能存在于另一个记录的代码中的错误。
第三个问题是,该代码不检查用户的菜单选择所源自的面板的提示,而只检查栏提示。这是因为我出于性能原因而决定进行 SEEK,并且所使用的标记是 UPPER(TYPE + ABBREV)。由于这是一个小表,因此您或许可以将面板提示放到 EXPANDED 列中,并使用 LOCATE FOR TYPE = “M” AND ABBREV = toParameter.MenuItem AND EXPANDED = toParameter.UserTyped 来确保找到确切的记录,而不会产生什么后果。
到本期截止时间为止,Microsoft 尚未决定在默认情况下是否有这样一个“标准的”MENUHIT 记录将存在于智能感知表中。如果没有,则他们可能会提供一种添加此类记录的简单方式,例如,通过解决方案示例添加。(解决方案示例是用于展示各种 VFP 功能的示例,并且可以通过任务窗格管理器轻松地访问。)
下面讨论最后一个 MENUHIT 问题:如果 DATA memo 字段中的代码具有任何编译错误,您将不会获得错误信息 — VFP 将简单地忽略该代码并使用默认行为。当然,这可能是令人讨厌的,因为您可能无法完全确定代码为什么没有执行您期望的操作。
500)this.width=500'>返回页首
它有什么好处呢?
对,这样我们就可以挂钩到 VFP 菜单项中。我们可以使用该机制做什么呢?
我所想到的第一件事情就是替换“New Property”和“New Method”对话框。因为我们现在能够保留自定义属性和方法的大小写以进行显示(请参阅我的 2004 年 6 月刊 FoxTalk 2.0 专栏“MemberData and Custom Property Editors”),所以我宁愿让 VFP 使用我输入到“New Property”或“New Method”对话框中的大小写,以避免启动 MemberData 编辑器并在其中更改大小写的额外步骤。而且,由于我必须单击“Add”按钮来添加新的属性或方法,然后单击“Close”按钮来关闭对话框,因此这总是让我感到苦恼。因为我通常一次只添加一个属性或方法,所以希望看到既能添加成员又能关闭对话框的按钮。
由于 MENUHIT 方法现在允许我们捕获“Form”和“Class”菜单中的“New Property”和“New Method”项,因此我们应当能够创建完全按照我们所希望的方式操作的替换对话框。图 1 显示了这样一个对话框,它具有下列功能:
•
它可以自动更新 _MemberData 属性(如果需要,则添加该属性),因此将使用为新成员输入的大小写(甚至还将使用为访问和分配方法输入的大小写 — 如果也创建它们的话),并且该成员将显示在“Favorites”选项卡上(如果在对话框中启用了该选项)。
•
它是非模式对话框。这意味着,您可以使其保持打开状态,添加一些属性或方法,切换到其他界面元素,返回以及添加更多的成员。
•
它是可停靠的:请尝试通过“Properties”窗口来停靠它。这非常棒!
•
它的大小可以调整,并且可以将它的大小和位置保存到资源 (FOXUSER) 文件中。
•
它具有一个“Add & Close”按钮,能够通过一次单击执行这两项任务。
•
属性的默认值会基于该属性的数据类型自动设置为某个值(如果您使用匈牙利表示法)。例如,lTest 是逻辑类型,因此默认值为 .F.。对于 nTest,默认值为 0。
•
它隐藏而不是禁用不适用的控件。由于这个对话框同时用于类设计器和窗体设计器中的“New Property”和“New Method”,因此对于给定实例,某些选项可能不适用。
•
当您输入名称而不是单击“Add”或“Add & Close”时,它将拒绝接受无效名称。
•
“Add”按钮仅在您输入名称时启用。
我们将不会考察构成 NewPropertyDialog.APP 的代码。它并不复杂;在核心部分,它调用在类设计器或窗体设计器中编辑的对象的 AddProperty 和 WriteMethod 方法,以便添加新的属性或方法。这两个方法都接受 VFP 9 中的两个新参数:可见性(1 代表公用,2 代表受保护,3 代表隐藏)以及新属性或方法的说明。
要使用替换对话框,只须“执行”NewPropertyDialog.APP 以便在智能感知表中注册它。它会添加前面讨论的 MENUHIT 记录以及两个在 DATA 中具有相同代码的记录(一个用于“New Property”,另一个用于“New Method”),以便使用 NewPropertyDialog 类。在该代码中,“<path>”表示系统中 NewPropertyDialog.APP 的路径(该应用程序在运行时自动插入正确的路径)。lparameters toParameter
local llReturn, ;
llMethod, ;
llClass
try
llMethod = toParameter.MenuItem = 'New Method'
llClass = toParameter.UserTyped = 'Class'
release _oNewProperty
public _oNewProperty
_oNewProperty = newobject('NewPropertyDialog', ;
'NewProperty.vcx', ;
'NewPropertyDialog.app', llMethod, llClass)
_oNewProperty.Show()
llReturn = .T.
catch
endtry
return llReturn
现在,当您从“Form”或“Class”菜单中选择“New Property”或“New Method”时,您将获得新的对话框而不是原有的那个。
500)this.width=500'>返回页首
MENUCONTEXT
除了挂钩到 VFP 系统菜单中的选择以外,您还可以捕获系统快捷菜单,例如,当您右键单击“Properties”窗口时显示的那个快捷菜单。这是使用与 MENUHIT 相同的机制完成的,不同之处在于,智能感知表中的 ABBREV 字段包含“MENUCONTEXT”而不是“MENUHIT”。
与 MENUHIT 一样,MENUCONTEXT 记录的代码应当接受一个对象参数。在这种情况下,有三个需要注意的属性:Item — 显示在快捷菜单中的提示数组;ValueType — 具有与在 MENUHIT 中相同的用途;MenuItem — 快捷菜单的 ID。
MenuItem 的一些值为:对应于“Command”窗口中的快捷菜单的“24446”,对应于 Project Manager 的快捷菜单的“24447”,以及对应于“Properties”窗口的快捷菜单的“24456”。我是如何发现这些值的?我用只显示 toParameter.MenuItem 值的代码创建了一个 MENUCONTEXT 记录,然后右键单击各个位置。与 MENUHIT 一样,您应当将 ValueType 设置为“V”或“L”,并且返回 .T. 以禁止原生行为(显示快捷菜单)。
MENUCONTEXT 的使用不像 MENUHIT 那么容易,这有多种原因。首先,更改 Item 数组的内容不会更改菜单的显示。例如,下面的代码似乎不会对快捷菜单产生任何作用:lparameters toParameter
toParameter.Items[1] = 'My Bar'
类似地,以下代码也不会在菜单中产生新栏:lparameters toParameter
lnItems = alen(toParameter.Items) + 1
dimension toParameter.Items[lnItems]
toParameter.Items[lnItems] = 'My Bar'
其次,当某个栏被选定时,没有办法更改所发生的事情。例如,您可能希望“Command”窗口快捷菜单的“Properties”栏显示您的对话框而不是原生对话框。问题在于,参数对象的属性中没有一个指定当菜单项被选择时应该执行的操作 — 该内容被内置到菜单本身中。
因此,看起来我们更改菜单的唯一方式就是使用我们自己的快捷菜单,并随后禁止原生行为。但是,该方法有一个新问题:由于您必须用自己的菜单替换整个菜单,因此当您的一些菜单项被选择时,您如何告诉 VFP 使用原生行为?在撰写本文之际,似乎没有任何能够完成该任务的办法,因此我怀疑 MENUCONTEXT 将只在极少数情况下使用。
500)this.width=500'>返回页首
替换 Project Manager 快捷菜单
即使存在上述问题,我们还是要尝试一个示例。在我的 2000 年 5 月刊 FoxTalk 文章“Zip it, Zip it Good”中,我介绍了一个能够将所有基于表的文件(例如,VCX、SCX 和 FRX 文件)打包到一个项目中的实用工具,以及另一个能够将某个项目中引用的所有文件压缩到一个 ZIP 文件中的实用工具。在该文章中,这些实用工具是从特定于项目的项目挂钩所显示的工具栏中调用的。让我们改而将它们放到将供任何项目使用的快捷菜单中。
MENUCONTEXT 具有与 MENUHIT 相同的潜在冲突问题,因此我们将使用相同的解决方案:一个简单地委托给特定快捷菜单的另一个记录的“标准”记录。实际上,该标准记录的代码与 MENUHIT 记录的代码完全相同(运行 StandardMenuContext.PRG 来创建 MENUCONTEXT 记录)。但是,我们不是将栏提示放到处理程序记录的 ABBREV 字段中,而是放置菜单 ID。(您可能希望将菜单用途 — 例如“命令窗口”— 放到 EXPANDED 字段中,以表明该记录的用途。)
在创建 MENUCONTEXT 记录之后,我用 TYPE = “M”、ABBREV = "24447"(Project Manager 的快捷菜单的 ID)和 DATA 中的以下代码创建了一个记录:lparameters toParameter
local loMenu
loMenu = newobject('_ShortcutMenu', ;
home() + 'ffc\_menu.vcx')
loMenu.AddMenuBar('\< loMenu.ShowMenu() plain?) ?{CTRL+J}? ?keyboard ; \
上述代码使用 FFC(FoxPro 基础类)_ShortcutMenu 类来提供快捷菜单。我在 1999 年 2 月刊 FoxTalk 文章“A Last Look at the FFC”中讨论了该类。运行 ProjectMenu.PRG 以便在智能感知表中创建该记录。
当您右键单击 Project Manager 时,新的自定义菜单将显示三个栏(请参见图 2):Pack Project — 它用当前项目文件的名称调用 PACKPROJ.PRG;Zip Project — 它用当前项目文件的名称调用 ZIPPROJ.PRG;Project Info — 它使用 KEYBOARD 命令来调用“Project”菜单中的“Project Info”对话框。(后一个功能说明,您可以在原生快捷菜单中再现某个栏的功能 — 前提是存在它所调用的系统菜单功能。)
500)this.width=500'>返回页首
小结
通过新的 MENUHIT 和 MENUCONTEXT 功能,较之以前,我们可以更紧密地挂钩到 VFP IDE 中。除了我在本文中介绍的用法以外,我还可以发现“Edit Property/Method”对话框以及“File”菜单中的“New”、“Import”和“Export”功能的替换方法。我肯定您能够想出用不同于 VFP 9 中的方法实现的 IDE 功能;请让我知道您对于这一出色的新功能都有哪些想法。
下载 409HENNIG.ZIP
有关 FoxTalk 和 Pinnacle Publishing 的详细信息,请访问它们位于 http://www.pinpub.com/ 的 Web 站点。
注:这不是 Microsoft Corporation Web 站点。Microsoft 对该 Web 站点的内容不承担责任。
本文是从 FoxTalk 2004 年 9 月刊转载的。版权所有 2004,Pinnacle Publishing, Inc.(除非另行说明)。保留所有权利。FoxTalk 是 Pinnacle Publishing, Inc. 独立发行的产品。未经 Pinnacle Publishing, Inc. 事先同意,不得以任何形式使用或复制本文的任何部分(评论文章中的简短引用除外)。要联系 Pinnacle Publishing, Inc.,请致电 1-800-788-1900。
转到原英文页面 |
|
|