基于PresentationRules个性化显示iModel的内容


基于PresentationRules个性化显示iModel的内容

 

iModel.js Presentation库

iModel.js Presentation库帮助从iModel中检索数据,并负责统一选择。从iModel中检索的数据是由使用者使用表示规则以声明方式定义的,这就是为什么该库通常很称为“表示规则引擎”。

 

要使用该库,需要在前后端执行一些步骤:

 

更多参考链接见官方文档https://www.imodeljs.org/learning/presentation/

使用

其实在使用的时候,用户更多的是只需要关注ruleset.json的内容,其中ruleset.json中定义的显示规则决定了控件(主要是树形控件,表格控件,属性框控件)所显示的imodel中的内容。其中ruleset定义了丰富的显示规则,用户只需要专注于编辑ruleset.json中的 显示规则,几乎不用修改任何代码,即可实现自己的个性化显示需求。

以下是一个tree形控件的ruleset.json内容示例:

{
  "$schema": "../../node_modules/@bentley/presentation-common/Ruleset.schema.json",
  "id": "imodel-content-tree/Default",
  "supportedSchemas": {
    "schemaNames": ["BisCore"]
  },
  "rules": [
    {
      "ruleType": "RootNodes",
      "autoExpand": true,
      "specifications": [
        {
          "specType": "InstanceNodesOfSpecificClasses",
          "classes": [
            {
              "schemaName": "BisCore",
              "classNames": ["Subject"]
            }
          ],
          "instanceFilter": "this.Parent = NULL",
          "arePolymorphic": false,
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"Subject\", \"BisCore\")",
      "specifications": [
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            {
              "relationship": {
                "schemaName": "BisCore",
                "className": "SubjectOwnsSubjects"
              },
              "direction": "Forward",
              "targetClass": {
                "schemaName": "BisCore",
                "className": "Subject"
              }
            }
          ],
          "instanceFilter": "json_extract(this.JsonProperties, \"$.Subject.Job.Bridge\") <> NULL OR ifnull(json_extract(this.JsonProperties, \"$.Subject.Model.Type\"), \"\") = \"Hierarchy\"",
          "hideNodesInHierarchy": true,
          "groupByClass": true,
          "groupByLabel": false
        },
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            {
              "relationship": {
                "schemaName": "BisCore",
                "className": "SubjectOwnsSubjects"
              },
              "direction": "Forward",
              "targetClass": {
                "schemaName": "BisCore",
                "className": "Subject"
              }
            }
          ],
          "instanceFilter": "json_extract(this.JsonProperties, \"$.Subject.Job.Bridge\") = NULL AND ifnull(json_extract(this.JsonProperties, \"$.Subject.Model.Type\"), \"\") <> \"Hierarchy\"",
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"Subject\", \"BisCore\")",
      "specifications": [
        {
          "specType": "InstanceNodesOfSpecificClasses",
          "classes": {
            "schemaName": "BisCore",
            "classNames": ["Model"]
          },
          "arePolymorphic": true,
          "relatedInstances": [
            {
              "relationshipPath": {
                "relationship": {
                  "schemaName": "BisCore",
                  "className": "ModelModelsElement"
                },
                "direction": "Forward",
                "targetClass": {
                  "schemaName": "BisCore",
                  "className": "InformationPartitionElement"
                }
              },
              "alias": "partition",
              "isRequired": true
            }
          ],
          "instanceFilter": "(parent.ECInstanceId = partition.Parent.Id OR json_extract(parent.JsonProperties, \"$.Subject.Model.TargetPartition\") = printf(\"0x%x\", partition.ECInstanceId)) AND NOT this.IsPrivate AND json_extract(partition.JsonProperties, \"$.PhysicalPartition.Model.Content\") = NULL",
          "groupByClass": true,
          "groupByLabel": false
        },
        {
          "specType": "InstanceNodesOfSpecificClasses",
          "classes": {
            "schemaName": "BisCore",
            "classNames": ["Model"]
          },
          "arePolymorphic": true,
          "relatedInstances": [
            {
              "relationshipPath": {
                "relationship": {
                  "schemaName": "BisCore",
                  "className": "ModelModelsElement"
                },
                "direction": "Forward",
                "targetClass": {
                  "schemaName": "BisCore",
                  "className": "InformationPartitionElement"
                }
              },
              "alias": "partition",
              "isRequired": true
            }
          ],
          "instanceFilter": "(parent.ECInstanceId = partition.Parent.Id OR json_extract(parent.JsonProperties, \"$.Subject.Model.TargetPartition\") = printf(\"0x%x\", partition.ECInstanceId)) AND NOT this.IsPrivate AND json_extract(partition.JsonProperties, \"$.PhysicalPartition.Model.Content\") <> NULL",
          "hideNodesInHierarchy": true,
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"ISubModeledElement\", \"BisCore\")",
      "specifications": [
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            {
              "relationship": {
                "schemaName": "BisCore",
                "className": "ModelModelsElement"
              },
              "direction": "Backward"
            }
          ],
          "instanceFilter": "NOT this.IsPrivate",
          "hideNodesInHierarchy": true,
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"GeometricModel3d\", \"BisCore\")",
      "specifications": [
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            [
              {
                "relationship": {
                  "schemaName": "BisCore",
                  "className": "ModelContainsElements"
                },
                "direction": "Forward"
              },
              {
                "relationship": {
                  "schemaName": "BisCore",
                  "className": "GeometricElement3dIsInCategory"
                },
                "direction": "Forward"
              }
            ]
          ],
          "suppressSimilarAncestorsCheck": true,
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"GeometricModel2d\", \"BisCore\")",
      "specifications": [
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            [
              {
                "relationship": {
                  "schemaName": "BisCore",
                  "className": "ModelContainsElements"
                },
                "direction": "Forward"
              },
              {
                "relationship": {
                  "schemaName": "BisCore",
                  "className": "GeometricElement2dIsInCategory"
                },
                "direction": "Forward"
              }
            ]
          ],
          "suppressSimilarAncestorsCheck": true,
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"Model\", \"BisCore\")",
      "onlyIfNotHandled": true,
      "specifications": [
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            [
              {
                "relationship": {
                  "schemaName": "BisCore",
                  "className": "ModelContainsElements"
                },
                "direction": "Forward"
              }
            ]
          ],
          "suppressSimilarAncestorsCheck": true,
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"SpatialCategory\", \"BisCore\")",
      "specifications": [
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            {
              "relationship": {
                "schemaName": "BisCore",
                "className": "GeometricElement3dIsInCategory"
              },
              "direction": "Backward"
            }
          ],
          "instanceFilter": "this.Model.Id = parent.parent.ECInstanceId ANDALSO this.Parent = NULL",
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"DrawingCategory\", \"BisCore\")",
      "specifications": [
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            {
              "relationship": {
                "schemaName": "BisCore",
                "className": "GeometricElement2dIsInCategory"
              },
              "direction": "Backward"
            }
          ],
          "instanceFilter": "this.Model.Id = parent.parent.ECInstanceId ANDALSO this.Parent = NULL",
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ChildNodes",
      "condition": "ParentNode.IsOfClass(\"Element\", \"BisCore\")",
      "onlyIfNotHandled": true,
      "specifications": [
        {
          "specType": "RelatedInstanceNodes",
          "relationshipPaths": [
            {
              "relationship": {
                "schemaName": "BisCore",
                "className": "ElementOwnsChildElements"
              },
              "direction": "Forward"
            }
          ],
          "groupByClass": true,
          "groupByLabel": false
        }
      ]
    },
    {
      "ruleType": "ImageIdOverride",
      "condition": "ThisNode.IsInstanceNode ANDALSO ThisNode.IsOfClass(\"Subject\", \"BisCore\")",
      "imageIdExpression": "IIF(this.Parent.Id = NULL, \"icon-imodel-hollow-2\", \"icon-folder\")"
    },
    {
      "ruleType": "ImageIdOverride",
      "condition": "ThisNode.IsInstanceNode ANDALSO ThisNode.IsOfClass(\"Model\", \"BisCore\")",
      "imageIdExpression": "\"icon-model\""
    },
    {
      "ruleType": "ImageIdOverride",
      "condition": "ThisNode.IsInstanceNode ANDALSO ThisNode.IsOfClass(\"Category\", \"BisCore\")",
      "imageIdExpression": "\"icon-layers\""
    },
    {
      "ruleType": "ImageIdOverride",
      "condition": "ThisNode.IsInstanceNode ANDALSO ThisNode.IsOfClass(\"Element\", \"BisCore\")",
      "imageIdExpression": "\"icon-item\""
    },
    {
      "ruleType": "ImageIdOverride",
      "condition": "ThisNode.IsClassGroupingNode",
      "imageIdExpression": "\"icon-ec-class\""
    }
  ]
}

通过将其导入到typescript中,示例代码如下:

/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import * as React from "react";
import { useCallback } from "react"; // tslint:disable-line: no-duplicate-imports
import { IModelApp, IModelConnection } from "@bentley/imodeljs-frontend";
import { useOptionalDisposable } from "@bentley/ui-core";
import { ControlledTree, SelectionMode, usePagedTreeNodeLoader, useTreeModelSource, useVisibleTreeNodes } from "@bentley/ui-components";
import { IPresentationTreeDataProvider, PresentationTreeDataProvider, useUnifiedSelectionTreeEventHandler } from "@bentley/presentation-components";
const RULESET_TREE = require("./Tree.ruleset.json"); // tslint:disable-line: no-var-requires

/** React properties for the tree component, that accepts an iModel connection with ruleset id */
export interface IModelConnectionProps {
  /** iModel whose contents should be displayed in the tree */
  imodel: IModelConnection;
}

/** React properties for the tree component, that accepts a data provider */
export interface DataProviderProps {
  /** Custom tree data provider. */
  dataProvider: IPresentationTreeDataProvider;
}

/** React properties for the tree component */
export type Props = IModelConnectionProps | DataProviderProps;

/** Tree component for the viewer app */
export default function SimpleTreeComponent(props: Props) {
  const imodel = (props as IModelConnectionProps).imodel;
  const imodelDataProvider = useOptionalDisposable(useCallback(() => {
    if (imodel)
      return new PresentationTreeDataProvider({ imodel, ruleset: RULESET_TREE });
    return undefined;
  }, [imodel]));
  const dataProvider = imodelDataProvider ?? (props as DataProviderProps).dataProvider;
  const modelSource = useTreeModelSource(dataProvider);
  const nodeLoader = usePagedTreeNodeLoader(dataProvider, 20, modelSource);
  const eventsHandler = useUnifiedSelectionTreeEventHandler({ nodeLoader, collapsedChildrenDisposalEnabled: true });
  return (
    <>
      <h3 data-testid="tree-component-header">{IModelApp.i18n.translate("SimpleViewer:components.tree")}</h3>
      <div style={{ flex: "1" }}>
        <ControlledTree
          nodeLoader={nodeLoader}
          visibleNodes={useVisibleTreeNodes(modelSource)}
          treeEvents={eventsHandler}
          selectionMode={SelectionMode.Extended}
          iconsEnabled={true}
        />
      </div>
    </>
  );
}

我们即可得到如下显示效果:

由于表格控件,属性框控件也是使用类似的规则,即可实现用户的个性化显示需求,在此不再赘述。若要了解更多相关统一选择内容请参见iModel.js前端统一选择一致性机制