Many application areas require a presentation of data where the nodes of a diagram are organized in a tabular way, i.e., where each node is associated to a specific row and column in the grid-like structure of a table. Swimlane layouts are a popular example of such presentations. Figure 4.34, “Tabular data presentation” shows samples of tabular presentations of diagrams.
Figure 4.34. Tabular data presentation
![]() |
![]() |
Swimlane layout with four lanes from top to bottom. | A diagram following the Business Process Modeling Notation (BPMN) where swimlanes are additionally subdivided by so-called milestones. |
The yFiles diagramming library contains comprehensive support for tabular data presentation, which builds on the general concept of grouped graphs. The provided functionality covers the "look" as well as the "feel," i.e., table structures with rows and columns can be both rendered and also interactively edited. Rows and columns can be:
Furthermore, the hierarchical layout style, more precisely class IncrementalHierarchicLayouter, provides advanced support for automatic tabular layout.
The presentation of a diagram in a tabular way needs an additional element, namely a table structure to encompass the proper nodes of the diagram. This table structure is backed by a group node, and the diagram's proper nodes, i.e., the actual content nodes, need to be set up so that they are child nodes of this group node.
Using a group node to hold the content nodes brings all advantages of this concept as described in the section called “Grouping”. For example, when the group node is moved, the child nodes move accordingly, thus maintaining the visual clue that they are contained in the table group node.
The table structure, which typically shows some rows and columns within which the content nodes lie, directly results from the table model which actually defines these rows and columns.
The group node that "is behind" the table structure uses a special node style implementation to govern the actual rendering. The visualization of individual rows and columns can be conveniently achieved by using node styles.
User interaction with the table structure is supported by a specialized input mode, which makes avaliable support for resizing rows and columns, re-parenting them, or editing their labels, for example.
It is crucial to understand, however, that the content nodes of a diagram and the table structure are only loosely coupled. More precisely, the association of a content node to a row and a column is only done on a geometric basis, i.e., the node's center coordinates determine the row (column) that it belongs to. This also means that when a content node is moved, for example, the representation of the table does not change in any way.
Basically, the table model represents the blueprint for the rendering of the table structure. The table model defines the rows and columns of a table, their sizes, and in particular their nesting structure, i.e., rows (columns) may have so-called child rows (columns) to support nested row (column) structures.
Figure 4.35, “Table structure with nested rows and columns” shows a table structure with nested rows and columns. The table has two top-level rows where one consists of two child rows. Similarly, there are two top-level columns where one consists of two child columns. Note that there are no content nodes.
Figure 4.35. Table structure with nested rows and columns
![]() |
Table structure with nested rows and columns. |
The table model is constituted by the types listed in Table 4.30, “Table model types”.
Table 4.30. Table model types
Type name | Description |
---|---|
ITable![]() |
Provides the virtual roots of the row and column hierarchies of a table model.
The Table![]() |
IStripe![]() |
Models common aspects of rows and columns in a table model. This includes the layout, insets, (minimum) sizes, for example, and also support for labels. |
IRow![]() |
Models a row in a table model. Note that IStripe is the superinterface. |
IColumn![]() |
Models a column in a table model. Note that IStripe is the superinterface. |
The ITable implementation provides the virtual roots of the row and column hierarchies of a table model. These allow to add top-level rows and columns, to which in turn child rows and child columns can be added to.
The area that belongs to a row (column) is determined by its height (width) as well as by its position with respect to the order of rows (columns). A row (column) spans all columns (rows) of the table structure, i.e., its width (height) is the accumulated width (height) of all columns (rows).
While the width of a row (height of a column) is determined implicitly, its height (width) can be set. This size, however, is restricted by the row's (column's) minimum size. In other words, a row (column) cannot be made smaller than its minimum size.
void SetSize(IStripe stripe, double newSize) |
|
Description | Sets the preferred size for a given IStripe, i.e., the height of a row or the width of a column. |
Also, if the row (column) contains any nested child rows (columns), setting the size does not take effect, since it is implicitly constrained to the accumulated sizes of the nested rows (columns).
For the same reason the actual bounds, or layout, of a row (column) also cannot
be set explicitly.
Instead, they are determined as a result of the table structure, which means they
are calculated anew through the row's (column's) Layout
property whenever necessary.
Figure 4.36. Insets in a table structure
![]() |
Table, column, and child column insets at the top of a table structure. Similarly, insets can also be specified for the bottom, left, and right sides of the table and the rows and columns. |
From a geometric point of view, a table consists of its content area, which is determined by the geometry of its rows and columns, and the surrounding border area. The border area is determined by the table insets, and may be used to display a table description, for example.
InsetsD Insets { get; set; } |
|
Description | Sets the table's insets. The insets define a border around the rows and columns of a table structure. |
Similarly, rows and columns also have a content area and a border area. The content area contains the content nodes of the row (column), respectively the content area of nested rows (colunns). The border area which is determined by corresponding row (column) insets, however, denotes both extra space outside of a row's (column's) content area as well as some padding inside the content area.
void SetInsets(IStripe stripe, InsetsD insets) |
|
Description | Sets insets for a given IStripe, i.e., a row or column. The insets denote extra space to the left and right of a row's content area, respectively at the top and bottom of a column's content area. The top and bottom values (row) as well as the left and right values (column) denote padding space at the inside of the content area. |
In a parent row (column), i.e., a row (column) with nested child rows (columns), the content area of the child rows (columns) lies completely within the content area of the parent row (column). The border area of the parent row (column) is to the left and right of its nested child rows (at the top and bottom of its nested child columns).
The actual insets and the actual size of a parent row (column) where nested child rows (columns) affect the parent row's (column's) preferred insets and size can be queried using the following methods:
InsetsD GetActualInsets() |
|
Description | Convenience (extension) getter methods for interface IStripe to get the actual insets and the actual size of a row or column. |
Unless explicitly set, insets for new rows and columns are adopted from the default
values as specified by the RowDefaults/ColumnDefaults
provided by the ITable instance:
IStripeDefaults RowDefaults { get; set; } |
|
Description |
Properties to manage the IStripeDefaults![]() |
Creating a table model that uses the default ITable implementation class Table
goes as follows:
Example 4.41. Creating a table model
// Creating a new table model using the default ITable implementation. ITable table = new Table();
To actually get a table model rendered, it needs yet two other things: a group node, to which the table model needs to be bound to, and an appropriate node style implementation that can handle table models and that is set as the group node's style.
Binding the table model to a group node means making the table model available in the group node's look-up, so that it can be queried using the look-up mechanism:
ITable table = groupNode.Get<ITable>();
This is important for properly handling user interaction with a table structure, for example.
Using class TableNodeStyle,
one of the predefined ITableNodeStyle implementations,
as the node style for a group node, for example, conveniently binds a table model
to the group node as a side effect:
Example 4.42. Associating a table model with a group node
// 'graph' is of type yWorks.yFiles.UI.Model.IGroupedGraph. // 'table' is of type yWorks.yFiles.UI.Model.ITable. ITableNodeStyle tableStyle = new TableNodeStyle(table); // Create a top-level group node and bind, via the TableNodeStyle, the table to // it. graph.CreateGroupNode(graph.Hierarchy.Root, table.Layout.ToRectD(), tableStyle);
Upon initialization, a Table instance holds an empty model that has no rows or columns. Rows and columns can be created using the following Table methods:
IRow CreateRow(IRow owner, int index, double height, double minHeight, InsetsD insets, INodeStyle style, object tag) |
|
Description | Creates a new row (column) as a child of the given IRow (IColumn). |
The following convenience methods enable creation of top-level and nested rows and columns:
IRow CreateRow() |
|
Description | Convenience (extension) method(s) (excerpt) for interface ITable to create top-level rows. Top-level rows are not nested within another row, their 'owner' is the table itself. |
IRow CreateRow(IRow owner) |
|
Description | Convenience (extension) method(s) (excerpt) for interface ITable to create rows that are nested within another row. |
IColumn CreateColumn() |
|
Description | Convenience (extension) method(s) (excerpt) for interface ITable to create top-level columns. Top-level columns are not nested within another column, their 'owner' is the table itself. |
IColumn CreateColumn(IColumn owner) |
|
Description | Convenience (extension) method(s) (excerpt) for interface ITable to create columns that are nested within another column. |
Example 4.43, “Adding rows and columns” illustrates a simple tabular presentation with two rows and columns together with its setup in code.
Example 4.43. Adding rows and columns
![]() |
A minimalistic table with 2 rows and columns. |
// 'graph' is of type yWorks.yFiles.UI.Model.IGroupedGraph. // Create a new table model and set insets on the table so that the rows and // columns stand out. var table = new Table() { Insets = new InsetsD(10, 10, 10, 10) }; table.RowDefaults.Style = new ShapeNodeStyle { Brush = Brushes.Transparent }; table.ColumnDefaults.Style = new ShapeNodeStyle { Brush = new SolidColorBrush(Color.FromRgb(139, 162, 220)) }; // Using no insets prevents column/row headers. table.ColumnDefaults.Insets = table.RowDefaults.Insets = new InsetsD(); table.ColumnDefaults.Size = table.RowDefaults.Size = 30; // Add top-level rows and columns. The table's size is then 2x2. table.CreateRow(); table.CreateRow(); table.CreateColumn(); table.CreateColumn(); // Using TableNodeStyle to render the table structure. TableNodeStyle tableStyle = new TableNodeStyle(table); tableStyle.BackgroundStyle = new ShapeNodeStyle { Brush = new SolidColorBrush(Color.FromRgb(248, 236, 201)) }; // Create a top-level group node and bind, via the TableNodeStyle, the table to // it. graph.CreateGroupNode(graph.Hierarchy.Root, table.Layout.ToRectD(), tableStyle);
Rows and columns can be deleted using the following method defined by the ITable interface:
void Remove (IStripe stripe) |
|
Description | Removes a given IStripe, i.e., a row or column. |
The following convenience methods support recursively deleting rows or columns and enable resizing of the remaining rows or columns:
void RemoveWithResize(IStripe stripe) |
|
Description | Convenience (extension) methods for interface ITable to remove rows or columns. |
Support for Undo/Redo operations for the tables in a diagram can be easily enabled using the convenience functionality in class Table:
static void RegisterStaticUndoSupport(IGraph graph, IUndoSupport undoSupport) |
|
Description | Undo/Redo support for tables. Uses the given IUndoSupport implementation to handle Undo/Redo operations. |
static void RegisterDynamicUndoSupport(IGraph graph) |
|
Description | Undo/Redo support for tables. The IUndoSupport implementation to handle Undo/Redo operations is retrieved from the given graph. |
Example 4.44. Enabling support for Undo/Redo operations for tables
// 'graph' is of type yWorks.yFiles.UI.Model.IGroupedGraph. if (graph != null) { // Enabling general undo support. DefaultGraph defaultGraph = graph.Graph.Get<DefaultGraph>(); if (defaultGraph != null) { defaultGraph.UndoEngineEnabled = true; } // Using the undo support from the graph also for all future table instances. Table.RegisterStaticUndoSupport(graph.Graph, graph.Graph.Get<IUndoSupport>()); }
A table model's visual representation is provided by a special node style implementation
that can handle table models.
Interface ITableNodeStyle defines
the general contract for such node styles:
Class TableNodeStyle, the default
implementation of ITableNodeStyle, can be set as the style for a group node as follows:
Example 4.45. Setting a table node style with a group node
// 'graph' is of type yWorks.yFiles.UI.Model.IGroupedGraph. // 'table' is of type yWorks.yFiles.UI.Model.ITable. ITableNodeStyle tableStyle = new TableNodeStyle(table); // Create a top-level group node and bind, via the TableNodeStyle, the table to // it. graph.CreateGroupNode(graph.Hierarchy.Root, table.Layout.ToRectD(), tableStyle);
The ITable instance that is set with the TableNodeStyle instance defines the style's table model. When the TableNodeStyle instance is set as the style of a group node, this table model is used to render a table structure that has the group node's dimensions and location.
As a side effect of setting the TableNodeStyle instance as the style for a group node, the table model is also conveniently bound to the group node. This means that it is made available in the group node's look-up, so that it can be queried using the look-up mechanism:
ITable table = groupNode.Get<ITable>();
This is important for properly handling user interaction with a table structure, for example.
Note that because of its table model class TableNodeStyle does not support style
sharing among multiple group nodes.
Style sharing is supported by class DynamicTableNodeStyle.
ITableNodeStyle governs the overall rendering of a table structure and allows to use a separate node style implementation to render the background behind all rows and columns:
INodeStyle BackgroundStyle { get; set; } |
|
Description | Sets the node style that is used to render the background behind all rows and columns of a table structure. |
Abstract class AbstractTableNodeStyle
defines the following property to determine the rendering order of rows and columns:
TableRenderingOrder TableRenderingOrder { get; set; } |
|
Description | Sets the rendering order of the rows and column of a table structure. By default, columns are rendered first, then rows. |
The rendering order also affects the fill colors for rows and columns. Using the default rendering order, for example, neither column fill colors nor border lines can be seen for non-transparent row fill colors.
The rendering order is also taken into account for hit-testing.
The visual representation of rows and columns of a table structure is conveniently provided by INodeStyle implementations. Node styles can be associated with IStripe instances either at creation time or using the following method defined in interface ITable:
void SetStyle(IStripe stripe, INodeStyle style) |
|
Description | Sets the node style for an IStripe instance, i.e., the row or column of a table structure. |
The overall rendering of a table structure, which is governed by the ITableNodeStyle
implementation that is set with the group node, delegates to these row and column
node styles.
Tutorial demo application TableNodeStyleWindow shows custom INodeStyle implementations that provide specialized rendering for rows and columns of table structures:
Rows and columns of a table structure can have labels whose position is determined by label models. The following methods defined in ITable can be used to add labels to IStripe instances:
ILabel AddLabel(IStripe item, ILabelModelParameter labelModelParameter, ILabelStyle style, string text, SizeD preferredSize, object tag) |
|
Description | Adds a label to an IStripe instance, i.e., to a row or column of a table structure. |
ILabel AddLabel(IStripe item, string text) |
|
Description | Convenience (extension) method(s) for interface ITable to add a label to an IStripe instance. |
Class StretchStripeLabelModel
is specifically tailored to support positions for the labels of rows and columns
in a table structure.
It is used as the default label model when adding labels to an IStripe.
Example 4.46, “Row and column labels” illustrates labels in a tabular presentation with a single row and a single column together with their setup in code.
Example 4.46. Row and column labels
![]() |
Row and column labels at both ends of row and column, respectively. |
// 'graph' is of type yWorks.yFiles.UI.Model.IGroupedGraph. // 'table' is of type yWorks.yFiles.UI.Model.ITable. // Setup of row and column default insets. table.RowDefaults.Insets = new InsetsD(30, 5, 30, 5); table.ColumnDefaults.Insets = new InsetsD(5, 30, 5, 30); // Create a single column. IColumn column = table.CreateColumn(100); // Add and configure two labels for the column. table.AddLabel(column, "Column North"); table.AddLabel(column, StretchStripeLabelModel.South, "Column South"); // Create a single row. IRow row = table.CreateRow(100); // Add and configure two labels for the row. table.AddLabel(row, "Row West"); table.AddLabel(row, StretchStripeLabelModel.East, "Row East"); // Using TableNodeStyle to render the table structure. TableNodeStyle tableStyle = new TableNodeStyle(table); tableStyle.BackgroundStyle = new ShapeNodeStyle { Brush = new SolidColorBrush(Color.FromRgb(248, 236, 201)) }; // Create a top-level group node and bind, via the TableNodeStyle, the table to // it. graph.CreateGroupNode(graph.Hierarchy.Root, table.Layout.ToRectD(), tableStyle);
Note that row labels that use StretchStripeLabelModel.West are automatically rotated 90 degrees counterclockwise while row labels that use StretchStripeLabelModel.East are rotated 90 degress clockwise.
Input mode class TableEditorInputMode
is a specialized controller that brings additional support for handling mouse gestures
and keyboard interaction specific to the tabular representation of a diagram.
The additional mouse gesture support covers, for example:
TableEditorInputMode can be used in conjunction with input mode GraphEditorInputMode as shown in the following code snippet, or can be used stand-alone.
Example 4.47. Enabling TableEditorInputMode to handle mouse gestures
// 'geim' is of type yWorks.yFiles.UI.Input.GraphEditorInputMode. geim.AddConcurrent(new TableEditorInputMode());
Upon initialization, TableEditorInputMode creates and installs the input modes listed in Table 4.31, “Input modes used by TableEditorInputMode” as concurrent input modes. If used in conjunction with GraphEditorInputMode, the ClickInputMode, KeyboardInputMode, and TextEditorInputMode child input modes of TableEditorInputMode are disabled and the respective GraphEditorInputMode counterparts are enabled.
Table 4.31. Input modes used by TableEditorInputMode
Type Name | Description |
---|---|
ResizeStripeInputMode![]() |
Handles resizing of rows and columns. |
ReparentStripeInputMode![]() |
Recognizes and handles row (column) re-parenting/reordering mouse gestures. |
ClickInputMode![]() |
Recognizes mouse clicks, including double clicks. This child input mode is disabled, if TableEditorInputMode is used as a child input mode of GraphEditorInputMode. |
KeyboardInputMode![]() |
Recognizes key events. This child input mode is disabled, if TableEditorInputMode is used as a child input mode of GraphEditorInputMode. |
StripeDropInputMode![]() |
Handles drag'n'drop operations of nodes onto the canvas. Facilitate convenient creation of new rows and columns, including visual feedback for a user. This child input mode is disabled, if TableEditorInputMode is used as a child input mode of GraphEditorInputMode. |
TextEditorInputMode![]() |
Handles label editing. This child input mode is disabled, if TableEditorInputMode is used as a child input mode of GraphEditorInputMode. |
Each of the input modes can be obtained or replaced using a like-named property defined by TableEditorInputMode.
In order for TableEditorInputMode and its child input modes to properly recognize the connection between a table structure/table model and its group node, they rely on the table model being bound to the group node. The input modes query the group node's look-up and expect to find a table model:
ITable table = groupNode.Get<ITable>();
Class TableEditorInputMode provides properties and callbacks that allow for fine-grained
control over the support for user interaction.
Table 4.32, “TableEditorInputMode customization” lists the customization
properties of class TableEditorInputMode.
Most of the properties support that combinations of stripe types can be specified
using the constants defined in the StripeTypes
enumeration type.
Table 4.32. TableEditorInputMode customization
Name | Purpose |
---|---|
ClickSelectableItems![]() |
A combination of StripeTypes that specifies whether rows and/or columns can be selected by mouse clicks. |
SelectableItems![]() |
A combination of StripeTypes that specifies whether rows and/or columns can be selected. |
LabelEditableItems![]() |
A combination of StripeTypes that specifies whether labels of rows and/or columns can be edited. |
DeletableItems![]() |
A combination of StripeTypes that specifies whether rows and/or columns can be deleted. |
ClickSelectableRegions![]() |
A combination of StripeSubregion![]() |
Hit-testing support in a table structure takes into account the overall rendering order of rows and columns (as defined by abstract class AbstractTableNodeStyle) as well as the complexities introduced by nested rows and columns.
By default, the specific needs for hit-testing support in a table structure are
provided by the StripeHitTestEnumerator
class.
The actual instance that is used is also available in the group node's look-up.
The following convenience methods in TableEditorInputMode help in finding which
part(s) of a table structure is (are) underneath a given location.
The StripeSubregionDescriptor
class encodes both the stripe, i.e., row or column, and the actual subregion within
the stripe.
StripeSubregionDescriptor FindStripe( PointD location, StripeTypes stripeTypes, StripeSubregion subregions, Predicate<StripeSubregionDescriptor> predicate) |
|
Description | Depending on the rendering order of rows and columns, returns the subregion of either a row or a column that is underneath the given location. |
IEnumerable<StripeSubregionDescriptor> FindStripes( PointD location, StripeTypes stripeTypes, StripeSubregion subregions, Predicate<StripeSubregionDescriptor> predicate) |
|
Description | Returns an ordered list of subregions of (nested) rows and columns that are underneath the given location. The ordering takes into account the rendering order of rows and columns and also the nesting of child rows (columns). |
The StripeSubregion enumeration
defines constants for the different subregions within a stripe.
Figure 4.39, “Stripe subregions in a column” depicts the default definitions of these
subregions together with their corresponding constants for the column of the table
structure in Example 4.46, “Row and column labels”.
The stripe subregions for a row are defined analogously.
Figure 4.39. Stripe subregions in a column
![]() |
![]() |
![]() |
![]() |
The column's Stripe![]() |
Header![]() |
LeadingHeader![]() |
and TrailingHeader![]() |
The hit-testing for the subregions can be handled through dedicated IHitTestable
implementations.
The IStripeHitTestHelper
interface
bundles all stripe-related IHitTestable instances and makes the default implementations
conveniently accessible.
TableEditorWindow demonstrates user interaction and shows how to customize it. The demo also presents a way to create new rows and columns via drag'n'drop gestures.
Automatic layout for a diagram containing table structures that are modeled using
ITable implementations can be conveniently invoked using class LayoutExecutor,
which performs all necessary setup.
Internally, LayoutExecutor uses the TableLayoutConfigurator
class.
Example 4.48. Using LayoutExecutor for automatic layout of table structures
// 'graphControl' is of type yWorks.yFiles.UI.GraphControl. // Configure IncrementalHierarchicLayouter. var ihl = new IncrementalHierarchicLayouter() { ComponentLayouterEnabled = false, LayoutOrientation = LayoutOrientation.LeftToRight, OrthogonalRouting = true, RecursiveGroupLayering = false }; ((SimplexNodePlacer) ihl.NodePlacer).BaryCenterMode = true; // Start layout. try { var layoutExecutor = new LayoutExecutor(graphControl, ihl) { Duration = TimeSpan.FromMilliseconds(500), UpdateContentRect = true }; layoutExecutor.TableLayoutConfigurator.CompactionEnabled = false; layoutExecutor.Start(); } catch (Exception exception) { Console.WriteLine(exception); }
TableLayoutConfigurator converts relevant parts of the table structure into PartitionGrid-based information that is understood by IncrementalHierachicLayouter which supports automatic tabular layout. In particular, this information includes:
Note that the row and column is determined geometrically, by the node's center coordinates.
The following tutorial demo applications show how to create table models using the Table class and how to use the TableNodeStyle node style implementation for tabular data representation:
Copyright ©2004-2015, yWorks GmbH. All rights reserved. |