八进制

少年壮志无烟抽

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  244 随笔 :: 0 文章 :: 3241 评论 :: 14 引用

第三篇帖子介绍了定制一个EMF应用程序的基本方法,这一篇让我们来看看怎样定制应用程序的使用界面。没有任何一个界面是万能的,所以定制工作 不可避免,而大多数定制都是通过修改代码来实现的。在实际应用中,同一个需求可能有多种修改方式可以实现,我认为修改涉及的地方(类,方法)越少越有利于 发挥EMF的优势,因此我们应该对EMF生成的代码有一定的了解,这是发挥自己创造力的基础。

下面有几个常见的需求,通过对这些需求的实现,相信你会对EMF应用程序的开发过程有一个更具体的认识。

一、简化模型创建向导

EMF帮我们生成的模型创建向导(菜单File->New->Other->Shop Model)分为两步,第一步要用户输入文件名,对于商店的例子文件名是*.shop格式;第二步用户要选择以哪个对象作为根节点,同时要指定XML文件 的编码方式,商店例子里显然要以商店对象为根节点,所以其实第二步可以省去,以免造成使用者的困扰。

生成的向导类是ShopModelWizard,比起增加一个步骤来,去掉一个步骤要简单得多。首先找到addPages()方法,把最后四句关于initialObjectCreationPage最的语句都注释掉;

/**
 * The framework calls this to create the contents of the wizard.
 * <!-- begin-user-doc -->
 * <!-- end-user-doc -->
 * @generated NOT
 
*/
public void addPages() {
    
    
//initialObjectCreationPage = new ShopModelWizardInitialObjectCreationPage("Whatever2");
    
//initialObjectCreationPage.setTitle(ShopEditorPlugin.INSTANCE.getString("_UI_ShopModelWizard_label"));
    
//initialObjectCreationPage.setDescription(ShopEditorPlugin.INSTANCE.getString("_UI_Wizard_initial_object_description"));
    
//addPage(initialObjectCreationPage);
}

现在因为没有了这个向导页,原来由它提供的信息我们要改为在程序里提供,所以要修改另外两个方法:第一,createInitialModel()方法本来是建立一个用户选择的对象作为根节点的模型,我们把它改为直接建立一个Shop对象;

protected EObject createInitialModel() {
//    EClass eClass = (EClass)shopPackage.getEClassifier(initialObjectCreationPage.getInitialObjectName());
//    EObject rootObject = shopFactory.create(eClass);
    EObject rootObject=shopFactory.createShop();
    
return rootObject;
}

第二,在performFinish()方法里设置文件编码的地方,改为使用UTF-8编码,当然你也可以规定使用其他编码,只是用户不能选择了:

public boolean performFinish() {
    
    options.put(XMLResource.OPTION_ENCODING, 
"UTF-8"/*initialObjectCreationPage.getEncoding()*/);
    
}

因此,这个类里我们总共修改了三个方法,一定记得要把每个方法前的@generated标记删除或修改。现在,用户只要简单的指定文件名后就可以Finish了,如图1所示。


图1 向导的最后一页

二、改造大纲视图的显示

对于EMF来说,在应用程序模型的根节点上还有两层,分别是Resource和ResourceSet,在商店的例子里,Category的父节点 是Shop,Shop的父节点是Resource(具体来说是XMLResource),Resource的父节点是ResourceSet,它们之间都 是多对一的关系。缺省情况下,大纲视图里显示的是完整的ResourceSet树(根节点不显示),显示出的最上层节点是“platform: /resource/Project3/My.shop”,它代表一个Resource,这个Resource(通过这个URI)指向保存着模型信息的 XML文件My.shop。对于使用者来说,这个节点显示在这里没有什么意义,用户看到的根节点应该是.shop文件里保存的Shop对象,见图2的对 比。


图2 在大纲视图里隐藏最上层节点

那么该修改哪些代码来实现这个需求呢?我们想到大纲视图里的内容是从ShopEditor的getAdapter()方法里得到的,通过查看 ShopEditor的getAdapter()方法发现名为getContentOutlinePage()的方法负责产生大纲模型,在这个方法里,变 量contentOutlineViewer是对大纲视图里的树控件的包装对象,它的输入(Input)是 editingDomain.getResourceSet(),我们要把它的输入改成ResourceSet的第一个Resource,修改后的代码如 下:

public IContentOutlinePage getContentOutlinePage() {
    
    
//contentOutlineViewer.setInput(editingDomain.getResourceSet());
    contentOutlineViewer.setInput(editingDomain.getResourceSet().getResources().get(0));
    
}

你可能会问,那么从Resource怎样得到Shop对象呢?很简单,(Shop)yourResource.getContents().get (0)即可,有兴趣的话你可以试试把大纲视图的输入设为Shop对象会看到什么。最后说一次不要忘记修改@generated,以后不再提醒了。

