 # 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 / _
Dim newHeight As Integer = height / _

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 / _
Dim newHeight As Single = height / _

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, _

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, _

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
```

# Visualizing LINQ Sequences with GDI+

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.

### Defining a State-Full Rectangle Manager

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.

```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
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, _
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

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.

```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

sender As System.Object, _
ByVal e As System.EventArgs) Handles _

state = New DrawRectState(p)
Invalidate()

End Sub

As System.Object, _
ByVal e As System.EventArgs) Handles _

state = New DrawEllipseState(p)
Invalidate()

End Sub

As System.Object, _
ByVal e As System.EventArgs) Handles _

state = New DrawSequenceState(p)
Invalidate()

End Sub
End Class
```

# Visualizing LINQ Sequences with GDI+

Listing 4: The DrawSequenceState class that contains the sequence and the call using the RectangleManager to represent that sequence.

```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
```

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.

[PaulK1.jpg]

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.

```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

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.