http://www.developer.com/net/vb/article.php/3725586/Visualizing-LINQ-Sequences-with-GDI.htm
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. 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. The utility is capable of producing rectangle sub-divisions as suggested by the roughly created grid in the comment in the first part of the listing. You can specify the number of vertical and horizontal sub-rectangles, which horizontal and vertical sub-rectangle to return, and CenterRect (at the end) will return a rectangle centered vertically and horizontally within another rectangle. The reason shared methods were used is that the Rectangles class itself stores no data; that is, it contains no fields, properties, or events. Following a general OOP rule such a class is naturally specified as a class containing all shared members. Next, I wanted to able to track individual rectangles, so a state-full RectangleManager was defined. This class keeps track of its horizontal and vertical sub-rectangles (with hSegments and vSegments), the desired padding between rectangles, and the original outer rectangle. This class is useful for keeping track of a specific rectangle set being used. Listing 2 contains the RectangleManager. Listing 2: The RectangleManager is state-full—keeping track of the rectangle under management. A LINQ query returns a sequence. (Think collection!) By using ellipses, you can visualize the sequence as a linear row of circles with each circle containing the text (or a representation of the text) in the circle itself. Listing 3 shows the form for representing the sequence visually, and Listing 4 contains a concrete behavior class that contains the draw-sequence behavior. (You can look up the State Behavior Pattern on dofactory.com or the GoF book on Design Patterns.) Listing 3: The Form using a state behavior pattern; the Form shows the visual representation of the LINQ query. Listing 4: The DrawSequenceState class that contains the sequence and the call using the RectangleManager to represent that sequence. The LINQ query is shown in Listing 4 on the line Dim result = From o In i Where o Mod 2 = 0 Select 0. This query selects the even numbered integers as shown in Figure 1. Figure 1: The results of the LINQ query returning even numbers in a set of integers 0 through 8. The base class for the state pattern MyStatePattern is shown in Listing 5. Listing 5: The abstract base class for the state pattern. Everything is Rectangles in a 2D rectilinear world. In this article, I demonstrated how you can draw specific rectangular sub-regions as rectangles or ellipses (or really as anything that is constrained by a rectangular region). That was followed by an application of this technique that visualizes LINQ queries. I hope you find the code useful. Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his upcoming book LINQ Unleashed for C# due in Spring 2008. You may contact him for technology questions at pkimmel@softconcepts.com. If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org. Glugnet opened a users group branch in Flint, Michigan in August 2007. If you are interested in attending, check out the www.glugnet.org web site for updates. Copyright © 2008 by Paul T. Kimmel. All Rights Reserved.
Visualizing LINQ Sequences with GDI+
February 4, 2008
Introduction
Defining the 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
Defining a State-Full Rectangle Manager
Imports System.Collections
Public Class RectangleManager
'Implements IEnumerator
' note to self - implement IEnumerator so we can do a foreach
' over the segments
Public Sub New(ByVal Rect As Rectangle, _
ByVal hSegments As Integer, ByVal vSegments As Integer)
Me.Rect = Rect
Me.vSegments = vSegments
Me.hSegments = hSegments
End Sub
Public Sub New(ByVal Rect As Rectangle, _
ByVal hSegments As Integer, _
ByVal vSegments As Integer, ByVal padding As Integer)
Me.Rect = Rect
Me.vSegments = vSegments
Me.hSegments = hSegments
Me.padding = padding
End Sub
Private Rect As Rectangle
Private vSegments As Integer
Private hSegments As Integer
Private padding As Integer = 4
Public Function GetSubRect(ByVal xSegment As Integer, _
ByVal ySegment As Integer) As Rectangle
Return Rectangles.GetSubRect(Rect, xSegment, ySegment, _
hSegments, _
vSegments, padding)
End Function
Public Function CenterRect(ByVal size As SizeF, _
ByVal xSegment As Integer, _
ByVal ySegment As Integer)
Return Rectangles.CenterRect(size, GetSubRect(xSegment, _
ySegment))
End Function
End Class
Visualizing a LINQ Query with the Rectangle Manager
Public Class Form1
Private state As MyStatePattern
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
state = New DrawSequenceState(p)
End Sub
Private p As Pen = New Pen(Color.Red, 4)
Private Sub Form1_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles Me.Paint
state.Draw(e.Graphics, Font)
End Sub
Private Sub DrawRectanglesToolStripMenuItem_Click(ByVal _
sender As System.Object, _
ByVal e As System.EventArgs) Handles _
DrawRectanglesToolStripMenuItem.Click
state = New DrawRectState(p)
Invalidate()
End Sub
Private Sub DrawEllipsesToolStripMenuItem_Click(ByVal sender _
As System.Object, _
ByVal e As System.EventArgs) Handles _
DrawEllipsesToolStripMenuItem.Click
state = New DrawEllipseState(p)
Invalidate()
End Sub
Private Sub DrawSequenceToolStripMenuItem_Click(ByVal sender _
As System.Object, _
ByVal e As System.EventArgs) Handles _
DrawSequenceToolStripMenuItem.Click
state = New DrawSequenceState(p)
Invalidate()
End Sub
End Class
Imports System.Drawing
Public Class DrawSequenceState
Inherits MyStatePattern
''' <summary>
''' Initializes a new instance of the DrawSequenceState class.
''' </summary>
Public Sub New(ByVal pen As Pen)
MyBase.New(pen)
End Sub
Public Overrides Sub Draw(ByVal Graphics As _
System.Drawing.Graphics, _
ByVal Font As System.Drawing.Font)
Dim i = New Integer() {1, 2, 3, 4, 5, 6, 7, 8, 9}
Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
Dim r As Rectangle = New Rectangle(10, 50, 200, 50)
Graphics.DrawRectangle(MyPen, r)
Dim result = From o In i Where o Mod 2 = 0 Select o
Dim instance As RectangleManager = _
New RectangleManager(r, result.Count, 1)
For x = 0 To result.Count - 1
Dim f As Rectangle = instance.GetSubRect(x, 0)
Graphics.FillEllipse(Brushes.Lavender, f.X, f.Y, _
f.Width, f.Height)
Graphics.DrawEllipse(Pens.Black, f.X, f.Y, _
f.Width, f.Height)
Dim t As Rectangle = instance.GetSubRect(x, 0)
Dim s As SizeF = Graphics.MeasureString( _
result.ElementAt(x).ToString(), Font)
Dim textRect As Rectangle = Rectangles.CenterRect(s, t)
Graphics.DrawString(result.ElementAt(x).ToString(), _
Font, _
Brushes.Blue, textRect.X, textRect.Y)
Next
End Sub
End Class

Public MustInherit Class MyStatePattern
Private FMyPen As Pen
Public Property MyPen() As Pen
Get
Return FMyPen
End Get
Set(ByVal Value As Pen)
FMyPen = Value
End Set
End Property
Public Sub New(ByVal pen As Pen)
FMyPen = pen
End Sub
Public MustOverride Sub Draw(ByVal Graphics As Graphics, _
ByVal Font As Font)
End Class
Summary
About the Author