Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Advanced Barcode Generation System for Code 39 Standards

0.00/5 (No votes)
20 Mar 2009 1  
Easily develop software to create fully custom Code 39 Barcodes for printing on Avery 8167 labels (document and box labels, or more based on customization).

Introduction

The ITS, LLC Barcode Generation System generates C39 Barcode Labels to be printed on Avery 8167 labels. The software was developed for printing labels to be used on document boxes and documents (both actual documents and chain of custody logs) to track documents with boxes, etc.

This software allows for the following on label creation and configuration:

  • Box Label Printing (80 unique labels per page)
  • Document Jaclet Label Printing (40 unique labels per page, double entry for CCT)
  • Automatic scanning of box barcode label to produce document labels
  • Advanced configuration of drawing styles on barcodes
  • Configuration of printing options for physical delivery of barcode page generation

Background

In terms of background on the software; the software was designed to print on the Avery 8167 labels, which has 80 labels per page -- however, with some configuration, you can use it to print on other style labels as well.

The software is developed on the Microsoft .NET Framework, and heavily uses the Microsoft .NET GDI+ system for drawing barcodes. It does not use a font to write the barcodes.

Using the Code

The code is fairly straightforward, and is very well commented. But as always, if you have any questions or comments, don't hesitate to contact me or leave a comment/question on the CodeProject page.

There are a few direct exerts; well actually, it's almost the complete code from the main Barcode Workspace below.

This is the main Barcode Generation System, allowing for the generation of each barcode image:

Public Class mainBarcodeWorkspace
    Private _encoding As Hashtable = New Hashtable
    Private Const _wideBarWidth As Short = 2
    Private Const _narrowBarWidth As Short = 1
    Private Const _barHeight As Short = 31

    'Here we define the page counters -- this comes into play later!
    Public totalPages As Integer = 1
    Public curPage As Integer = 0

    'Create a new box scan form
    Public boxScanFrm As boxScan = New boxScan

    'Declare the return timer countdown (seconds before return to the box scanner)
    Public returnTimer As Integer = 15

    'Define the main barcode canvas used for the master page
    Dim barcodeCanvas As Bitmap

    'Declare the printing object 
    '(used later for sending the barcode canvas to a printer)
    Private WithEvents m_PrintDocument As Printing.PrintDocument