三、移除编辑器里多余的Tab页

EMF生成的Editor为我们提供了六个Tab页,其主要目的是向我们演示如何以各种方式展示数据(例如在大纲视图里选择一个Category对 象,通过Parent页里可以很容易的看到前面说过的Category->Shop->Resource->ResourceSet关 系),在实际的应用里一般不会用到全部这些页,下面我们就只保留Table页而移除其他五页,利用大纲和Table页的组合,实现类似Windows资源 管理器的界面。

编辑器里的页面在createPages()方法里被添加,它虽然很长但EMF在这个方法里生成了不少注释,每段代码的作用都很明显,只要把我们不 需要的那五段注释掉即可。现在把程序运行起来,打开一个模型文件,稍微调整一下布局把大纲视图放在编辑器的旁边,如图3所示,有点资源管理器的样子了吧。


图3 和资源管理器类似的布局

但是很遗憾,现在的表格里只有两列,而且两列里显示的内容是相同的。按照资源管理器的设计,当用户在大纲视图里选择一个对象时,表格中应该显示该对象的子对象详细信息的列表,现在子对象列表已经有了(表格里每一行就是一个子对象),让我们做一些修改以显示详细信息。

首先增加一个表格列,还是在ShopEditor的createPages()方法里修改,搜索一下TableColumn很容易找到应该修改的位 置。新的三列标题分别为“Name”、“Children”和“Description”,其中Children列里显示子对象的数目。

public void createPages() {
    
    TableColumn objectColumn 
= new TableColumn(table, SWT.NONE);
    layout.addColumnData(
new ColumnWeightData(3100true));
    objectColumn.setText(
"Name");
    objectColumn.setResizable(
true);

    TableColumn childrenColumn 
= new TableColumn(table, SWT.NONE);
    layout.addColumnData(
new ColumnWeightData(2100true));
    childrenColumn.setText(
"Children");
    childrenColumn.setResizable(
true);

    TableColumn descColumn 
= new TableColumn(table, SWT.NONE);
    layout.addColumnData(
new ColumnWeightData(2100true));
    descColumn.setText(
"Description");
    descColumn.setResizable(
true);
    
}

如果现在运行程序,看到的将是三列,但内容仍然是相同的。表格里显示的内容是由生成的XXXItemProvider类决定的,例如对于一个 Category对象在表格或树控件里怎样展示是由CategoryItemProvider来负责,你可以把它看作是JFace里的 ContentProvider加上LabelProvider,这些XXXItemProvider都被放在.edit项目里了。EMF生成的 CategoryItemProvider没有实现ITableItemLabelProvider接口,所以缺省情况下不能支持表格的展示(能够显示, 但每列的内容相同),所以我们要对代码进行一些修改,在CategoryItemProvider实现的接口列表里增加 ITableItemLabelProvider,并实现它的两个方法,修改后的代码如下:

public class CategoryItemProvider
    
extends ItemProviderAdapter
    
implements    
        IEditingDomainItemProvider,    
        IStructuredItemContentProvider,    
        ITreeItemContentProvider,    
        IItemLabelProvider,    
        IItemPropertySource,
        ITableItemLabelProvider{
    
    
    
public Object getColumnImage(Object object, int columnIndex) {
        
return null;
    }

    
public String getColumnText(Object object, int columnIndex) {
        Category category
=(Category)object;
        
switch (columnIndex) {
        
case 0:
            
return category.getName();
        
case 1:
            
return category.getChildren().size()+"";
        
case 2:
            
return "";//Categories don't own descriptions
        default:
            
return "";
        }
    }
    
}

现在只差一步就完成了,如果你注意看过ShopEditor的createPages()方法里定义TableViewer的代码,会发现这个 TableViewer的ContentProvider和LabelProvider都是一个 AdapterFactoryContentProvider对象,这个对象会把TableViewer对getText()、getElements ()的请求转发到XXXItemProvider上;转发之前它要得到这个XXXItemProvider,这是通过 ShopItemProviderAdapterFactory的adapt()方法实现的,而 ShopItemProviderAdapterFactory维护了一个supportTypes列表,只有注册到这个列表中的类型才能被adapt。 这里出现了不少新内容,可能不那么容易理解,没有关系,因为在以后的帖子里会专门介绍到它们,现在只要记住需要把我们新实现的接口类型注册到 ShopItemProviderAdapterFactory的supportTypes里即可,具体的方法是修改它的构造方法,如下所示:

public ShopItemProviderAdapterFactory() {
    supportedTypes.add(IEditingDomainItemProvider.
class);
    supportedTypes.add(IStructuredItemContentProvider.
class);
    supportedTypes.add(ITreeItemContentProvider.
class);
    supportedTypes.add(IItemLabelProvider.
class);
    supportedTypes.add(IItemPropertySource.
class);    
    supportedTypes.add(ITableItemLabelProvider.
class);//Added to support table
}

