基本自定义 > 用户界面自定义 > 在 UI 中呈现信息 > Windchill 客户端体系结构树 > 解决方案
  
解决方案
使用 Windchill 客户端体系结构树以树格式显示 Windchill 业务对象。
必备知识
要实现此目标,需要了解以下内容:
Windchill 客户端体系结构中的操作框架。
JCA 表格组件
数据采集、数据实用程序、GUI 组件。
JSP、JavaScript 和自定义 taglib。
Java 注释
解决方案元素
元素
类型
说明
<您的_ConfigBuilder>
java
在此定义 TreeConfiguration
<您的_DataBuilder>
java
在此定义数据提取服务/命令
<您的_查看_页面>.jsp
jsp
在此定义查看呈现逻辑
<您的>service.properties.xconf
xconf
在此定义 DataUtilities 和服务。
<您的>action.rbInfo
rbInfo
在此定义的操作属性。
<您的>actions.xml
xml
可在此定义操作。
<您的>actionModels.xml
xml
可在此处定义操作模型。
使用异步数据源实现树组件
要使用异步数据源实现树组件,需要实现 TreeDataBuilderAsync 接口。具体类可实现单个方法,如下所示:
void buildNodeData(Object node, ComponentResultProcessor resultProcessor) throws Exception
该方法为每个节点提取子项。从基础结构对该方法进行的第一次调用会始终将第一个自变量作为 TreeNode.RootNode 传递。第一次调用可确保为后续调用提取所有 rootNode。后续调用将始终传递需要提取子节点的 treeNode。
例如,代码应处理类似于以下代码段的内容:
List nodes;
if (node == TreeNode.RootNode){
nodes = getRootNodes();
resultProcessor.addElements(nodes);
}else {
getNodes(node);
nodes = resultProcessor.addElements(nodes);
}
在不使用数据源的情况下实现树组件
要在不使用数据源的情况下实现树组件,需要实现 TreeHandler 接口。TreeHandlerAdapter 可提供默认实现。TreeHandler 是可用于填充树内容的 JAVA 接口。以下方法由 TreeHandler 提供:
ModelContext getModelContext():- 获取处理程序的模型上下文
void setModelContext(ModelContext mc) throws WTException:- 设置与正在构建的树有关的上下文信息,例如描述符、命令 bean 等。在从处理程序请求数据前,应对此内容进行初始化。
List getRootNodes() throws WTException:- 获取树的根节点列表。
Map<Object,List> getNodes(List parents) throws WTException:- 获取给定列表中每个父节点的子节点的映射。这是将针对展开操作调用的唯一方法,因此,该方法必须能够正确初始化处理程序,以便能够回答父项的子项问题。
boolean isExpandNeeded(Object node, int level) throws WTException:- 确定是否需要展开给定节点。默认实现会查看已由标记展开或配置的节点列表的会话状态。
boolean hasChildren(Object node) throws WTException:- 确定给定节点是否应显示展开按钮 (如果处于折叠状态)。覆盖此方法以提高性能或获得自定义行为。默认实现将调用 getNodes,并查看其大小是否大于 0。
void addExpandedNode(Object node) throws WTException:- 将节点添加到展开的节点列表中。系统会返回此列表,并在会话状态下对其进行使用,以记录树展开的内容。
由于树的递归特性会导致对数据库执行许多查询,因此需要认真对其进行创建。Treehandler 类可将树状态信息另存为类的专用属性,并针对每次 getNodes 调用对其进行重新使用。此外,您可以通过了解用户使用 isExpandNeeded 方法展开哪些树行执行操作。
TreeHandlerAdapter 是一个抽象类,可实现您可以扩展的 TreeHandler。示例如下所示
getRootNodes() 可确定根节点应该是什么。TreeHandler 具有访问 ModelContext (可用于访问所需信息) 的权限。
public List getRootNodes() throws WTException {
NmCommandBean cb = getModelContext().getNmCommandBean();
WTPart part;
NmOid oid = cb.getPageOid();
if (oid == null) {
log.debug("No oid found in request, trying GOLF_CART");
part = getGolfCart();
}else {
if (!oid.isA(WTPart.class)) {
throw new ClassCastException("Expected part, but was: " + oid);
}
part = (WTPart)oid.getRef();
}
if (part == null) {
log.debug("Couldn't get part");
return null;
}

configSpec = ConfigHelper.service.getConfigSpecFor(part);
return Collections.singletonList(part);
}
该方法首先从模型上下文中检索 NmCommandBean,并从中提取主 oid。如果用户未请求 oid,则该方法将使用 GOLF_CART 部件,而不是调用 getGolfCart 方法。getGolfCart 方法仅查询并返回 GOLF_CART。如果用户在请求参数中指定了 oid,则该方法会进行检查以确保 oid 为 WTPart。否则,该方法会抛出 ClassCastException。如果已提供所需 oid,则该方法会通过借助 getRef 方法从 oid 中检索部件来继续运行。该方法可扩展参考对象,然后可将其强制转换为 WTPart。最后,该方法需要检查从 oid 或 GOLF_CART 中检索的部件是否为 null,如果是,则返回 null 值。使用 ConfigHelper 类为 configSpec 变量分配部件的 ConfigSpec。此帮助程序类包含有助于从主对象获取迭代对象的服务。最后,系统将返回包含部件的不可变列表。
getNodes(List parents) 可为给定列表中的每个父节点获取子节点的映射。无需先调用 getRootNodes() 方法即可直接对其进行调用。例如 (展开操作)。这意味着,此方法必须能够正确初始化处理程序,以便能够回答父项的子项问题。
给定示例会根据 WTPartUsageLinks 生成 WTPart 的层次结构。
public Map<Object,List> getNodes(List parents) throws WTException {
if (configSpec == null) {
configSpec = getDefaultConfigSpec();
}
Map<Object,List> result = new HashMap<Object,List>();
//API returns a 3D array where the 1st dim is the parent parts,
//the 2nd dim is the list of children for a given parent,
//and the 3rd dim is 2 element array w/the link obj at 0 and the child part at 1
Persistable[][][] all_children = WTPartHelper.service.getUsesWTParts(
new WTArrayList(parents), configSpec);
for (ListIterator i = parents.listIterator(); i.hasNext();) {
WTPart parent = (WTPart)i.next();
Persistable[][] branch = all_children[i.previousIndex()];
if (branch == null) {
continue;
}
List children = new ArrayList(branch.length);
result.put(parent,children);
for (Persistable[] child : branch) {
children.add(child[1]);
}
}
log.debug("ParentsToChildren: " + result);
return result;
}
private ConfigSpec getDefaultConfigSpec() throws WTException {
return ConfigHelper.service.getDefaultConfigSpecFor(WTPart.class);
}
需要注册自定义树处理程序。有关如何注册服务的信息,请参阅 <DataUtilities 文档> 的参考。以下是示例树处理程序条目。
<Service context="default" name="com.ptc.core.components.beans.TreeHandler">
<Option requestor="your_object" selector="your_treeHandler"
serviceClass="your_treeHandler_class"
cardinality="duplicate"/>
</Service>
如果要在树中显示非可持续对象,则建议您返回 getRootNodes 和 getNodes 方法的元素。如果您的元素没有可用的有效 "ufid" 值或 "obid" 属性,则需要为每个行对象提供 NmObject。可通过两种方式完成此操作。
1. 创作可扩展 DefaultNmObjectUtility 的 DataUtility,并覆盖其 getTargetObject() 方法,该方法将返回行对象的 NmObject。有关详细信息,请参阅NmObject 实用程序
2. 元素将具有名为 "nmObject" 的属性,其值为 instanceof NmObject
否则,要显示非可持续对象,可以编写从 NmObject 扩展的封套类,并覆盖 getOid() 方法,以返回正确的 NmOid。
过程 - 配置 JCA 树
实现 ComponentConfigBuiler 和 ComponentDataBuilder
定义视图
实现 ComponentConfigBuiler 和 ComponentDataBuilder
有两种方法可供使用:
单独的构建器方法
单一构建器方法
单独的构建器方法
在这种情况下,将为 ComponentConfigBuiler 和 ComponentDataBuilder 编写单独的类。
使用异步数据源
实现 ComponentConfigBuilder
示例:
@ComponentBuilder(value = "custom.treeExample.seperate", type = ComponentBuilderType.CONFIG_ONLY)
public class TreeExampleConfigBuilder extends AbstractComponentConfigBuilder {
private static final String RESOURCE = "com.ptc.carambola.carambolaResource";
@Override
public ComponentConfig buildComponentConfig(ComponentParams params)
throws WTException {
ComponentConfigFactory factory = getComponentConfigFactory();
//Create TreeConfig
TreeConfig tree = factory.newTreeConfig();
// Need to set DataSOurceModes explicitely to DataSourceMode.ASYNCHRONOUS
((JcaTreeConfig) tree).setDataSourceMode(DataSourceMode.ASYNCHRONOUS);
// Set expansion level . Default is TableTreeProperties.ONE_EXPAND
(expand by one level)
((JcaTreeConfig) tree).setExpansionLevel(TableTreeProperties.FULL_EXPAND);

tree.setLabel((new ResourceBundleClientMessageSource(RESOURCE)).getMessage
("PART_TREE_LABEL"));

//Add Columns to the config
tree.addComponent(factory.newColumnConfig(NAME, true));
tree.addComponent(factory.newColumnConfig(NUMBER, true));
//Set column to which expand/collapse norgie should appear
tree.setNodeColumn(NUMBER);
return tree;
}
}
重要的点:
1. 要使用异步数据源,应将数据源模式明确设置为 DataSourceMode.ASYNCHRONOUS。
2. setNodeColumn 方法,可用于选择需要展开/折叠按钮的列。默认值为“名称”列。
实现 ComponentDataBuilder
具体类应实现接口 com.ptc.mvc.components.TreeDataBuilderAsync。有关详细信息,请查看 Java API 文档。
示例:
@ComponentBuilder(value = "custom.treeExample.seperate", type =
ComponentBuilderType.DATA_ONLY)
public class TreeExampleComponentDataBuilder
implements TreeDataBuilderAsync {
@Override
public void buildNodeData(Object node,
ComponentResultProcessor resultProcessor)
throws Exception {
if (node == TreeNode.RootNode) {
List<Object> objects = getRootNodes();
resultProcessor.addElements(objects);
} else {
List nodeList = new ArrayList();
nodeList.add(node);
Map<Object, List> map = getNodes(nodeList);
Set keySet = map.keySet();
for (Object key : keySet) {
resultProcessor.addElements(map.get(key));
}
}
}
private List<Object> getRootNodes(){ // Add code to find RootNodes}
private List<Object> getNodes(List<Object> nodeList){ // Add code to find ChildNodes}
}
通过实现 TreeExpansionStateManager 设置的高级配置
TreeExpansionStateManager 提供以下 API
boolean isExpandNeeded(DefaultMutableTreeNode node, ComponentConfig config,
ComponentParams params) throws WTException;
void addExpandedNode(Object node, ComponentConfig config,
ComponentParams params)
throws WTException;

Set getExpandedOids(ComponentConfig config, ComponentParams params)
throws WTException;
boolean hasChildren(Object node, ComponentResultProcessor resultProcessor,
TreeDataBuilderAsync builder) throws Exception;
List<DefaultMutableTreeNode> getDynamicExpandedNodes(ComponentConfig config,
ComponentParams params) throws WTException;
有关详细信息,请参阅 JavaDoc。
TreeExpansionStateManager 的具体实现由 DefaultTreeExpansionStateManager 提供。可对此实现进行扩展,以根据需要自定义 API,也可直接实现 TreeExpansionStateManager。具体实现可通过使用 ExpansionStateManager 注释插入到构建器中。
示例:
@ComponentBuilder ("folderbrowser_tree")
@ExpansionStateManager (FolderTreeExpansionStateHandler.class)
//
public class FolderTreeBuilder extends AbstractComponentConfigBuilder implements TreeDataBuilderAsync{…}
此处的 FolderTreeExpansionStateHandler 可实现 DefaultTreeExpansionStateManager 并插入到 FolderTreebuilder
这些 API 对于自定义树展开状态行为非常有用。例如,IsExpandNeeded API 可用于覆盖用于确定是否应展开节点的默认逻辑。
不使用数据源
在这种情况下,setDataSourceMethod 不应进行调用或应将 dataSourceMode 设置为 DataSourceMode.SYNCHRONOUS。所有其他配置均与“实现 ComponentConfigBuilder”一节介绍的配置相同。
实现 ComponentConfigBuilder
实现 ComponentDataBuilder
在这种情况下,databuilder 应实现 ComponentDataBuilder 接口。
示例:
@ComponentBuilder(value = "custom.treeExample.seperate", type = ComponentBuilderType.DATA_ONLY)
public class TreeExampleComponentDataBuilder implements ComponentDataBuilder {
@Override
public TreeHandler buildComponentData(ComponentConfig config, ComponentParams params) throws WTException {
return new customTreeHandler();// customTreeHandler should implement TreeHandler or extend TreeHandlerAdapter
}
}
单一构建器方法
在这种情况下,ComponentConfigBuilder 和 ComponentDataBuilder 均由单个类实现。
使用异步数据源
示例:
@ComponentBuilder(value = "custom.treeExample")
public class TreeExampleBuilder implements TreeDataBuilderAsync, ComponentConfigBuilder,ComponentConfigFactoryAware {....}
不使用数据源
示例:
@ComponentBuilder(value = "custom.treeExample")
public class TreeExampleComponentDataBuilder extends AbstractConfigurableTableBuilder{….}
定义视图
componentConfig 的 setView 方法可启用特定构建器的视图设置,该视图应相对于 /WEB-INF/jsp 文件夹进行设置。默认视图指向使用 WEB-INF/jsp/components/tree.jsp
默认视图可以使用在文件 <Windchill>\codebase\WEB-INF\tlds\jcaMvc.tld 中提供的自定义标记定义进行自定义。