#Region "Barcode Generation System"
    'This adds keys to the encoding hashtable, 
    'which tells us exactly what we need to draw later in GDI
    'But more or less, these are the encoding bits 
    'for each of the Code39 allowable characters
    Sub ITS_BarcodeC39()
        _encoding.Add("*", "bWbwBwBwb")
        _encoding.Add("-", "bWbwbwBwB")
        _encoding.Add("$", "bWbWbWbwb")
        _encoding.Add("%", "bwbWbWbWb")
        _encoding.Add(" ", "bWBwbwBwb")
        _encoding.Add(".", "BWbwbwBwb")
        _encoding.Add("/", "bWbWbwbWb")
        _encoding.Add("+", "bWbwbWbWb")
        _encoding.Add("0", "bwbWBwBwb")
        _encoding.Add("1", "BwbWbwbwB")
        _encoding.Add("2", "bwBWbwbwB")
        _encoding.Add("3", "BwBWbwbwb")
        _encoding.Add("4", "bwbWBwbwB")
        _encoding.Add("5", "BwbWBwbwb")
        _encoding.Add("6", "bwBWBwbwb")
        _encoding.Add("7", "bwbWbwBwB")
        _encoding.Add("8", "BwbWbwBwb")
        _encoding.Add("9", "bwBWbwBwb")
        _encoding.Add("A", "BwbwbWbwB")
        _encoding.Add("B", "bwBwbWbwB")
        _encoding.Add("C", "BwBwbWbwb")
        _encoding.Add("D", "bwbwBWbwB")
        _encoding.Add("E", "BwbwBWbwb")
        _encoding.Add("F", "bwBwBWbwb")
        _encoding.Add("G", "bwbwbWBwB")
        _encoding.Add("H", "BwbwbWBwb")
        _encoding.Add("I", "bwBwbWBwb")
        _encoding.Add("J", "bwbwBWBwb")
        _encoding.Add("K", "BwbwbwbWB")
        _encoding.Add("L", "bwBwbwbWB")
        _encoding.Add("M", "BwBwbwbWb")
        _encoding.Add("N", "bwbwBwbWB")
        _encoding.Add("O", "BwbwBwbWb")
        _encoding.Add("P", "bwBwBwbWb")
        _encoding.Add("Q", "bwbwbwBWB")
        _encoding.Add("R", "BwbwbwBWb")
        _encoding.Add("S", "bwBwbwBWb")
        _encoding.Add("T", "bwbwBwBWb")
        _encoding.Add("U", "BWbwbwbwB")
        _encoding.Add("V", "bWBwbwbwB")
        _encoding.Add("W", "BWBwbwbwb")
        _encoding.Add("X", "bWbwBwbwB")
        _encoding.Add("Y", "BWbwBwbwb")
        _encoding.Add("Z", "bWBwBwbwb")
    End Sub

    'This returns a color associated with the current line drawing 
    'in GDI, since it's a barcode we primarly want a contrasting
    'color and a light background color, so depending on what 
    'the character is (whether it's a char or white space) we add color
    Protected Function getBCSymbolColor(ByVal symbol As String) _
                       As System.Drawing.Brush
        getBCSymbolColor = Brushes.Black
        If symbol = "W" Or symbol = "w" Then

            getBCSymbolColor = Brushes.White
        End If
    End Function

    'Now we determine whether or not we are going to be drawing 
    'a small or large BC bar on this character code
    Protected Function getBCSymbolWidth(ByVal symbol As String) As Short
        getBCSymbolWidth = _narrowBarWidth
        If symbol = "B" Or symbol = "W" Then
            getBCSymbolWidth = _wideBarWidth
        End If
    End Function

    'Now the fun part, this function is called to generate the actual barcode
    Protected Overridable Function GenerateBarcodeImage(ByVal imageWidth As Short, _
              ByVal imageHeight As Short, ByVal Code As String) As IO.MemoryStream
        'Declare a new bitmap canvas to store our new barcode 
        '(well, it will be new -- we will make it soon)!
        Dim b As New Bitmap(imageWidth, imageHeight, _
                     Imaging.PixelFormat.Format32bppArgb)

        'Create the actualy canvas associated with the barcode drawing       
        Dim canvas As New Rectangle(0, 0, imageWidth, imageHeight)

        'Create our graphics object from our barcode canvas
        Dim g As Graphics = Graphics.FromImage(b)

        'Fill the drawing with a white background
        g.FillRectangle(Brushes.White, 0, 0, imageWidth, imageHeight)

        'Draw the "eye candy" items on the barcode canvas
        'Here we are drawing the gradient directly behind the barcode are at the top
        g.FillRectangle(New Drawing2D.LinearGradientBrush(_
           New Drawing.RectangleF(1, 1, 169, 30), Color.White, _
           Color.LightGray, Drawing2D.LinearGradientMode.Vertical), 1, 1, 169, 30)

        'Now we draw the seperation line (under barcode) 
        'and the liner gradient background
        g.FillRectangle(New Drawing2D.LinearGradientBrush(_
          New Drawing.RectangleF(1, 34, 169, 15), Color.LightGray, _
          Color.White, Drawing2D.LinearGradientMode.Vertical), 1, 34, 169, 15)
        g.FillRectangle(New SolidBrush(Color.Black), 1, 33, 169, 1)

        'Now that we have the "fine art" drawn, 
        'let's switch over to high-quality rendering for our text and images
        'However, we are using SingleBitPerPixel because when 
        'printed it appears sharper as opposed to anti-aliased
        g.TextRenderingHint = Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit
        g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
        g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
        g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality

        'If we are printing a box label then we need to add a side image 
        'and a "BOX" announcment on the right.
        If boxNumber.Enabled = False Then
            g.DrawImage(My.Resources.Shipping, 10, 2, 23, 23)
            g.DrawString("BOX", New Font("Tahoma", 10, _
                         FontStyle.Bold), New SolidBrush(Color.Silver), 129, 7)

            'Write out the original barcode ID, and preceed it with "BOX"
            g.DrawString("Box " & Code, New Font("Tahoma", _
                         8, FontStyle.Bold), New SolidBrush(Color.Black), 3, 34)
        Else
            'Write out the original barcode ID
            g.DrawString(Code, New Font("Tahoma", 8, FontStyle.Bold), _
                               New SolidBrush(Color.Black), 3, 34)
        End If

        'Write out a lighter barcode generation script version 
        'on the right (again, eye candy)
        g.DrawString("ITS v2.0", New Font("Tahoma", 8, _
                     FontStyle.Bold), New SolidBrush(Color.Gray), 113, 34)

        'Now that we are done with the high quality rendering, we are going 
        'to draw the barcode lines -- which needs to be very straight, and not blended
        'Else it may blur and cause complications with the barcode 
        'reading device -- so we won't take any chances.
        g.TextRenderingHint = Drawing.Text.TextRenderingHint.SystemDefault
        g.SmoothingMode = Drawing2D.SmoothingMode.None
        g.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
        g.CompositingQuality = Drawing2D.CompositingQuality.Default

        'Code has to be surrounded by asterisks to make it a valid C39 barcode, 
        'so add "*" to the front and read of the barcode
        Dim UseCode As String = String.Format("{0}{1}{0}", "*", Code)

        'Define a starting X and Y position for the barcode
        Dim XPosition As Short = 1
        Dim YPosition As Short = 1

        'Initialize our IC marker, and give a default of false
        'This is used to track what?  Incorrectly assigned 
        'characters for the barcode (ones that won't match
        'C39 standards) So we don't use them, and mark it as invalid.
        Dim invalidCharacter As Boolean = False

        'Declare our current character string holding variable
        Dim CurrentSymbol As String = String.Empty

        'THIS PART IS *ONLY* FOR CALCULATING THE WIDTH OF THE BARCODE TO CENTER IT!
        'Begin at the starting area of our FINAL rendered barcode value
        For j As Short = 0 To CShort(UseCode.Length - 1)
            'Set our current character to the character space of the barcode
            CurrentSymbol = UseCode.Substring(j, 1)

            'Check to ensure that it's a valid character 
            'per our encoding hashtable we defined earlier
            If Not IsNothing(_encoding(CurrentSymbol)) Then
                'Create a new rendered version of the character per our 
                'hashtable with valid values (don't read it, 
                'it will be encoded -- look above at the HT)
                Dim EncodedSymbol As String = _encoding(CurrentSymbol).ToString

                'Progress throughout the entire encoding value of this character
                For i As Short = 0 To CShort(EncodedSymbol.Length - 1)
                    'Extract the current encoded character value from 
                    'the complete rendering of this character (it's getting deep, right?)
                    Dim CurrentCode As String = EncodedSymbol.Substring(i, 1)

                    'Change our coordinates for drawing to match the next 
                    'position (current position plus the char. bar width)
                    XPosition = XPosition + getBCSymbolWidth(CurrentCode)
                Next

                'Now we need to "create" a whitespace as needed, and get the width
                XPosition = XPosition + getBCSymbolWidth("w")
            End If
        Next

        'Now the nice trick of division helps with centering the barcode on the canvas
        XPosition = (imageWidth / 2) - (XPosition / 2)

        'NOW WE GO LIVE!  THIS IS WHERE WE ACTUALLY DRAW THE BARCODE BARS
        'Begin at the starting area of our FINAL rendered barcode value
        For j As Short = 0 To CShort(UseCode.Length - 1)
            'Set our current character to the character space of the barcode
            CurrentSymbol = UseCode.Substring(j, 1)

            'Check to ensure that it's a valid character per our encoding 
            'hashtable we defined earlier                  
            If Not IsNothing(_encoding(CurrentSymbol)) Then
                'Create a new rendered version of the character per our 
                'hashtable with valid values (don't read it, 
                'it will be encoded -- look above at the HT)
                Dim EncodedSymbol As String = _encoding(CurrentSymbol).ToString

                'Progress throughout the entire encoding value of this character
                For i As Short = 0 To CShort(EncodedSymbol.Length - 1)
                    'Extract the current encoded character value from 
                    'the complete rendering of this character (it's getting deep, right?)
                    Dim CurrentCode As String = EncodedSymbol.Substring(i, 1)

                    'Use our drawing graphics object on the canvase to create 
                    'a bar with out position and values based
                    'on the current character encoding value
                    g.FillRectangle(getBCSymbolColor(CurrentCode), XPosition, _
                                    YPosition, getBCSymbolWidth(CurrentCode), _barHeight)
                    'Lets disect this a little to see how it actually works, want to?
                    '   getBCSymbolColor(CurrentCode)
                    '       We already know, but this gets the color of the bar,
                    '       either white or colorized (in this case, black)
                    '   XPosition, YPosition
                    '       Again, we already know -- but this is the coordinates
                    '       to draw the bar based on previous locations
                    '   getBCSymbolWidth(CurrentCode)
                    '       This is the important part, we get the correct width 
                    '       (either narrow or wide) for this character (post encoding)
                    '   _barHeight
                    '       This is static as defined earlier, it doesn't much 
                    '       matter but it also depends on your Barcode reader

                    'Change our coordinates for drawing to match the next position 
                    '(current position plus the char. bar width)
                    XPosition = XPosition + getBCSymbolWidth(CurrentCode)
                Next

                'Now we need to "ACTUALLY" create 
                'a whitespace as needed, and get the width
                g.FillRectangle(getBCSymbolColor("w"), XPosition, _
                                YPosition, getBCSymbolWidth("w"), _barHeight)

                'Change our coordinates for drawing to match the next position 
                '(current position plus the char. bar width)
                XPosition = XPosition + getBCSymbolWidth("w")
            Else
                'This is our fallback, if it's not a valid 
                'character per our hashtable in C39, discard!
                invalidCharacter = True
            End If
        Next

        'As we set it above (if needed) for an invalid character 
        '(not allowed by the C39 guide), then we handle it here  
        If invalidCharacter Then
            'Just fill the whole canvas white
            g.FillRectangle(Brushes.White, 0, 0, imageWidth, imageHeight)

            'What's the deal?  Tell them!  It's not right, so we can't do it -- here is why.
            g.DrawString("Invalid Charachers Detected", _
                         New Font("Tahoma", 8), New SolidBrush(Color.Red), 0, 0)
            g.DrawString("- Barcode Not Generated -", _
                         New Font("Tahoma", 8), New SolidBrush(Color.Black), 0, 10)
            g.DrawString(Code, New Font("Tahoma", 8, FontStyle.Italic), _
                         New SolidBrush(Color.Black), 0, 30)
        End If

        'Create a new memorystream to use with our function return
        Dim ms As New IO.MemoryStream

        'Setup the encoding quality of the final barcode rendered image
        Dim encodingParams As New EncoderParameters
        encodingParams.Param(0) = New EncoderParameter(Encoder.Quality, 100)

        'Define the encoding details of "how" for the image
        'We will use PNG because, well it's got the best image quality for it's footprint
        Dim encodingInfo As ImageCodecInfo = FindCodecInfo("PNG")

        'Save the drawing directly into the stream
        b.Save(ms, encodingInfo, encodingParams)

        'Clean-up!  Nobody likes a possible memory leaking application!
        g.Dispose()
        b.Dispose()

        'Finally, return the image via the memorystream
        Return ms
    End Function

