快速业务通道

EMF介绍系列(六、自定义命令) - 编程入门网

作者 佚名技术 来源 NET编程 浏览 发布时间 2012-06-17

EMF介绍系列(六、自定义命令)

时间:2011-04-30 博客园 bjzhanghao

EMF生成的应用程序里,用户的发出的每一条命令都是可以撤销(Undo)的,例如修改了 产品的价格,按一下撤销按钮就能恢复原来的价格,当然还可以通过重做(Redo)再回到新 的价格。为了实现这个功能,应用程序里维护了一个用于存放命令的类似栈的数据结构 (CommandStack),每一条执行过的命令都被存放在那里,需要撤销时取出最近一条命令进 行撤销。这个数据结构是由EditingDomain对象负责维护的, EditingDomain相当于编辑模型 时的环境。

在EMF里命令框架实际上可以分为两大部分,一部分是与模型无关的通用命令,另一部分 是.Edit命令,后者是建立在前者的基础之上的。EMF对模型的任何修改都是通过命令完成的 ,例如当用户在属性视图里修改一个对象的属性时,会生成一个新的SetCommand实例,然后 执行它的execute ()方法,这个方法里对模型进行修改(实际上是通过doExecute()方法), 成功执行完成后这个命令被放入命令栈以便撤销时使用。

通用命令可以完全脱离EMF使用,也就是说,这个命令框架可以应用到任何需要命令框架 的应用程序中,包括非EMF应用程序。它位于 org.eclipse.emf.common.command包里,其中 Command接口定义了什么是“命令“,一个命令具有execute()、 undo()和redo()等方法,还 有canExecute()和canUndo()方法用于判断命令是否可被执行或撤销(考虑到资源消耗,有些 命令可能设计为不可撤销更合理)。另一个重要的接口是前面提到过的CommandStack,它的 作用是保存所有命令,可以通过 addCommandStackListener()方法注册监听器来观察 CommandStack的状态变化。CompoundCommand接口可以把多个命令按顺序包装成一个组合命令 ,它具有原子性,类似数据库里事务(Transaction)的概念,只有所有命令都可执行时这个 组合命令才可执行,撤销也是如此。

EMF在.Edit框架提供了针对EMF模型编辑所需要的一些命令(位于 org.eclipse.emf.edit.command包),例如 SetCommand用于修改对象的属性, CreateChildCommand的作用是创建一个子元素,还有MoveCommand、 CopyCommand和 CutToClipboardCommand等等。这些命令都实现Command接口,并且大部分继承自 AbstractOverrideableCommand这个抽象类,它给我们带来的影响是在Command接口里的方法 名前面都加了一个do,比如 execute()变为doExecute()、canUndo()变为doCanUndo()等等, 我们在扩展这些.Edit命令时要覆盖doXXX方法。.Edit命令是通过反射的方式来修改模型的。

EMF提供的这些命令为我们完成基本的模型编辑功能,多数情况下直接使用它们就可以了 ,但有时通过自定义的命令可以实现一些特别的需求。举个例子来说,在网上商店的例子里 ,假设要求产品的价格只精确到小数点后两位,那么我们要在用户输入新价格以后立刻对这 个数值进行四舍五入处理,这个操作就可以利用自定义命令完成。因为利用了.Edit提供的类 ,所以一般我们应该扩展.Edit命令,具体来说就是SetCommand。

首先通过继承SetCommand创建我们的SetPriceCommand,在这个方法里覆盖doExecute()方 法,SetCommand 里有很多可供利用的环境变量,我们要用到的是owner和value这两项,前者 是要修改的对象,在这里是产品对象,后者是属性的新值,在这里也就是新价格。所以我们 的SetPriceCommand可以像下面这样写(为了使代码最简,我们直接把EObject类型转换为 Product类型,这样就不需要用反射的方式了):

public class SetPriceCommand extends SetCommand {    public SetPriceCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, Object value) {      super(domain, owner, feature, value);    }    public void doExecute() {      Product product = (Product) owner;      double newPrice = ((Double) value).doubleValue();      newPrice = Math.max(0, newPrice);//New price value must >= 0      newPrice=Math.round(newPrice*100)/100d;//Max fraction digits is 2        product.setPrice(newPrice);    } }

要让这个自定义命令生效,必须在ProductItemProvider里覆盖createSetCommand()方法 ,因为这个方法缺省是返回SetCommand的,我们要在这里做一个判断:如果修改的是价格属 性,就返回我们自定义的这个命令,如下所示:

protected Command createSetCommand(EditingDomain domain, EObject owner, EStructuralFeature feature, Object value,      int index) {    if (feature == ShopPackage.eINSTANCE.getProduct_Price())      return new SetPriceCommand(domain, owner, feature, value);    return super.createSetCommand(domain, owner, feature, value, index); }

这样当用户在属性页里改价格属性时,就会调用我们的SetPriceCommand了。顺便说一句 ,在GEF里也有类似的EditDomain和Command,只是GEF里的Command一般通过EditPolicy的 createXXXCommand()方法来创建。因为GEF和EMF的两套Command机制没有实现统一的接口,所 以结合GEF和EMF的时候常会遇到一些问题,需要额外的代码帮助解决,请参考这两处讨论。

最后要说一句,CreateChildCommand有点特殊,它是.Edit命令但不继承 AbstractOverrideableCommand,而且如果想在创建子元素时自动完成一些工作,不应该通过 扩展这个类完成,而应该在 XXXItemProvider的collectNewChildDescriptors()方法里处理 ,这个方法决定每个对象可以创建哪些子元素,你可以修改它的代码以对新建的元素做一些 处理。

凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

分享到: 更多

Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved

地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008

电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134

《中华人民共和国增值电信业务经营许可证》闽B2-20100024  ICP备案:闽ICP备05037997号