Custom diagram canvas pagination

In this post we’ll show how to implement Visio-like dynamic pagination of the diagram canvas, expanding or shrinking it while users draw nodes interactively, and rendering custom page borders. This is demonstrated using MindFusion Windows Forms Diagram control, but equivalent APIs are provided by MindFusion diagramming libraries for other platforms.

Start by creating a new WinForms project and adding the MindFusion.Diagramming package in NuGet Package Manager. After installing the package, add DiagramView and Diagram instances to the form, setting view’s Diagram property to the form’s member.

Add the following fields to the form class to keep track of the current pages:

SizeF pageSize = new SizeF(150, 100);
int pageCols = 1;
int pageRows = 1;

In form’s Load event handler, set the following properties to let us control the canvas size manually, and render the area outside diagram.Bounds in a different color:

diagram.ExteriorBrush = new SolidBrush(Color.Gray);
diagram.AutoResize = AutoResize.None;
diagram.Bounds = new RectangleF(new PointF(), pageSize);
diagram.RestrictItemsToBounds = RestrictToBounds.NoRestriction;

Handle NodeCreating and NodeModifying events in order to add or remove pages while users draw on the canvas. Handle DrawForeground event to draw custom page borders:

diagram.NodeCreating += OnNodeCreating;
diagram.NodeModifying += OnNodeModifying;
diagram.DrawForeground += OnDrawForeground;

The pages are arranged in a grid that fits the rectangle returned by GetContentBounds method:

void ResizePageGrid(DiagramNode newNode = null)
{
	var contentBounds = diagram.GetContentBounds(false, true);
	if (newNode != null)
	{
		contentBounds = RectangleF.Union(
			contentBounds, newNode.Bounds);
	}

	int newPageCols = 1 + (int)(contentBounds.Right / pageSize.Width);
	int newPageRows = 1 + (int)(contentBounds.Bottom / pageSize.Height);

	if (pageCols != newPageCols || pageRows != newPageRows)
	{
		pageCols = newPageCols;
		pageRows = newPageRows;

		diagram.Bounds = new RectangleF(
			0, 0, pageSize.Width * pageCols, pageSize.Height * pageRows);

		diagramView.RecreateCacheImage();
		diagramView.Refresh();
	}
}

void OnNodeCreating(object sender, NodeValidationEventArgs e)
{
	ResizePageGrid(e.Node);
}

void OnNodeModifying(object sender, NodeValidationEventArgs e)
{
	ResizePageGrid();
}

Page borders are drawn by looping over the grid rows and columns:

void OnDrawForeground(object sender, DiagramEventArgs e)
{
	var pen = new System.Drawing.Pen(Color.Black)
	{
		Width = 0,
		DashStyle = System.Drawing.Drawing2D.DashStyle.Dot
	};

	// draw vertical page borders
	for (int c = 0; c <= pageCols; c++)
	{
		float x = c * pageSize.Width;
		e.Graphics.DrawLine(
			pen, x, 0, x, diagram.Bounds.Height);
	}

	// draw horizontal page borders
	for (int r = 0; r <= pageRows; r++)
	{
		float y = r * pageSize.Height;
		e.Graphics.DrawLine(
			pen, 0, y, diagram.Bounds.Width, y);
	}

	pen.Dispose();
}

Now if you drag a node outside the initial page boundaries, the page grid will expand to accommodate the new node position:

The complete sample project is available for download here:

https://mindfusion.eu/_samples/CustomPaging.zip

An alternative way to render pages could be to create a cell for each page in the built-in LaneGrid object. Finally, note that if you don’t need to display all pages on the same canvas, you can use the TabbedDiagramView control to show a multi-page DiagramDocument object.

Enjoy!