This is where we set the encoding for the image before we send it to the returned memory stream:

    'Find the encoding method in the codec list 
    'on the computer based on the known-name (PNG, JPEG, etc)
    Protected Overridable Function FindCodecInfo(ByVal codec As String) As ImageCodecInfo
        Dim encoders As ImageCodecInfo() = ImageCodecInfo.GetImageEncoders
        For Each e As ImageCodecInfo In encoders
            If e.FormatDescription.Equals(codec) Then Return e
        Next
        Return Nothing
    End Function
#End Region

Now, we move onto actually printing the barcode, for both the physical printing and the on-screen printing...

#Region "Printing Methods (Physical Printing and On-Screen Printing)"
    'PHYSICAL PRINTING
    'This is a method that just basically sends the current barcode 
    'renderings to the printer under the printing options
    Public Sub doPrintActive()
        m_PrintDocument = New Printing.PrintDocument
        m_PrintDocument.PrinterSettings = PrintDialog1.PrinterSettings
        m_PrintDocument.DefaultPageSettings.Color = False
        m_PrintDocument.DefaultPageSettings.Margins = _
                New System.Drawing.Printing.Margins(8.5, 8.5, 35, 35)
        m_PrintDocument.Print()
    End Sub
    Private Sub m_PrintDocument_PrintPage(ByVal sender As Object, _
            ByVal e As System.Drawing.Printing.PrintPageEventArgs) _
            Handles m_PrintDocument.PrintPage
        'Dim x As Integer = e.MarginBounds.X + _
        '     (e.MarginBounds.Width - barcodeCanvas.Width) \ 2
        'Dim y As Integer = e.MarginBounds.Y + _
        '     (e.MarginBounds.Height - barcodeCanvas.Height) \ 2

        Dim x As Integer = e.MarginBounds.X
        Dim y As Integer = e.MarginBounds.Y
        e.Graphics.DrawImage(barcodeCanvas, x, y)
        e.HasMorePages = False
    End Sub


    'LOGICAL/ON-SCREEN PRINTING
    'This is an extended method that we call when we want 
    'to generate the barcodes and display them, or print them
    Public Sub doPrintNow()
        'Set our current page counter and determine the total pages to be printed
        curPage = 0

        'This depends on whether we are printing a box label or a document label because...
        '   BOX Labels
        '       We only need one label, so we can print the whole sheet with unique labels
        '       The Avery 8167 labels have 80 labels per sheet
        '   DOC Labels
        '       We need TWO labels per document (one for the document,
        '       and one for the chain-of-custody log)
        '       Since the Avery 8167 paper has 80 labels, we want
        '       to print one unique label per two items horizontal
        '           This means that we will print like this... 
        '                   |LABEL 1| |LABEL 1| |LABEL 21| |LABEL 21|
        '                   |LABEL 2| |LABEL 2| |LABEL 22| |LABEL 22|
        If boxNumber.Enabled = True Then
            totalPages = (CInt(bcEnd.Text) - (CInt(bcStart.Text) + 1)) / 40
        Else
            totalPages = (CInt(bcEnd.Text) - (CInt(bcStart.Text) + 1)) / 80
        End If

        'Set our toolstrip status text
        ToolStrip_StatusLabel.Text = "Generating barcodes..."

        'If it appears this printing session will not be effecient, 
        'alert the user before we just "continue"
        If NotEffecientLabel.Visible Then
            Dim surePrint As MsgBoxResult = _
              MsgBox("It appears this label printing session isn't effecient!" & _
              Chr(13) & Chr(13) & "Are you sure you would like to continue?", _
              MsgBoxStyle.Information + MsgBoxStyle.YesNo, Me.Text)

            If surePrint = MsgBoxResult.No Then
                Exit Sub
            End If
        End If

        'Declare the current barcode holding variable 
        'and the current barcode render object
        Dim barcode As String
        Dim thisBarCode As Bitmap

        'Define a new barcode canvas drawing object with a 8.5" x 11" 
        'A4 paper (around there -- we add padding later)
        'barcodeCanvas = New Bitmap(1275, 1650)
        barcodeCanvas = New Bitmap(766, 960)

        'Create our trusty graphics object for this canvas
        Dim dc As Graphics = Graphics.FromImage(barcodeCanvas)

        'Make it white! (the background of the canvas)
        dc.Clear(Color.White)

        'Declare our placeholders for coordinates used on drawing the barcode object
        Dim pgX As Integer = 0
        Dim pgXc As Integer = 0

        Dim pgY As Integer = 0

        'Declare our counter so we can apply various actions at different levels
        Dim newColCounter As Integer = 1
        Dim i As Integer

        'Remember, if "BoxNumber" is enabled, 
        'then we are printing a DOCUMENT label, if not -- it's a BOX
        If boxNumber.Enabled = True Then
            'IT IS A DOCUMENT LABEL
            'Loop through however many labels the user requested
            For i = CInt(bcStart.Text) To CInt(bcEnd.Text)
                'Set our status label again with the "status" and the completion progress
                ToolStrip_StatusLabel.Text = "Generating Barcodes " & i & _
                     " (" & Math.Round((i / CInt(bcEnd.Text)) * 100, 0) & "%)..."

                'Define a variable to hold our box number, and our page number
                Dim bid As String = "0"
                Dim pid As String = "0"

                'Determine the boxnumber and preceed it with zeros 
                'if needed, we want it to occupy 4 spaces so
                'We will always have a consistent width on the barcode 
                '(just, I don't know (desire for perfection maybe?) -- you will see)
                If Len(boxNumber.Text) < 4 Then
                    Select Case 4 - Len(boxNumber.Text)
                        Case 1
                            bid = "0" & boxNumber.Text
                        Case 2
                            bid = "00" & boxNumber.Text
                        Case 3
                            bid = "000" & boxNumber.Text
                    End Select
                Else
                    bid = boxNumber.Text
                End If

                'Do the same thing for the page number 
                '(well, techically not page number -- but document ID)
                If Len(CStr(i)) < 4 Then
                    Select Case 4 - Len(CStr(i))
                        Case 1
                            pid = "0" & CStr(i)
                        Case 2
                            pid = "00" & CStr(i)
                        Case 3
                            pid = "000" & CStr(i)
                    End Select
                Else
                    pid = CStr(i)
                End If

                'Set the barcode "code" with what we created
                barcode = bid & "-" & pid

                'Request that the barcode be rendered and exported
                'via memorystream into our new barcode holder
                thisBarCode = New Bitmap(GenerateBarcodeImage(169, 48, barcode))

                'Draw the barcode onto the barcode printing page canvas
                dc.DrawImage(thisBarCode, New PointF(pgX, pgY))

                'Increase our X coordinates, since we need 
                'to draw another EXACT label to the right
                pgX = pgX + (169 + 30)

                'Draw it again at the new position
                dc.DrawImage(thisBarCode, New PointF(pgX, pgY))

                'Reset the X position and drop down a single label instance
                pgY = pgY + 48
                pgX = pgXc

                'If we have filled a column of the label page, we need to...
                If newColCounter = 20 Then
                    'Move to the third column (because we occupy 2 columns with DOC labels)
                    pgXc = (165 * 2) + (32.5 * 2)

                    'Reset the Y position and save the X constant variable
                    pgY = 0
                    pgX = pgXc
                End If

                'Update our current view within the application with what we have so far
                PictureBox1.Image = barcodeCanvas

                'If we have filled the entire page and there *ARE* more 
                'labels that need to be drawn, we need to make a new page... but not before
                'Printing this current page (if the user has requested it), 
                'else we will lose what we just did!
                If newColCounter = 40 And CInt(bcEnd.Text) > i Then
                    'Update the current page counter
                    curPage = curPage + 1

                    'Update the progress bar
                    printingProgressBar.Visible = True
                    printingProgressBar.Maximum = totalPages
                    printingProgressBar.Value = curPage
                    Me.Refresh()
                    Application.DoEvents()

                    'Check to see if the user wants us to print the labels or not
                    If printLabels.Checked Then
                        'Yep, they sure do!  So do it!
                        ToolStrip_StatusLabel.Text = "Printing Label Page " & _
                          curPage & " (" & _
                          Math.Round((curPage / totalPages)) * 100 & "%)..."
                        Me.Refresh()
                        Application.DoEvents()

                        doPrintActive()
                    End If

                    'Start a new page and continue on...
                    dc.Clear(Color.White)

                    'Reset the coordinates for the new page
                    pgXc = 0
                    pgY = 0
                    pgX = pgXc

                    'Reset our column counter as well
                    newColCounter = 0
                End If

                'Increase the column counter
                newColCounter = newColCounter + 1
            Next
        Else
            'IT IS A BOX LABEL
            'Just refer to the above comments, if there 
            'is anything different I will point it out here
            For i = CInt(bcStart.Text) To CInt(bcEnd.Text)
                ToolStrip_StatusLabel.Text = "Generating Barcodes " & _
                  i & " (" & Math.Round((i / CInt(bcEnd.Text)) * 100, 0) & "%)..."

                'Since this is a BOX label, we don't need to determine the document ID
                Dim pid As String = "0"
                If Len(CStr(i)) < 4 Then
                    Select Case 4 - Len(CStr(i))
                        Case 1
                            pid = "0" & CStr(i)
                        Case 2
                            pid = "00" & CStr(i)
                        Case 3
                            pid = "000" & CStr(i)
                    End Select
                Else
                    pid = CStr(i)
                End If

                'A little different code format for this one
                barcode = pid
                thisBarCode = New Bitmap(GenerateBarcodeImage(169, 48, barcode))
                dc.DrawImage(thisBarCode, New PointF(pgX, pgY))

                'You can see we aren't drawing two of them now, 
                'we are just dropping straight down and moving on
                pgY = pgY + 48
                pgX = pgXc

                'Now we have different numbers based on wheter 
                'we are on one column, two, three, or the last
                If newColCounter = 20 Then
                    pgXc = 165 + 32.5

                    pgY = 0
                    pgX = pgXc
                End If
                If newColCounter = 40 Then
                    pgXc = (165 * 2) + (32.5 * 2)

                    pgY = 0
                    pgX = pgXc
                End If
                If newColCounter = 60 Then
                    pgXc = (165 * 3) + (32.5 * 3)

                    pgY = 0
                    pgX = pgXc
                End If

                'Update the current view of the barcodes page
                PictureBox1.Image = barcodeCanvas

                'The page is full -- and we have more, 
                'if we get to this conditional and it's true
                If newColCounter = 80 And CInt(bcEnd.Text) > i Then
                    curPage = curPage + 1
                    printingProgressBar.Visible = True
                    printingProgressBar.Maximum = totalPages
                    printingProgressBar.Value = curPage
                    Me.Refresh()
                    Application.DoEvents()

                    'If we need to print, do it!
                    If printLabels.Checked Then
                        ToolStrip_StatusLabel.Text = "Printing Label Page " & _
                          curPage & " (" & _
                          Math.Round((curPage / totalPages)) * 100 & "%)..."
                        Me.Refresh()
                        Application.DoEvents()

                        doPrintActive()
                    End If

                    'Clear it and start over.
                    dc.Clear(Color.White)
                    pgXc = 0

                    pgY = 0
                    pgX = pgXc

                    newColCounter = 0
                End If

                'Increase column counter
                newColCounter = newColCounter + 1
            Next
        End If

        'Lastly, since we are on the last page -- if we need to print -- do it!
        If printLabels.Checked Then
            ToolStrip_StatusLabel.Text = "Printing Label Page " & curPage & _
              " (" & Math.Round((curPage / totalPages)) * 100 & "%)..."
            Me.Refresh()

            doPrintActive()
        End If

        'We're finished, so update the interface!
        ToolStrip_StatusLabel.Text = "Finished (idle)"
        printingProgressBar.Visible = False

        'Start the timer to return to the box scanning area
        returnToBoxScanTimer.Start()
        returnToBoxScanPanel.Visible = True
    End Sub