现在,表格里显示的Category对象已经按我们的要求列出其他信息了,如图4所示,Description列是空白因为Category没有这 个属性。我们还应该修改ProductItemProvider以展示产品的详细信息,方法和修改Category是类似的,而且增加 supportTypes的步骤不须要重复做,所以更加简单了,不妨就留作练习。


图4 经过定制的表格

经过上面的这些定制,我们就实现了应用程序从EMF缺省界面到资源管理器风格界面的转换,虽然文字比较多,但掌握以后这个过程是相当快速的,而且其他的定制也是同样的思路。点此下载代码

posted on 2005-12-23 17:09 八进制 阅读(4180) 评论(6)  编辑 收藏 网摘 所属分类: EclipseEMF

评论

不错,不错,哈哈,暂时还能跟上
  回复  引用    

请问直接修改代码后,直接运行不就行了,为什么非要把@generated去掉?我初学EMF,很多地方不懂,还请指教。谢谢!
  回复  引用    

#3楼[楼主] 2006-07-03 23:44 八进制      
修改代码后是可以直接运行了,但如果不去掉@generated标记,下次生成代码时这些修改会被覆盖掉。
  回复  引用  查看    

我如果改成@generated NO后,下次模型不变的话,代码不会被覆盖掉,如果模型变了的话,这时候的覆盖算法是什么?究竟是全部重写,还是保留原样不动?如果保留原样不动的话,是否适合新的模型呢?
  回复  引用    

#5楼[楼主] 2006-07-22 08:48 八进制      
在一个类里,EMF只选择性的覆盖标记为@generated的元素(成员变量、方法)。
  回复  引用  查看    

我下载了你的代码后,通过ecore生成emf project,运行后创建Category后,一点击就出错,log如下:
!ENTRY org.eclipse.jface 4 2 2007-01-02 00:21:29.546
!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.jface".
!STACK 0
java.lang.ExceptionInInitializerError
at shop.impl.ShopFactoryImpl.createProduct(ShopFactoryImpl.java:134)
at shop.provider.CategoryItemProvider.collectNewChildDescriptors(CategoryItemProvider.java:187)
at org.eclipse.emf.edit.provider.ItemProviderAdapter.getNewChildDescriptors(ItemProviderAdapter.java:645)
at org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain.getNewChildDescriptors(AdapterFactoryEditingDomain.java:630)
at shop.presentation.ShopActionBarContributor.selectionChanged(ShopActionBarContributor.java:269)
at shop.presentation.ShopEditor.setSelection(ShopEditor.java:1598)
at shop.presentation.ShopEditor$10.selectionChanged(ShopEditor.java:832)
at org.eclipse.jface.viewers.Viewer$2.run(Viewer.java:162)
at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:37)
at org.eclipse.core.runtime.Platform.run(Platform.java:843)
at org.eclipse.ui.internal.JFaceUtil$1.run(JFaceUtil.java:44)
at org.eclipse.jface.util.SafeRunnable.run(SafeRunnable.java:149)
at org.eclipse.jface.viewers.Viewer.fireSelectionChanged(Viewer.java:160)
at org.eclipse.jface.viewers.StructuredViewer.updateSelection(StructuredViewer.java:1976)
at org.eclipse.jface.viewers.StructuredViewer.setSelection(StructuredViewer.java:1577)
at shop.presentation.ShopEditor$9.run(ShopEditor.java:748)
at shop.presentation.ShopEditor.setSelectionToViewer(ShopEditor.java:752)
at shop.presentation.ShopEditor$8.run(ShopEditor.java:701)
at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:123)
at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3325)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2971)
at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:1914)
at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1878)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:419)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:95)
at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:78)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:177)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.eclipse.core.launcher.Main.invokeFramework(Main.java:336)
at org.eclipse.core.launcher.Main.basicRun(Main.java:280)
at org.eclipse.core.launcher.Main.run(Main.java:977)
at org.eclipse.core.launcher.Main.main(Main.java:952)
Caused by: java.lang.IllegalArgumentException: The value '255,90,150' is invalid.
at org.eclipse.emf.ecore.impl.EFactoryImpl.createFromString(EFactoryImpl.java:420)
at shop.impl.ShopFactoryImpl.createRGBFromString(ShopFactoryImpl.java:174)
at shop.impl.ShopFactoryImpl.createFromString(ShopFactoryImpl.java:86)
at shop.impl.ProductImpl.<clinit>(ProductImpl.java:172)
... 40 more

  回复  引用    

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 303508


历史上的今天:
2004-12-23 [转载]健康食品和垃圾食品
2004-12-23 安装了一个Eclipse 3.0

相关文章:

相关链接: