Microsoft & .NETVisual BasicVisualizing LINQ Sequences with GDI+

# Visualizing LINQ Sequences with GDI+

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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

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

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

Subscribe to Developer Insider for top news, trends & analysis