Visualizing LINQ Sequences with GDI+

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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read