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

Ordermate

0.00/5 (No votes)
20 Nov 2013 1  
Ordermate can be used as a basic example of utilizing the XMLSerializer class to automate the creation of invoice forms.

685222/screenshot.jpg

Introduction

Ordermate was created to simplify the process of building an invoice of materials based on Movie 'product' requests. The actors of this scenario are: Distribution Company Employee (i.e. User), Theater Company Contact (i.e. Customer). Ordermate covers the following workflow for movie order processing.

  1. Movie Theater 'Customer' requests to place an order for one or more movies.
  2. Distributor company Employee selects Customer from ComboBox dropdown menu.
  3. Requested products are selected from a CheckedListBox.
  4. Employee enters the Quantity of items ordered.
  5. Employee clicks the [Add] button to update form.
  6. Employee has option to View the invoice on screen and/or Export the invoice to Microsoft Excel.

Purpose

Ordermate is a demonstration of using xml as a flat file type data store. XML can be converted, formatted, and imported to any type database you choose (i.e. MS Access, SQL Server, Oracle, MYSQL, etc.) The purpose of this article is to give you a sense of "what it is like" to build a .NET application using XML Serialization. It demonstrates a variety of programing topics for the entry to mid-level VB.NET programmer. Topics covered:

  • XmlSerializer: Serializes and deserializes objects into and from XML documents.
  • PrintDocument: Create custom invoice forms for Print and Print preview.
  • DocumentFormat.OpenXml: Open XML Format to create/output Excel spreadsheet invoices.
  • SpreadsheetLight: SpreadsheetLight is an open source Open XML spreadsheet library for .NET Framework.
  • DataBindings: Data binding is the process that establishes a connection between the application UI and business objects.
  • StringRandomGenerator: Helps to generate random invoice numbers.
Ordermate generally involves the following: Process movie inventory, create invoices, create, edit, and update product supplies. It can be used to Read/Write XML files. XML input/output and to export to a Microsoft Excel worksheet. Invoices maybe viewed for Print and Print preview. Read Write XML.

Background

History

Ordermate is actually and update to a program that I created about 10 yrs ago. It was recently revisited due to a client's need to reformat some Nessus Scanner By Tenable report files. The files themselves are just plain vanilla xml datasets that needed to be extracted, formatted, and then dropped into a Microsoft Excel spreadsheet.

Steps

The following steps were taken to generate class files based on existing xml file(s):

  1. Create xml file(s) using standard text editor.
  2. Use xsd.exe to generate schema file(s) (*.xsd).
    • - start Visual Studio Tools 'Developer Command Prompt'
    • - Format / correct type schema properties
  3. Use xsd.exe to generate class file(s) from Step 2
    • - xsd products.xsd /classes /language:VB
    • - xsd customers.xsd /classes /language:VB
  4. Clean up the generated class file(s) (*.vb)
  5. Create Visual Studio Solution, include (*.vb) file(s)

XmlSerializer

DataCollection Class

The DataCollection class represents the primary -top level element of the Xml file. Only one top level element is allowed in an XML document. It holds a reference to all Customer and Product objects found within a deserialized xml file. Once all Products and Customers are loaded it triggers a PropertyChangedEvent which a listener (i.e. Form control) can respond to.

Public Class DataCollection
    Implements System.ComponentModel.INotifyPropertyChanged

    Private _Products As List(Of Product)

    Private _Customers As List(Of Customer)

    ' These are the variables which will store data
     Public Property Products() As List(Of Product)
        Get
            Return _Products
        End Get
        Set(ByVal value As List(Of Product))
            _Products = value
            RaisePropertyChanged("Products")
        End Set
    End Property

    Public Property Customers() As List(Of Customer)
        Get
            Return _Customers
        End Get
        Set(ByVal value As List(Of Customer))
            _Customers = value
            RaisePropertyChanged("Customers")
        End Set
    End Property

    Public Event PropertyChanged As _
    System.ComponentModel.PropertyChangedEventHandler _
    Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Protected Sub RaisePropertyChanged(ByVal propertyName As String)
        Dim propertyChanged As _
        System.ComponentModel.PropertyChangedEventHandler _
        = Me.PropertyChangedEvent
        
        If (propertyChanged IsNot Nothing) Then
            propertyChanged(Me, _
            New System.ComponentModel.PropertyChangedEventArgs(propertyName))
        End If
    End Sub
End Class
    

Deserialization

In order to deserialize data from an xml file into class objects. We need to utilize the XmlSerializer class. This procedure is taken care of in the Initialize method of the FrmMain class. As Deserialization occurs connect an EventHandler to receive and handle events.

Private Sub Initialize()
    ' Initialize the Primary DataCollection
    _Data = New DataCollection
    
    ' FrmMain acts as a listener of events for the DataCollection class.
    ' Connect a EventHandler to receive and handle events raised by 
    ' DataCollection class
    AddHandler _Data.PropertyChanged, AddressOf OnDataChanged

    ' Used to process two separate xml files 
    Dim XmlDataCollection As DataCollection = Nothing

    ' Format used to load data from XML
    Dim format As New XmlSerializer(GetType(DataCollection))

    ' Use the CustomerStream to Deserialize all Customer objects
    Using CustomerStream As Stream = _
    File.OpenRead(DataDirectory & CustomersXml)
        XmlDataCollection = DirectCast(format.Deserialize(CustomerStream), _
        DataCollection)

        If Not XmlDataCollection Is Nothing Then
            _Data.Customers = XmlDataCollection.Customers
        End If
    End Using
    ...
End Sub

Serialization

The opposite of Deserialization is Serialization. Serialization involves saving data back to an xml file. You can do this by again using the XmlSerializer class. Each time an edit is made to a Customer's data it can be written back to the hard disk. For the FrmCustomer class this is done in the SaveCustomers method.

Customer Screen

Private Sub SaveCustomers()
    ' Format used to load data from XML
    Dim serializer As New XmlSerializer(GetType(DataCollection))
    Dim Data As New DataCollection
    Data.Customers = _Customers

    ' Create an XmlTextWriter using a FileStream. 
    Using CustomerStream As Stream = _
    New FileStream(DataDirectory & "Customers.xml", FileMode.Create)
        Dim writer As New XmlTextWriter(CustomerStream, Encoding.Unicode)

        serializer.Serialize(writer, Data)
    End Using
End Sub

PrintDocument

The PrintDocument class defines a reusable object that sends output to a printer, when printing from a Windows Forms application. The following method creates the invoice on screen for print out.

Screen Invoice

Private Sub CreateInvoiceDocument(ByVal g As Graphics)

    Dim srcRect As RectangleF = New Rectangle(0, 0, InvoiceSize.Width, _
    InvoiceSize.Height)
    
    Dim nWidth As Integer = _
        docInvoice.PrinterSettings.DefaultPageSettings.PaperSize.Width
    
    Dim nHeight As Integer = _
        docInvoice.PrinterSettings.DefaultPageSettings.PaperSize.Height
        
    Dim destRect As RectangleF = New Rectangle(0, 0, nWidth, nHeight)
    Dim scalex As Single = CSng(destRect.Width / InvoiceSize.Width)
    Dim scaley As Single = CSng(destRect.Height / InvoiceSize.Height)

    Dim aPen As New Pen(Brushes.Black, 1)

    ' Draw the Invoice Image
    If picGroupLogo.BackgroundImage IsNot Nothing Then
        Dim gu As GraphicsUnit = GraphicsUnit.Pixel
        Dim scaledRectangle As RectangleF = GetScaledRectangle(scalex, _
        scaley, picGroupLogo.Bounds)
        
        Dim myImage As Image = CType(picGroupLogo.BackgroundImage.Clone(), _
        Image)
        
        g.DrawImage(myImage, scaledRectangle, _
        picGroupLogo.BackgroundImage.GetBounds(gu), GraphicsUnit.Pixel)
        
    End If

    ' Draw out the Invoice Header
    WriteInvoiceHeader(g, scalex, scaley)

    ' Draw out the Invoice Line Items
    WriteInvoiceLineItems(g, scalex, scaley)

End Sub

SpreadsheetLight

Probably the most exciting portion of this program for me was finding the OpenSource SpreadsheetLight [^] library. Although the source is written in C# I am still a fan of this nifty little utility. Maybe someday later I will translate it over to a more sensible language like Visual Basic (i.e. VB.NET). ;-) All jokes aside SpreadsheetLight is a great utility. Below is an example of how toExport invoice data into a Microsoft Excel spreadsheet. A preformatted template spreadsheet is used and invoice data is dropped onto it. Creating a Helper class makes is simple to format and manipulate the eventual output.

Excel Invoice

Public Class Helper
      ...
      
    Private Sub Create(ByVal inInvoice As Invoice, info As FileInfo)
         ' SpreadsheetLight works on the idea of a currently selected worksheet.
        ' If no worksheet name is provided on opening an existing spreadsheet,
        ' the first available worksheet is selected.
        Dim sl As New SLDocument(TemplateDirectory & "Invoice.xlsx", "Invoice")
        
        ...
        
          ' Iterate through each Lineitem and set spreadsheet values
        For Each item As LineItem In SortedHost
        ...
        Next item
        
            sl.SaveAs(OutputFile)
    End Sub
   
    ...
End Class
           

Databinding

Databinding makes it easier and much simpler to bind class properties to System.Windows.Forms.Control properties. In the Databind method of the FrmProduct form we bind all the neccessary controls to a specific Product object.

Product Screen

Private Sub Databind(ByVal item As Product)
    ' Prior databindings must be clear before new databindings are added
    ClearDatabindings()

    ' Create Binding objects for all controls (i.e. ComboBox, TextBox,  etc..).  
    ' The data-bound property for controls is typically the Text property.
    '  The data source is a Product (i.e. 'item'). 
    ' The data member is specified property on the Product object.


    cboCategory.DataBindings.Add("Text", item, "Category")
    nbrPrice.DataBindings.Add("Value", item, "UnitPrice")
    txtYear.DataBindings.Add("Text", item, "Year")
    txtTitle.DataBindings.Add("Text", item, "Name")
    nbrRating.DataBindings.Add("Text", item, "Rating")
    txtNumber.DataBindings.Add("Text", item, "Number")
    txtDescription.DataBindings.Add("Text", item, "Description")
    txtActor.DataBindings.Add("Text", item, "Actor")
    nbrQuantity.DataBindings.Add("Value", item, "Quantity")
End Sub

StringRandomGenerator

The StringRandomGenerator is actually a custom designed class derived from an article found here on CodeProject. Appended in the GetRandom method is code to put a dash after every fourth Char.

Public Function GetRandom() As String
    ...
          
    If i > 0 AndAlso (i Mod 4) = 0 Then sb.Append("-")
    ...
End Function

Summary

Ordermate can be used as a basic example of utilizing the XMLSerializer class to automate the creation of invoice forms. The purpose of this article was to give you a sense of the experience of building an WindowsForm application based upon XMLSerialization techniques. I hope that you discovered that building an XML flat file application is not difficult.

In this article, we examined only the most basic features of the .NET framework and using component libraries.

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