
Figure 1 - Part of the ruler.
If you misplaced your ruler, here's an application that will create one for you on your printer! Unfortunately, you'll still need a ruler the first time using it so that you can calibrate the measurement for your particular printer, but once you know the calibration value, you are all set with a fairly accurate ruler. Below is the simple design of our ruler. The ruler itself is drawn in a Form. The only other class used is the Calibration Dialog used to enter and retrieve a calibration value:

This design was reverse engineered using the WithClass 2000 UML Design Tool for VB.NET.
Simply simply determining the resolution in pixels per inch creates the ruler. This information can be retrieved from the graphics object itself:
Sub SizeFormToRuler()
' get resolution, most screens are 96 dpi, but
' you never know...
Dim g As Graphics = Me.CreateGraphics()
Me.HRes = g.DpiX ' Horizontal Resolution
Me.VRes = g.DpiY ' Vertical Resolution
Width = CInt(HRes)
Height = CInt(VRes) * 11
Left = 250
Top = 5
End Sub 'SizeFormToRuler
The actual tick marks and numbers are drawn in the Form_Paint event handler method. This method establishes a Ruler Height in inches and then calls the method to draw the ruler:
Private Sub Form1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs)
Dim g As Graphics = e.Graphics
RulerHeight = 11
DrawRuler(g)
End Sub 'Form1_Paint
The DrawRuler method is the heart of the application. It draws the tick marks and numbers and spaces them according to the number of pixels in an inch (+ a calibration value to make the inch measurement accurate). The Modulus Function is used to determine when it's appropriate to draw a particular tick mark. For example when i modulus PixelsPerInch is equal to 0, then we've found an inch marker.
Private Sub DrawRuler(g As Graphics)
g.DrawRectangle(Pens.Black, ClientRectangle)
g.FillRectangle(Brushes.White, 0, 0, Width, Height)
Dim PixelsPerInch As Single = VRes + CSng(Calibration)
Dim count As Integer = 1 'cycle through every pixel in the ruler at 1/16 pixel intervals
' Mark the appropriate tick values
Dim i As Single
For i = 1 To (CSng(PixelsPerInch) * RulerHeight + 10) - 0.0625F Step 0.0625F
' use the modulus function to determine what type of tick to use
If i Mod PixelsPerInch = 0 Then
g.DrawLine(Pens.Black, 0, i, 25, i)
' Draw a Number at every inch mark
g.DrawString(count.ToString(), TheFont, Brushes.Black, 25, i, New StringFormat())
count += 1
Else
If i * 2 Mod PixelsPerInch = 0 Then
g.DrawLine(Pens.Black, 0, i, 20, i)
Else
If i * 4 Mod PixelsPerInch = 0 Then
g.DrawLine(Pens.Black, 0, i, 15, i)
Else
If i * 8 Mod PixelsPerInch = 0 Then
g.DrawLine(Pens.Black, 0, i, 10, i)
Else
If i * 16 Mod PixelsPerInch = 0 Then
g.DrawLine(Pens.Black, 0, i, 5, i)
End If
End If
End If
End If
End If
Next i
End Sub 'DrawRuler
Using the calibration dialog retrieves the calibration value. This dialog brings up a NumericUpDown control for choosing the number of pixels to offset the inch. The offset can be a negative or positive number of pixels depending upon how your printer is off. I found my printer was off by 4 pixels too small, so my calibration was +4. Below is the calibration dialog:

Fig 3 - Ruler Calibration Dialog.
The Calibration Dialog uses ShowDialog to display the form and retrieves the Calibration Value in the CalibrationValue property of the dialog:
Private Sub CalibrationMenu_Click(sender As Object, e As System.EventArgs)
' Create Calibration Dialog
Dim dlg As New CalibrationForm() ' Set the initial Calibration Value
dlg.CalibrationValue = Calibration ' Show the dialog and retrieve the Calibration Value
If dlg.ShowDialog() = DialogResult.OK Then
Calibration = dlg.CalibrationValue
WriteCalibration() ' write the value out to a file to remember for next time
End If
Invalidate()
End Sub 'CalibrationMenu_Click
Printing the ruler is accomplished using 3 different print controls: The PrintDocument, The PrintDialog, and the PrintPreviewDialog control. Once you've dropped these controls on your form and assigned the PrintDocument Instance to the corresponding Document properties in the PrintDialog and the PrintPreviewDialog, it's fairly easy to set up printing. Below is the routine for PrintPreview. As you can see there is not really much to it.
Private Sub PrintPreviewMenu_Click(sender As Object, e As System.EventArgs)
Me.printPreviewDialog1.ShowDialog()
End Sub 'PrintPreviewMenu_Click
Printing is not much more complicated. Simply open the PrintDialog and once the user has assigned properties, call the Print method on the PrintDocument:
Private Sub PrintMenu_Click(sender As Object, e As System.EventArgs)
If printDialog1.ShowDialog() = DialogResult.OK Then
printDocument1.Print()
End If
End Sub 'PrintMenu_Click
The actual printing occurs in the PrintDocument's PrintPage Event Handler. This is where you put all the GDI routines for drawing the ruler. You can actually use the same drawing routines in your print handler as you do for your Paint Handler. Your just passing in a Printer Graphics Object instead of a Screen Graphics object:
Private Sub printDocument1_PrintPage(sender As Object, e As System.Drawing.Printing.PrintPageEventArgs)
Dim g As Graphics = e.Graphics
Dim ps As PageSettings = e.PageSettings ' Set the ruler height based on the Page Settings of the printer
' That way you can have a larger ruler for a larger piece of paper
RulerHeight = CInt(ps.Bounds.Height / 100.0)
DrawRuler(g)
End Sub 'printDocument1_PrintPage
We've also added persistence to this application to remember the calibration value. The two routines below read and write the calibration value using the StreamReader and StreamWriter classes:
Sub ReadCalibration()
' Determine the path of the file from the application itself
Dim calfile As String = Application.StartupPath + "\cal.txt"
If File.Exists(calfile) Then '
Illegal whitespace in constant
Dim tr As New StreamReader(calfile)
Dim num As String = tr.ReadLine()
Calibration = Convert.ToInt32(num)
tr.Close()
End If
End Sub 'ReadCalibration
Sub WriteCalibration()
Dim calfile As String = Application.StartupPath + "\cal.txt"
Dim tw As New StreamWriter(calfile) '
tw.Flush() ' Write out the calibration value
tw.WriteLine(Calibration.ToString())
tw.Close()
End Sub 'WriteCalibration
Improvements.
This control could be improved by adding a metric ruler option. This is easy enough to accomplish simply by figuring out the pixels per centimeter and then using modulus 10 to get the millimeters. Also printing to multiple pages can create a large ruler. I'm sure you can think of some other ideas with a little measured thought.