#End Region

We calculate the page requirements (physical pages of labels) to print the barcodes, with the code below. We then write it out to the user for review, and warn them if need be.

#Region "Label Pages Requirement Calculation System"
    'This is what we call when we want to know how
    'many pages of labels it will take to print our barcodes
    Public Sub calculatePages()
        'We can't calculate if the starting and ending point is nothing usable...
        If bcStart.Text.Length < 1 Or bcStart.Text.Length < 1 Or _
              bcEnd.Text.IsNullOrEmpty(bcEnd.Text) = True Or _
              bcStart.Text.IsNullOrEmpty(bcStart.Text) = True Then
            Exit Sub
        End If

        'Determine the page allocation limit based on BOX of DOC assignment
        Dim pageAllocation As Integer = 40
        If boxNumber.Enabled = False Then
            pageAllocation = 80
        End If

        'Set the total pages initial amount to how many based on the allocation limit
        Dim tpages As Double = Math.Round((CDbl(bcEnd.Text) - _
                 CDbl(bcStart.Text) + 1) / pageAllocation, 2)
        pageUsageLabel.Text = tpages & " label pages"

        'If we are over normal pages then we need to make this a non-effecient print
        '   Yes, there are probably many other and possibly 
        'better ways to check for a remainder, but this is how I wanted to do it
        If CStr(tpages).Contains(".") Then
            'We're over our limit, but how much?
            Dim ovr As Double = CDbl(CStr(tpages).Split(".")(1))

            'Just check to make sure localization didn't 
            'add the decimal even for a non-remaining number
            If ovr > 0 Then
                'Set our non-effecient warning
                NotEffecientLabel.Visible = True
                YesEffecientLabel.Visible = False

                'Give the user more information on fixining the issue, 
                'so determine a low and high page that is effecient
                Dim useL As Integer = 0
                For useL = CDbl(bcEnd.Text) To CDbl(bcEnd.Text) + pageAllocation
                    If CStr(Math.Round((CDbl(useL) - CDbl(bcStart.Text) + 1) / _
                            pageAllocation, 2)).Split(".").Length = 1 Then
                        Exit For
                    End If
                Next

                Dim useH As Integer = 0
                For useH = CDbl(bcEnd.Text) - (pageAllocation - 1) To _
                           CDbl(bcEnd.Text) + pageAllocation
                    If CStr(Math.Round((CDbl(useH) - CDbl(bcStart.Text) + 1) / _
                              pageAllocation, 2)).Split(".").Length = 1 Then
                        Exit For
                    End If
                Next

                'Write out what they can use you have an even number of pages
                NotEffecientLabel.Text = "Not Effecient (Use " & _
                     useH & " or " & useL & " instead)"
            Else
                'We are effecient!  Great job "user"!
                NotEffecientLabel.Visible = False
                YesEffecientLabel.Visible = True
            End If
        Else
            'Yes! Yes! Yes! -- you get it.
            NotEffecientLabel.Visible = False
            YesEffecientLabel.Visible = True
        End If
    End Sub

    'Calculate the page requirements when they change counts
    Private Sub bcStart_TextChanged(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles bcStart.TextChanged
        calculatePages()
    End Sub
    Private Sub bcEnd_TextChanged(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles bcEnd.TextChanged
        calculatePages()
    End Sub
#End Region

While the main form loads, we will load the hashtable with the encoding values, among other things:

    'Do the pre-load events for when this form is up
    Private Sub mainBarcodeWorkspace_Load(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles MyBase.Load
        'Generate the hashtable of encoding values
        ITS_BarcodeC39()

        'Do initial calculations of pages required to print
        calculatePages()

        'Focus on the box number, it's primarly the most important in most cases
        boxNumber.Focus()

        'Set our global position in memory in our module
        mainFrm = Me

        'Show the box scanning form
        boxScanFrm.ShowDialog(Me)
    End Sub
End Class

Points of Interest

This was a very fun and interesting project to tackle. I've learned a lot more about GDI+ and have an overall knowledge of the .NET framework from this project. As with every project, there are problems that arise, but all can be solved with a clear head and time to think!

History

  • Initial release - 03/19/2009, 12:40 AM CST.
  • Version 2.0 release - 03/20/2009, 10:49 PM CST.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here