Grid Patterns

It is common to want to display a grid of lines drawn at regular intervals. You may also want to force dragged parts to be aligned on grid points, and to resize parts to be multiples of the grid cell size.

Grids are implemented using a type of Panel, Panel.Grid. Grid Panels, like most other types of Panels, can be used within Nodes or any other kind of Part. However when they are used as the Diagram.grid, they are effectively infinite in extent.

Unlike in other kinds of Panels, Grid Panel elements must be Shapes that are only used to control how the grid lines or grid bars are drawn.

See samples that make use of grids in the samples index.

Default Grid

To display a grid pattern in the background of the diagram, you can just make the Diagram.grid visible:


  diagram.grid.visible = true;

  diagram.nodeTemplate =
    new go.Node("Auto")
      .add(
        new go.Shape("Rectangle", { fill: "lightgray" }),
        new go.TextBlock({ margin: 5 })
          .bind("text", "key")
      );
  const nodeDataArray = [
    { key: "Alpha" }, { key: "Beta" }, { key: "Gamma" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray);

Grid Snapping

The DraggingTool and ResizingTool can change their behavior based on the background grid pattern, if you set the DraggingTool.isGridSnapEnabled and/or ResizingTool.isGridSnapEnabled properties to true.

Setting DraggingTool.isGridSnapEnabled to true will not affect disconnected Links, but these can snap if you define a custom Part.dragComputation to do so on the Link template.


  diagram.grid.visible = true;
  diagram.toolManager.draggingTool.isGridSnapEnabled = true;
  diagram.toolManager.resizingTool.isGridSnapEnabled = true;

  diagram.nodeTemplate =
    new go.Node("Auto", { resizable: true })
      .add(
        new go.Shape("Rectangle", { fill: "lightgray" }),
        new go.TextBlock({ margin: 5 })
          .bind("text", "key")
      );
  const nodeDataArray = [
    { key: "Alpha" }, { key: "Beta" }, { key: "Gamma" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray);

Simple Grid Customization

You can change the size of the grid cell by setting Panel.gridCellSize:


  diagram.grid.visible = true;
  diagram.grid.gridCellSize = new go.Size(30, 20);
  diagram.toolManager.draggingTool.isGridSnapEnabled = true;
  diagram.toolManager.resizingTool.isGridSnapEnabled = true;

  diagram.nodeTemplate =
    new go.Node("Auto", { resizable: true })
      .add(
        new go.Shape("Rectangle", { fill: "lightgray" }),
        new go.TextBlock({ margin: 5 })
          .bind("text", "key")
      );
  const nodeDataArray = [
    { key: "Alpha" }, { key: "Beta" }, { key: "Gamma" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray);

The cell size used when snapping the locations of Parts during a drag need not be exactly the same as the background grid's cell size. The value of DraggingTool.gridSnapCellSize takes precedence over the Panel.gridCellSize. Note that if DraggingTool.gridSnapCellSize is set but ResizingTool.cellSize is not, Parts will use the DraggingTool.gridSnapCellSize value when resizing.


  diagram.grid.visible = true;
  diagram.toolManager.draggingTool.isGridSnapEnabled = true;
  diagram.toolManager.resizingTool.isGridSnapEnabled = true;

  // snap to every other point both vertically and horizontally
  // (the default background grid has a cell size of 10x10)
  diagram.toolManager.draggingTool.gridSnapCellSize = new go.Size(20, 20);

  diagram.nodeTemplate =
    new go.Node("Auto", { resizable: true })
      .add(
        new go.Shape("Rectangle", { fill: "lightgray" }),
        new go.TextBlock({ margin: 5 })
          .bind("text", "key")
      );
  const nodeDataArray = [
    { key: "Alpha" }, { key: "Beta" }, { key: "Gamma" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray);

Custom Grids

Grid patterns are implemented by the Panel class when its Panel.type is Panel.Grid. The elements of a Grid Panel must be Shapes whose Shape.figure is one of a small set of known kinds of figures. The only figures it can accept are: "LineH", "LineV", "BarH", and "BarV". The two "Line" figures result in stroked lines separating the grid cells; the two "Bar" figures result in filled rectangles in the grid cells.

Here is a simple grid consisting of blue horizontal lines and green vertical lines:


  diagram.grid =
    new go.Panel("Grid", { gridCellSize: new go.Size(25, 25) })
      .add(
        new go.Shape("LineH", { stroke: "blue" }),
        new go.Shape("LineV", { stroke: "green" })
      );

The Shape.interval property is also used by a Grid Panel to determine how frequently a line should be drawn. The value should be a positive integer specifying how many cells there are between drawings of this particular line. So if you wanted darker blue and darker green lines every five cells:


  diagram.grid =
    new go.Panel("Grid", { gridCellSize: new go.Size(10, 10) })
      .add(
        new go.Shape("LineH", { stroke: "lightblue" }),
        new go.Shape("LineV", { stroke: "lightgreen" }),
        new go.Shape("LineH", { stroke: "blue", interval: 5 }),
        new go.Shape("LineV", { stroke: "green", interval: 5 })
      );

  diagram.nodeTemplate =
    new go.Node("Auto", { resizable: true })
      .add(
        new go.Shape("Rectangle", { fill: "lightgray" }),
        new go.TextBlock({ margin: 5 })
          .bind("text", "key")
      );
  const nodeDataArray = [
    { key: "Alpha" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray);

Note that the Shapes are drawn in the order in which they appear in the Panel, so you can see that the dark blue horizontal lines are drawn in front of the light green vertical lines, and that the dark green vertical line crosses in front of the dark blue horizontal lines.

Here is the definition of the predefined Diagram.grid:


  diagram.grid =
    new go.Panel("Grid", {
        name: "GRID",
        visible: false,
        gridCellSize: new go.Size(10, 10),
        gridOrigin: new go.Point(0, 0)
      })
      .add(
        new go.Shape("LineH", { stroke: "lightgray", strokeWidth: 0.5, interval: 1 }),
        new go.Shape("LineH", { stroke: "gray", strokeWidth: 0.5, interval: 5 }),
        new go.Shape("LineH", { stroke: "gray", strokeWidth: 1.0, interval: 10 }),
        new go.Shape("LineV", { stroke: "lightgray", strokeWidth: 0.5, interval: 1 }),
        new go.Shape("LineV", { stroke: "gray", strokeWidth: 0.5, interval: 5 }),
        new go.Shape("LineV", { stroke: "gray", strokeWidth: 1.0, interval: 10 })
      );

  diagram.grid.visible = true;  // so that this example shows the standard grid
  diagram.div.style.background = "white";

You can get a green-bar pattern by using the "BarH" figure. Note the use of Shape.fill instead of Shape.stroke and explicitly setting the GraphObject.height:


  diagram.grid =
    new go.Panel("Grid", { gridCellSize: new go.Size(50, 50) })
      .add(
        new go.Shape("BarH", { fill: "lightgreen", interval: 2, height: 50 })
      );

  diagram.nodeTemplate =
    new go.Node("Auto", {
        dragComputation: (node, pt, gridpt) => {
          pt.y = Math.round(pt.y/100)*100;
          return pt;
        }
      })
      .add(
        new go.Shape("Rectangle", { fill: "lightgray" }),
        new go.TextBlock({ margin: 5 })
          .bind("text", "key")
      );
  const nodeDataArray = [
    { key: "Alpha" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray);

This example also demonstrates how one can use the Part.dragComputation property to customize where the user can drag the node. In this case the Part.location.y is limited to be multiples of 100, corresponding to the rows of cells filled by the green bars.

To get a tablecloth effect, one can use both vertical and horizontal bars with a translucent color:


  diagram.grid =
    new go.Panel("Grid", { gridCellSize: new go.Size(100, 100) })
      .add(
        new go.Shape("BarV", { fill: "rgba(255,0,0,0.1)", width: 50 }),
        new go.Shape("BarH", { fill: "rgba(255,0,0,0.1)", height: 50 })
      );

  diagram.toolManager.draggingTool.isGridSnapEnabled = true;

  diagram.nodeTemplate =
    new go.Node("Auto", { width: 50, height: 50 })
      .add(
        new go.Shape("Rectangle", { fill: "lightgray" }),
        new go.TextBlock({ margin: 5 })
          .bind("text", "key")
      );
  const nodeDataArray = [
    { key: "Alpha" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray);

This example limits dragging of all nodes by setting DraggingTool.isGridSnapEnabled to true.

Another custom grid: dots


  diagram.div.style.background = "white";

  diagram.grid =
    new go.Panel("Grid")
      .add(
        new go.Shape("LineH", { strokeWidth: 1, strokeDashArray: [0, 9, 1, 0] })
      );

  diagram.toolManager.draggingTool.isGridSnapEnabled = true;

  diagram.nodeTemplate =
    new go.Node("Auto")
      .add(
        new go.Shape("Rectangle", { fill: "white" }),
        new go.TextBlock({ margin: 5 })
          .bind("text", "key")
      );
  const nodeDataArray = [
    { key: "Alpha" }
  ];
  diagram.model = new go.GraphLinksModel(nodeDataArray);

Here is an example of using a "Grid" Panel as a regular data bound element in a Node:



  diagram.nodeTemplate =
    new go.Node("Auto", { resizable: true, resizeObjectName: "GRID" })
      .add(
        new go.Shape("Rectangle", { fill: "transparent" }),
        new go.Panel("Grid", {
            name: "GRID",
            desiredSize: new go.Size(100, 100),
            gridCellSize: new go.Size(20, 20)
          })
          .bindTwoWay("desiredSize", "size", go.Size.parse, go.Size.stringify)
          .bindTwoWay("gridCellSize", "cell", go.Size.parse, go.Size.stringify)
          .add(
            new go.Shape("LineV")
              .bind("stroke"),
            new go.Shape("LineH")
              .bind("stroke")
          )
      );

  diagram.model = new go.GraphLinksModel([
    { key: "Alpha", cell: "25 25", stroke: "lightgreen" },
    { key: "Beta", size: "150 75", cell: "15 30" }
  ]);

Other Considerations

A Grid Panel should have a non-null background if it needs to be pickable. One cannot set or bind the Panel.itemArray of a Grid Panel.

Events on the Shapes will be ignored. Shapes in a Grid Panel must not be scaled or rotated.