The other day, I decided to cruise ZDNET to look for a simple shareware program to make invoices. I have to say, I couldn't find what I was looking for, so I turned to C# and .NET. This program can stand improvements but it will get you started in creating an invoice and printing it to the printer. You can customize the invoice by changing the bitmap supplied in the download to an invoice of your choice and then move the controls to fit into the proper locations on the Background bitmap. This invoice layout was scanned in from Intuit's, Quicken 99 and modified to add a few features.
Figure 1 - The Invoice in a Windows Form
Below is the design for the InvoiceMaker.NET program in UML:
Figure 2 - UML Diagram of InvoiceMaker.NET reverse engineered using WithClass 2000
The program has several pieces of code probably worth discussing. In this article we'll concentrate on printing and serialization. If you are curious about how the printing was designed, you'll want to check out my article on the W-2 Form in which I borrowed most of the printing code. The only section that differs radically is how I printed out the ListView. Below is the code for printing a ListView called from the printDocument1_PrintPage event handler:
private void DrawAll(Graphics g)
{......
// handle List View Control printing
if (Controls[i].GetType() == this.listView1.GetType())
{
for (int row = 0; row < listView1.Items.Count; row++)
{
int nextColumnPosition = listView1.Bounds.X;
for (int col = 0; col < listView1.Items[row].SubItems.Count; col++)
{
g.DrawString(listView1.Items[row].SubItems[col].Text, listView1.Items[row].Font,Brushes.Black,
(nextColumnPosition + 3)*scalex,(listView1.Items[row].Bounds.Y + listView1.Bounds.Y)* scaley,
new StringFormat());
nextColumnPosition += listView1.Columns[col].Width;
}
}
}
...
}
This code simply cycles through the ListView Items and through each ListView Item's Subitem and prints them to the Graphics surface using DrawString. The horizontal position of the string is determined using the widths in the Columns collection of the ListView. (Note that the position is scaled to the printing surface as in the W-2 Forms article).
Serialization
In order to serialize the form, I created a separate object that can serialize itself called InvoiceData and then filled and unfilled the form using this object. (I tried to make the Form serializable, but .NET didn't like that). The InvoiceData class also contains a collection of objects called InvoiceDataRows, which are also serializable. To make an object serializable in .NET, simply stick the attribute Serializable above the class as shown below:
[Serializable]
public class InvoiceData
{
public InvoiceDataRow[] DataRows = new InvoiceDataRow[23];
public string LogoFile = "";
public string BillerAddress = "";
public string BillToAddress = "";
public string ShipToAddress = "";
public string InvoiceDate = "";
public string InvoiceNumber = "";
public string DueDate = "";
public string PONumber = "";
public string PercentTax = "";
public string Subtotal = "";
public string Total = "";
public int RowCount = 0;
....
}
If we want to save the data in the form, the following method is used:
void SaveForm()
{
// find a suitable place to put the invoice output
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
// Fill the InvoiceData Object with the Form Input
FillInvoiceDataWithForm();
// Create a BinaryFormatter object for streaming data out to a file
IFormatter formatter = new BinaryFormatter();
// Create a file stream for writing
Stream stream = new FileStream(saveFileDialog1.FileName, FileMode.Create,
FileAccess.Write, FileShare.None);
// Serialize the members of the InvoiceData class into the file
formatter.Serialize(stream, TheInvoiceData);
stream.Close();
}
}
The BinaryFormatter object spits out the data into a file containing all the members of the InvoiceData class. Because InvoiceDataRow is also serializable and because its a member of the InvoiceData class as an array, the entire InvoiceDataRow[] array is also spit out to the file. (This is a serious time saver, still it would be nice if Forms were serializable).
To Deserialize the data, we use the method below: