Introduction
It’s all rectangles. Draw a rectangle and you need a rectangle control. Draw an ellipse and you need the bounding rectangle. Drawing controls, graphics, positioning forms—these all require rectangles.
My upcoming book, LINQ Unleashed for C# from Sams, talks about Language INtegrated Query. LINQ produces sequences. I wanted to visualize those sequences for the books—I got tired of creating them by hand in MS-Paint—so I thought I’d write a utility to generate the LINQ visualizations for me. Guess what? Rectangles are needed to lay out each item in the sequence.
In this article, a general utility for laying out rectangles and sub-rectangles in a line is provided. The utility also will lay out multiple rows of rectangles and sub-rectangles. Finally, the utility is used to display the results of LINQ queries. Enjoy.
Defining the Rectangle Utility
The basic objective is to define a simple utility that will lay out rectangles and sub-rectangles. The utility includes the ability to center a rectangle; this can be used to lay out text within the rectangle.
The solution chosen is a shared class that defines a couple of functions to return a rectangle and some number and position of smaller rectangles within the outer rectangle. The solution supports inserting padding between rectangles for a uniform, evenly spaced layout. Finally, some of the methods are overloaded to support rectangles with integer coordinates and those with floating point coordinates. Listing 1 contains the solution.
Listing 1: A general rectangle utility.
Imports System.Drawing Public Class Rectangles ' example: a rectangle divided four across and three down ' get h=2 and v = 2 the x shows the sub-rect we would get ' ----------------- ' | | | | | ' ----------------- ' | | x | | | ' ----------------- ' | | | | | ' ----------------- ' Given a rectangle return a sub-rectangle based on horizontal ' and vertical sub-divsions Public Shared Function GetSubRect(ByVal x As Integer, _ ByVal y As Integer, _ ByVal width As Integer, ByVal height As Integer, _ ByVal horizontalSegment As Integer, _ ByVal verticalSegment As Integer, _ ByVal numberOfHorizontalSegments As Integer, _ ByVal numberOfVerticalSegments As Integer) As Rectangle Return GetSubRect(x, y, width, height, horizontalSegment, _ verticalSegment, numberOfHorizontalSegments, _ numberOfVerticalSegments, 0) End Function ' Given a rectangle return a sub-rectangle based on horizontal ' and vertical sub-divsions Public Shared Function GetSubRectF(ByVal x As Integer, _ ByVal y As Integer, _ ByVal width As Single, ByVal height As Single, _ ByVal horizontalSegment As Single, _ ByVal verticalSegment As Single, _ ByVal numberOfHorizontalSegments As Integer, _ ByVal numberOfVerticalSegments As Integer) As RectangleF Return GetSubRect(x, y, width, height, horizontalSegment, _ verticalSegment, numberOfHorizontalSegments, _ numberOfVerticalSegments, 0) End Function ' Given a rectangle with padding in between ' return a sub-rectangle based on horizontal and vertical ' sub-divsions Public Shared Function GetSubRect(ByVal x As Integer, _ ByVal y As Integer, _ ByVal width As Integer, ByVal height As Integer, _ ByVal horizontalSegment As Integer, _ ByVal verticalSegment As Integer, _ ByVal numberOfHorizontalSegments As Integer, _ ByVal numberOfVerticalSegments As Integer, _ ByVal padding As Integer) As Rectangle Dim newWidth As Integer = width / _ numberOfHorizontalSegments - padding Dim newHeight As Integer = height / _ numberOfVerticalSegments - padding Return New Rectangle(x + (newWidth + padding) * _ horizontalSegment, _ y + (newHeight + padding) * verticalSegment, _ newWidth, newHeight) End Function ' Given a rectangle with padding in between ' return a sub-rectangle based on horizontal and vertical ' sub-divsions Public Shared Function GetSubRectF(ByVal x As Integer, ByVal y As Integer, _ ByVal width As Single, ByVal height As Single, _ ByVal horizontalSegment As Single, _ ByVal verticalSegment As Single, _ ByVal numberOfHorizontalSegments As Integer, _ ByVal numberOfVerticalSegments As Integer, _ ByVal padding As Single) As RectangleF Dim newWidth As Single = width / _ numberOfHorizontalSegments - padding Dim newHeight As Single = height / _ numberOfVerticalSegments - padding Return New Rectangle(x + (newWidth + padding) * _ horizontalSegment, y + (newHeight + padding) * _ verticalSegment, _ newWidth, newHeight) End Function ' Given a rectangle return a sub-rectangle based on horizontal _ ' and vertical sub-divsions Public Shared Function GetSubRect(ByVal boundingRect _ As Rectangle, _ ByVal horizontalSegment As Integer, _ ByVal verticalSegment As Integer, _ ByVal numberOfHorizontalSegments As Integer, _ ByVal numberOfVerticalSegments As Integer) As Rectangle Return GetSubRect(boundingRect.X, boundingRect.Y, _ boundingRect.Width, _ boundingRect.Height, horizontalSegment, _ verticalSegment, numberOfHorizontalSegments, _ numberOfVerticalSegments) End Function ' Given a rectangle return a sub-rectangle based on horizontal ' and vertical sub-divsions Public Shared Function GetSubRectF(ByVal boundingRect _ As RectangleF, _ ByVal horizontalSegment As Single, _ ByVal verticalSegment As Single, _ ByVal numberOfHorizontalSegments As Integer, _ ByVal numberOfVerticalSegments As Integer) As RectangleF Return GetSubRectF(boundingRect.X, boundingRect.Y, _ boundingRect.Width, _ boundingRect.Height, horizontalSegment, _ verticalSegment, numberOfHorizontalSegments, _ numberOfVerticalSegments) End Function ' Given a rectangle with padding in between ' return a sub-rectangle based on horizontal and vertical ' sub-divsions Public Shared Function GetSubRect(ByVal boundingRect _ As Rectangle, _ ByVal horizontalSegment As Integer, _ ByVal verticalSegment As Integer, _ ByVal numberOfHorizontalSegments As Integer, _ ByVal numberOfVerticalSegments As Integer, _ ByVal padding As Integer) As Rectangle Return GetSubRect(boundingRect.X, boundingRect.Y, _ boundingRect.Width, _ boundingRect.Height, horizontalSegment, _ verticalSegment, numberOfHorizontalSegments, _ numberOfVerticalSegments, padding) End Function ' Given a rectangle with padding in between ' return a sub-rectangle based on horizontal and vertical ' sub-divsions Public Shared Function GetSubRectF(ByVal boundingRect _ As RectangleF, _ ByVal horizontalSegment As Single, _ ByVal verticalSegment As Single, _ ByVal numberOfHorizontalSegments As Integer, _ ByVal numberOfVerticalSegments As Integer, _ ByVal padding As Single) As RectangleF Return GetSubRectF(boundingRect.X, boundingRect.Y, _ boundingRect.Width, _ boundingRect.Height, horizontalSegment, _ verticalSegment, numberOfHorizontalSegments, _ numberOfVerticalSegments, padding) End Function ' Given the size of something get a centered horizontally ' and vertically centered rectangle Public Shared Function CenterRect(ByVal size As SizeF, _ ByVal boundingRectangle As Rectangle) Return New Rectangle(boundingRectangle.X + _ (boundingRectangle.Width - size.Width) / 2, _ boundingRectangle.Y + (boundingRectangle.Height - _ size.Height) / 2, _ boundingRectangle.Width, _ boundingRectangle.Height) End Function End Class