1 mxGraph Model and Cells
1.1 Core mxGraph architecture
1.1.1 The mxGraph Model
mxGraph模型是描述图形结构的核心模型,该类称为mxGraphModel,可以在模型包中找到。图结构的添加、更改和删除通过图模型API进行。该模型还提供了确定图形结构的方法,以及设置可视状态(如可见性、分组和样式)的方法.
然而,尽管模型的事务存储在模型中,但是mxGraph的设计方式是,主要公共API是通过mxGraph类实现的。“将此单元格添加到图中”的概念比“将此单元格添加到图的模型中”更自然地描述了操作。在直观的情况下,模型和单元格上可用的函数在图上复制,而图类上的方法被认为是主要的公共API。
尽管许多主要API调用都是通过mxGraph类进行的,但请记住,mxGraphModel是存储图数据结构的底层对象。
mxGraph使用事务系统对模型进行更改。在HelloWorld的例子中,我们看到了以下代码:
// Adds cells to the model in a single step
graph.getModel().beginUpdate();
try
{
var v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30);
var v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);
var e1 = graph.insertEdge(parent, null, '', v1, v2);
}
finally
{
// Updates the display
graph.getModel().endUpdate();
}
执行两个顶点和一条边的插入。对于模型的每个更改,您都要调用beginUpdate(),然后调用相应的调用来更改模型,然后调用endpdate()来完成更改并发出更改事件通知。
Key API Methods:
- mxGraphModel.beginUpdate() - starts a new transaction or a sub-transaction.
- mxGraphModel.endUpdate() - completes a transaction or a sub-transaction.
- mxGraph.addVertex() - Adds a new vertex to the specified parent cell.
- mxGraph.addEdge() - Adds a new edge to the specified parent cell.
注意: 从技术上讲,没不要使用.beginUpdate()和endUpdate()来包围数据代码. 超出此更新范围的更改立即生效,并立即发送通知。事实上,更新范围内的更改会直接对模型执行,更新范围用于控制事件通知的时间和连接。除非更新包装引起代码美学问题,否则应该习惯使用它来避免事件和撤消粒度可能出现的问题。
1.1.2 The Transaction Model
上面蓝色块中的子事务指的是可以嵌套事务的事实。也就是说,模型中有一个计数器,它为每个beginUpdate调用递增,为每个endpdate调用递减。在增加到至少1之后,当这个计数再次达到0时,就认为模型事务已经完成,并触发模型更改的事件通知。
这意味着代码的每个子包含部分都可以(而且应该)被开始/结束组合包围。这提供了在mxGraph中创建用作“库事务”的单独事务的能力,创建复合更改的能力,以及为所有更改触发一组事件而只创建一个撤销的能力。自动布局是一个很好的例子,说明哪里需要功能。
在自动布图中,用户通常通过用户界面对图形进行更改,应用程序根据一些规则自动定位结果。自动定位(layouting)是开始/结束更新调用之间的一个自包含算法,它不知道更改的细节。因为开始/结束更新中的所有更改都是直接对图模型进行的,所以当更改正在进行时,布局可以根据模型的状态进行操作
It is important to distinguish between functionality that acts on the graph model as part of a compound change and functionality that reacts to atomic graph change events. In the first case, such as for automatic layouting, the functionality takes the model as-is and acts upon it. This method should only be used for parts of compound model changes. All other parts of the application should only react to model change events.
当最后一个endpdate调用将计数器减少到0并指示至少发生了一个原子图更改时,将触发模型更改事件。更改事件包含关于更改内容的完整信息(有关详细信息,请参阅稍后的事件部分)。
1.1.2.1 The Model Change Methods
下面是更改图模型的方法列表,这些方法应该直接或间接地放置在更新的范围内
- add(parent, child, index)
- remove(cell)
- setCollapsed(cell, collapsed)
- setGeometry(cell, geometry)
- setRoot(root)
- setStyle(cell, style)
- setTerminal(cell, terminal, isSource)
- setTerminals(edge,source,target)
- setValue(cell, value)
- setVisible(cell, visible)
首先,我们只关心添加和删除,以及几何和样式编辑方法。注意,这些不是核心API方法,通常这些方法在适当的地方位于mxGraph类上,并且它们为您执行更新封装
设计背景—有些人对模型所存储的视觉信息的存在感到困惑。这些属性包括单元格定位、可见性和折叠状态。模型存储这些属性的默认状态,提供了一个公共的位置来在每个单元格的基础上设置它们,而视图可以在每个视图的基础上覆盖这些值。模型只是体系结构中第一个可以在全局基础上设置这些属性的常见位置。记住,这是一个图形可视化库,可视化部分是核心功能。
Inserting Cells
在HelloWorld应用程序中创建的三个图形单元格是两个顶点和一条连接顶点的边。如果您不熟悉基本图论及其术语,请参阅wikipedia条目。
您可以使用模型上的add()方法添加顶点和边。但是,出于对这个库的一般使用目的,请了解mxGraph.insertVertex()和mxGraph.insertEdge()是用于添加单元格的核心公共API。模型的函数要求已经创建了要添加的单元格,而mxGraph.insertVertex()为您创建了单元格。
Core API functions:
- mxGraph.insertVertex(parent, id, value, x, y, width, height, style) – creates and inserts a new vertex into the model, within a begin/end update call.
- mxGraph.insertEdge(parent, id, value, source, target, style) – creates and inserts a new edge into the model, within a begin/end update call.
insertvertex()将创建一个mxCell对象,并从使用的方法返回它。函数的参数为:
parent:组结构中新单元的直接父单元。我们将很快处理组结构,但现在使用graph.getDefaultParent();作为默认父类,如HelloWorld示例中所使用的。
id –这是一个描述单元格的全局惟一标识符,它始终是一个字符串。这主要用于从外部引用持久输出中的单元格。如果您不希望自己维护id,请将null传递给这个参数,并确保mxGraphModel.isCreateIds()返回true。通过这种方式,模型将管理id并确保它们是惟一的。
value –:这是单元格的用户对象。User对象就是简单的对象,只是对象,但是形成允许您将应用程序的业务逻辑与mxGraph的可视表示相关联的对象。但是,在本手册后面的部分将对它们进行更详细的描述,首先,如果您使用字符串作为用户对象,它将显示为顶点或边缘上的标签。
x, y, width, height – 顾名思义,这是顶点左上角的x和y位置以及它的宽度和高度。
style – 要应用于此顶点的样式描述。稍后将更详细地描述样式,但在简单级别上,该参数是遵循特定格式的字符串。在字符串中出现零个或多个样式名和一些覆盖全局样式或设置新样式的键/值对。在创建自定义样式之前,我们只使用当前可用的样式
使用边缘加法,相同命名的参数执行与顶点加法相同的功能。源参数和目标参数定义连接边缘的顶点。注意,源顶点和目标顶点应该已经插入到模型中
3.1.3 mxCell
mxCell是顶点和边的单元格对象。mxCell复制了模型中可用的许多函数。使用上的关键区别在于,使用模型方法创建适当的事件通知和撤消,使用单元格进行更改,但是没有更改的记录。这对于临时的视觉效果非常有用,例如动画或鼠标移动时的变化。不过,作为一般规则,除非您遇到此机制的特定问题,否则请使用模型编辑API。
在创建新单元格时,构造函数中需要三件事:值(用户对象)、几何图形和样式。在回到单元格之前,我们将探讨这三个概念。
3.1.3.1 Styles
样式和样式表的概念在概念上类似于CSS样式表,但是请注意,CSS实际上用于mxGraph,但只影响HTML页面DOM中的全局样式。在编辑器中打开util.mxConstants.js文件,搜索“STYLE_”上的第一个匹配项。如果向下滚动,您将看到大量为使用此前缀的所有不同样式定义的字符串。一些样式应用于顶点,一些应用于边,还有一些应用于两者。如您所见,这些属性在它们所作用的元素上定义了可视化属性。
mxStylesheet只包含一个对象styles,它是一个哈希表,将样式名映射到一个样式数组:
样式集合中的样式数组
在上图中,蓝色框表示mxStyleSheet中的样式哈希表。字符串'defaultVertex'是字符串/值对数组的键,这是实际的样式。注意,mxGraph创建了两个默认样式,一个用于顶点,一个用于边。如果回顾一下helloworld示例,就会发现没有任何样式被传递到insertVertex或insertEdge的可选样式参数中。在本例中,这些单元格将使用默认样式。
设置单元格的样式
如果希望为单元格指定一个样式而不是默认样式,则必须在创建单元格时将该新样式传递给单元格(mxGraph的insertVertex和insertEdge都有一个可选参数),或者使用model.setStyle()将该样式传递给单元格。
您传递的样式具有形式stylename。注意,样式名和键/值对可以是任意顺序的。下面的例子演示了这个概念,调整了我们在helloworld中看到的insertVertex调用:
A new style called 'ROUNDED' has been created, to apply this to a vertex:
var v1 = graph.insertVertex(parent, null, 'Hello', 20, 20, 80, 30, 'ROUNDED');
To create a new vertex with the ROUNDED style, overriding the stroke and fill colors:
var v1 = graph.insertVertex(parent, null, 'Hello', 20, 20, 80, 30, 'ROUNDED;strokeColor=red;fillColor=green');
To create a new vertex with no global style, but with local stroke and fill colors:
var v1 = graph.insertVertex(parent, null, 'Hello', 20, 20, 80, 30, ';strokeColor=red;fillColor=green');
To create a vertex that uses the defaultVertex style, but a local value of the fill color:
var v1 = graph.insertVertex(parent, null, 'Hello', 20, 20, 80, 30, 'defaultVertex;fillColor=blue');
注意,在本例中,默认样式必须显式命名,如果缺少style out,则当分号启动字符串时,单元格上不会设置全局样式。如果字符串开始时没有分号,则使用默认样式。
同样,mxGraph类提供了实用函数,它们构成了访问和更改单元格样式的核心API:
Core API functions:
- mxGraph.setCellStyle(style, cells) – Sets the style for the array of cells, encapsulated in a begin/end update.
- mxGraph.getCellStyle(cell) – Returns the style for the specified cell, merging the styles from any local style and the default style for that cell type.
Creating a New Global Style
要创建上面描述的圆形全局样式,您可以使用这个模板创建一个样式并将其注册到mxStyleSheet:
var style = new Object();
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE;
style[mxConstants.STYLE_OPACITY] = 50;
style[mxConstants.STYLE_FONTCOLOR]= '#774400';
graph.getStylesheet().putCellStyle('ROUNDED',style);
3.1.3.2 Geometry
在helloworld示例中,我们看到了传递到insertVertex函数的顶点的位置和大小。JavaScript中的坐标系是x向右为正,y向下为正,从图的角度看,定位绝对于放置mxGraph的容器。
单独使用mxGeometry类而不是简单地让mxRectangle类存储此信息的原因是,边缘也具有几何信息。
边的宽度和高度值被忽略,x和y值与边标签的位置有关。此外,边缘还有控制点的概念。这些是沿着边的中间点,这条边是画出来的。控制点的使用有时被称为边缘路由。
由两个控制点路由的边
几何中还有两个更重要的附加概念,相对定位和偏移量
Relative Positioning、
默认情况下,顶点的x和y位置是父元素的边框左上角到单元格本身的边框左上角的偏移量。父元素和组的概念将在本章后面进行讨论,但是,如果一个单元格没有单元格父元素,那么图容器就是它的父元素,用于定位。
Non-relative vertex positioning
对于边缘,在非相对模式(默认模式)中,边缘标签位置是与图形原点的绝对偏移量。
Non-relative edge label positioning
对于相对模式下的顶点,(x,y)是沿着原始单元格所在的父单元格(宽,高)的比例。(0,0)与父结点原点相同,(1,1)将原点放在父结点右下角。对于两个维度,相同的相对定位都延伸到0以下和1以上。这种定位对于保持子单元格相对于总体父单元格大小的固定很有用。
Relative vertex positions
最后,根据边缘中心的位置对相对模式下的边缘标签进行palced。x坐标是从边的源端(-1)到边的目标端(1)的相对距离。y坐标是与边缘垂直的像素偏移量。下图显示了相对模式下不同边缘标签的x、y值。注意,对于直边,计算很简单。对于具有多个控制点的边缘,必须沿着其段(段是端点和/或控制点之间的线)跟踪边缘,以找到沿边缘的正确距离。y值是这段的正交偏移量。
切换边缘标签的相对定位是应用程序的常见首选项。导航到mxGraph. insertedge()函数,您将看到它调用createEdge()。在createEdge()中,为使用此原型创建的每条边设置相对的几何形状。这在一定程度上是mxGraph中有大量辅助函数的原因,它们支持轻松更改默认行为。您应该尽量使用mxGraph类API,以便在应用程序中提供这种好处。
Offsets
mxGeometry中的偏移量字段是应用于单元格标签的绝对x、y偏移量。在边缘标签的情况下,偏移量总是在根据上一节的相对标志计算边缘标签后应用。
Core API functions:
- mxGraph.resizeCell(cell, bounds) – Resizes the specified cell to the specified bounds, within a begin/end update call.
- mxGraph.resizeCells(cells, bounds) – Resizes each of the cells in the cells array to the corresponding entry in the bounds array, within a begin/end update call.
3.1.3.3 User Objects
User对象为mxGraph图提供了上下文,它存储与可视单元格关联的业务逻辑。在HelloWorld示例中,user对象只是一个字符串,在本例中,它只是表示将为该单元格显示的标签。在更复杂的应用程序中,这些用户对象将代替对象。对象的某些属性通常是可视单元格将显示的标签,对象的其余部分描述与应用程序域相关的逻辑。
使用一个简单的工作流或流程应用程序的例子,假设我们有下图(这个例子可以在线获得,从tasks窗口中选择swimlane例子):
A simple workflow
通常,此工作流将存在于某些应用服务器和/或数据库上。浏览器用户连接到该服务器,或者连接到应用程序服务器的一些前端服务器,用户的web应用程序请求“order”工作流。服务器获取该工作流的数据并将其传输给客户机。
mxGraph支持在服务器端填充模型并将其传输到客户机,然后再返回。参见后面关于“I/O和服务器通信”的章节。
传输的数据将是可视化模型(图)和业务逻辑(主要包含在用户对象中)。客户机将首先显示上面的图表。如果用户有权编辑这个工作流,他们通常可以做两件事:1)编辑图表,添加和删除顶点,以及更改连接;2)编辑单元格的用户对象
在在线演示中,如果您右键单击并选择“检查库存”菱形的属性,您将看到如下对话框:
The properties of a vertex
这些属性显示几何图形、标签、ID等,但是对话框也可以轻松地显示单元格的用户对象。对于如何实际检查库存,可以参考工作流引擎上的某个流程。这可能是一种特定于应用程序的机制,用于服务器和客户机为远程方法调用分配一些标识。另一个值可能是处理返回的对象的类型,在本例中可能是一个布尔值或一个整数来指示股票级别。给定该返回类型,就可以使用关系图强制约束并提供可视化警报
这些属性显示几何图形、标签、ID等,但是对话框也可以轻松地显示单元格的用户对象。对于如何实际检查库存,可以参考工作流引擎上的某个流程。这可能是一种特定于应用程序的机制,用于服务器和客户机为远程方法调用分配一些标识。另一个值可能是处理返回的对象的类型,在本例中可能是一个布尔值或一个整数来指示股票级别。给定该返回类型,就可以使用关系图强制约束并提供可视化警报
请记住,上面的示例是非常特定于领域的,它用于解释用户对象如何映射到应用程序的业务逻辑。它可视化了mxGraph如何创建我们所说的上下文图。上下文由顶点和存储在用户对象中的业务逻辑之间的连接组成。典型的应用程序从服务器接收可视化逻辑和业务逻辑,可能允许对两者进行编辑,然后将两者都发回服务器进行持久性和/或执行。
3.1.3.4 Cell Types
如前所述,mxGraph是使用这个库的主要API,同样的概念也适用于单元格。图中没有显示单元格的一个基本状态是单元格是顶点还是边,这个调用将在单元格或模型上执行。
mxCell上有两个布尔标志,顶点和边缘,当创建单元格时,helper方法将其中一个设置为true。mxIGraphModel上的isVertex()和isEdge()是模型用来确定单元格类型的,这两种类型都没有单独的对象。从技术上讲,可以在运行时切换单元格的类型,但是要注意在更改类型之后使单元格状态无效(请参阅后面的部分)。另外,要注意几何对象变量对顶点和边的意义是不同的。通常,不建议在运行时更改单元格类型。
3.1.4 Group Structure
- 在mxGraph中,分组是单元格之间逻辑关联的概念。在许多图形工具包中,这通常被称为子图的概念。分组涉及到图模型数据结构中的一个或多个顶点或边成为父顶点或边(通常是一个顶点)的子节点。分组允许mxGraph提供一些有用的功能:
- 子图,逻辑上独立的图的概念,在更高层次的图中显示为每个子图的单元格。
- 扩大和崩溃。折叠是将一组单元格可视化地替换为它们的父单元格的能力。而扩张恰恰相反。单击在线workfloweditor示例中泳道示例的组单元格左上角的小“-”可以看到这种行为。这将在下面的复杂性管理部分中进行描述。
- 分层。分层是将单元格分配到图形显示中的特定z阶层的概念。
- 向下钻,向上钻。这些概念允许子图可视化和编辑,就像它们是一个完整的图一样。在User Objects部分,我们看到“check inventory”顶点是一个单元格。例如,开发人员将流程中的每个顶点描述为执行任务的软件流程。应用程序可能有一个向下钻取check inventory顶点的选项。这将导致出现一个描述deta的新图表
在分组中,为单元格分配一个父单元格。在最简单的情况下,所有单元格都有默认父元素作为它们的父元素。默认父元素是一个不可见的单元格,具有与图相同的边界。这是helloworld示例中graph.getDefaultParent()返回的单元格。顶点的x、y位置是相对于其父节点的位置,因此在默认分组(共享默认父节点的所有单元格)的情况下,单元格位置也是图组件上的绝对坐标。在将所有单元格添加到默认根目录的情况下,在helloworld示例中,组结构在逻辑上看起来如下图所示。
注意添加了第0层单元格,这是组结构中默认的间接性,允许根据额外单元格的需求进行层更改。为了正确起见,我们将它包含在下面,但是在稍后的组图中,它将被省略。
he group structure of the helloworld example
另外,请注意边缘标签(几何中的x,y)的位置是相对于父单元格的。
如果我们回到User Objects部分中的简单工作流示例,我们可以看到可视化的分组可能是什么样的。在本例中,组单元格表示人员,子顶点表示分配给这些人员的任务。在这个例子中,逻辑组结构是这样的:
The logical group structure of the workflow example
工作流操作顶点是黄色的子节点,泳道组顶点被标记为蓝色。
使用mxGraph类上的insertVertex和insertEdge函数的父参数,可以将单元格插入到组结构中。这些函数相应地在子单元上设置父单元,重要的是,通知父单元它的新子单元。
更改组结构是通过mxGraph.groupCells()和mxGraph.ungroupCells()函数执行的。
Core API functions:
- mxGraph.groupCells(group, border, cells) – Adds the specified cells to the specified group, within a begin/end update
- mxGraph.ungroupCells(cells) – Removes the specified cells from their parent and adds them to their parent's parent. Any group empty after the operation are deleted. The operation occurs within a begin/end update.
3.1.5 Complexity Management
控制每次显示的单元格数量有两个主要原因。首先是性能,在任何平台上绘制越来越多的单元都将在某个时候达到性能可用性限制。第二个原因是易用性,人类只能理解一定数量的信息。上面列出的所有与分组相关的概念都可以用于减少用户屏幕上信息的复杂性。
3.1.5.1 Folding
折叠是我们用来表示展开和折叠基团的总称。我们说一个单元格通过使它的子顶点不可见来折叠。与此功能有关的功能有:
Core API function:
- mxGraph.foldCells(collapse, recurse, cells) – States the collapsed state of the specificed cells, within a begin/end update.
Folding related functions:
mxGraph.isCellFoldable(cell, collapse) – By default true for cells with children.
mxGraph.isCellCollapsed(cell) – Returns the folded state of the cell
当一个组单元格崩溃时,默认情况下会发生三件事:
- 那个单元格的子单元格变得不可见。
- 使用组单元格的组边界。在mxGeometry中有一个alternative ativebounds字段,在groups单元格中,默认情况下为它们的折叠和扩展状态存储一个单独的边界。这些实例之间的切换由mxGraph.swapBounds()调用,并在foldCells()调用中为您处理。这允许折叠组的大小可以调整,而当再次展开时,使用预折叠大小的大小看起来是正确的。
- :默认情况下,会发生边缘提升。边缘提升是指显示连接到折叠组内的子元素的边缘,这些子元素也连接到折叠组外的单元格,方法是让它们看起来连接到折叠的父元素。
Expanded swimlane
Collapsed Swimlane
上面的两幅图展示了这三个概念。在展开状态下,上面的组单元格在左上角显示一个小框,其中有一个“-”字符。这表示单击此框将折叠组单元格。这样做,我们得到了底部的图像,其中组单元格呈现其折叠大小。不离开组单元格的子顶点和边是不可见的。最后,将退出组单元格的边缘提升为与折叠的组单元格连接。单击现在出现在框内的“+”字符将展开组单元格,并将其带回到顶部图像的原始状态。
使用mxGraph.foldCells()函数,您可以通过编程实现与单击展开/折叠符号相同的结果。这种方法的一个常见用法是,当应用程序缩小特定的数量时,对单元格集群进行分组,分组后的单元格折叠(由于应用程序控制折叠,通常不使用“-”框)。这样,用户可以看到更少、更大的单元格,每个单元格逻辑上表示其子单元格。然后,您可能会提供一种机制来放大组,从而在过程中展开组。您还可以提供向下钻取/向上提升,expl
3.1.5.2 Sub-Graphs, Drill-Down / Step-Up
有时,作为展开/折叠的替代方法,或者可能与之结合,您的图将由许多嵌套在层次结构中的图组成。下面我们来看一个简单的例子:
An example top level workflow
这个简单的工作流由三个高级步骤组成。显然,每个步骤包含许多子步骤,我们将查看Solve Bug单元格的子图。
在“解决Bug顶点”下面,我们创建了一些子节点来更详细地表示解决Bug的过程,在本例中,是在企业号星舰上解决Bug的过程。
在这个使用GraphEditor示例的示例中,上面图片中选择的菜单选项调用mxGraph.enterGroup(cell),它是子图的一对核心API函数之一。
Core API functions:
- mxGraph.enterGroup(cell) – Makes the specified cell the new root of the display area.
- mxGraph.exitGroup() - Makes the parent of the current root cell, if any, the new root cell.
- mxGraph.home() - Exits all groups, making the default parent the root cell.
到目前为止,图的根单元格一直是所有一级单元格的默认父节点。使用这些函数,您可以使组结构中的任何组单元格成为根单元格,以便该父单元格的子单元格在显示中显示为完整的图形。
Result of drilling down into the Solve Bug vertex
用折叠展开的图是这样的:
使用shape->退出组选项退出组,该选项调用mxGraph。exitGroup,带你回到原来的3个顶点顶层图。
3.1.5.3 Layering and Filtering
与许多图形应用程序一样,在mxGraph中也有z阶的概念。也就是说,当你看屏幕方向时,物体的顺序。对象可以在其他对象的后面或前面,如果它们重叠并且不透明,那么最后面的对象将部分或完全模糊。回头看看上面HelloWorld插图的图形结构。子单元格存储在父单元格下的顺序是确定的(默认情况下是您添加它们的顺序)。
如果我们移动HelloWorld例子中的单元,我们会看到以下结果:
重叠的顶点
可以看出,World顶点在Hello顶点的前面。这是因为World顶点的子索引比Hello顶点高,分别位于保存根单元格子元素的有序集合的位置1和0。
为了改变顺序,我们使用mxGraph.orderCells。
Core API function:
mxGraph。orderCells(back, cells)——在开始/结束更新中,根据标记将单元格数组移动到其兄弟元素的前面或后面。
mxGraph中的同级单元格是任何具有相同父级的单元格。通过在Hello顶点上调用这个函数它就会与World顶点重叠。
排序和分组可以扩展成逻辑分层的组。单元格是通过深度优先搜索绘制的。再次以HelloWorld为例,假设Hello和World顶点下面都有一些子顶点层次结构。Hello顶点及其所有子节点将在World顶点或其任何子节点之前绘制。如果Hello和World是不可见的组单元格,那么就有两个层次的单元格,一个单元格完全在另一个单元格之前绘制。您还可以通过简单地切换不可见组单元格的顺序来切换层次结构的顺序。
分层的概念在layers.html示例中得到了演示。这里的按钮用于设置组层单元格的可见性。这个例子与过滤的概念紧密相关。
在具有某些特定属性的筛选单元格中显示。提供过滤功能的一个选项是在呈现单元格之前检查某些状态。另一种方法是,如果过滤条件简单且事先已知,则按组分配可过滤单元。使组可见和不可见执行此筛选操作。