Ordermate






4.40/5 (4投票s)
Ordermate 可用作利用 XMLSerializer 类自动创建发票表的简单示例。
引言
Ordermate 的创建是为了简化基于电影“产品”请求构建材料发票的过程。此场景的参与者是:发行公司员工(即用户)、影院公司联系人(即客户)。Ordermate 涵盖了电影订单处理的以下工作流程。
- 电影院“客户”请求订购一部或多部电影。
- 发行公司员工从 ComboBox 下拉菜单中选择客户。
- 从 CheckedListBox 中选择请求的产品。
- 员工输入订购的数量。
- 员工点击 [添加] 按钮更新表单。
- 员工可以选择在屏幕上**查看**发票和/或将发票**导出**到 Microsoft Excel。
目的
Ordermate 是使用 xml 作为平面文件类型数据存储的演示。XML 可以转换为、格式化并导入到您选择的任何类型的数据库(例如 MS Access、SQL Server、Oracle、MYSQL 等)。本文的目的是让您了解使用 XML Serialization 构建 .NET 应用程序的“感觉”。它演示了面向中级 VB.NET 程序员的各种编程主题。涵盖的主题
- XmlSerializer:将对象序列化为 XML 文档或从 XML 文档反序列化。
- PrintDocument:创建用于打印和打印预览的自定义发票表。
- DocumentFormat.OpenXml:Open XML 格式,用于创建/输出 Excel 电子表格发票。
- SpreadsheetLight:SpreadsheetLight 是一个用于 .NET Framework 的开源 Open XML 电子表格库。
- DataBindings:数据绑定是将应用程序 UI 与业务对象连接起来的过程。
- StringRandomGenerator:帮助生成随机发票号。
背景
历史
Ordermate 实际上是我大约 10 年前创建的一个程序的更新。最近,由于一个客户需要重新格式化一些 Nessus Scanner(由 Tenable 提供)的报告文件,我重新审视了它。文件本身只是普通的 vanilla xml 数据集,需要进行提取、格式化,然后放入 Microsoft Excel 电子表格。
步骤
为基于现有 xml 文件生成类文件所采取的步骤如下:
- 使用标准文本编辑器创建 xml 文件。
- 使用 xsd.exe 生成模式文件(*.xsd)。
- - 启动 Visual Studio Tools 的“开发人员命令提示符”
- - 格式化/更正类型模式属性
- 使用 xsd.exe 从第 2 步生成类文件
- - xsd products.xsd /classes /language:VB
- - xsd customers.xsd /classes /language:VB
- 清理生成的类文件(*.vb)
- 创建 Visual Studio 解决方案,包含(*.vb)文件
XmlSerializer
DataCollection 类
DataCollection
类代表 Xml 文件的主**顶层元素。XML 文档中只允许一个顶层元素。它保存了反序列化后的 XML 文件中所有 Customer 和 Product 对象的引用。一旦所有产品和客户都加载完毕,它就会触发一个 PropertyChangedEvent
,监听器(即窗体控件)可以响应此事件。
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
反序列化
为了将数据从 xml 文件反序列化到类对象中。我们需要利用 XmlSerializer
类。这个过程在 FrmMain
类的 Initialize
方法中处理。在反序列化发生时,连接一个 EventHandler 来接收和处理事件。
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
序列化
Deserialization
的反义词是 Serialization
。Serialization 涉及将数据保存回 xml 文件。您可以通过再次使用 XmlSerializer
类来完成此操作。每次对客户数据进行编辑时,都可以将其写回硬盘。对于 FrmCustomer
类,这是在 SaveCustomers
方法中完成的。
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
PrintDocument
类定义了一个可重用对象,当在 Windows Forms 应用程序中进行打印时,它会将输出发送到打印机。以下方法创建要在屏幕上打印的发票。
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
对我来说,这个程序中最令人兴奋的部分可能是找到了 OpenSource SpreadsheetLight [^] 库。虽然它是用 C# 编写的,但我仍然是这个精巧小工具的粉丝。也许将来有一天我会将其翻译成更合理的语言,如 Visual Basic(即 VB.NET)。 ;-) 说笑归说笑,SpreadsheetLight 是一个很棒的实用工具。下面是如何将发票数据**导出**到 Microsoft Excel 电子表格的示例。使用预先格式化的模板电子表格,并将发票数据放入其中。创建一个辅助类可以使格式化和操作最终输出变得简单。
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
数据绑定
数据绑定使类属性与 System.Windows.Forms.Control
属性的绑定更加容易和简单。在 FrmProduct 窗体的 Databind 方法中,我们将所有必要的控件绑定到一个特定的 Product 对象。
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
StringRandomGenerator
实际上是一个自定义设计的类,源自 CodeProject 上这里的一篇文章。在 GetRandom
方法中附加的代码是在每四个 Char
之后添加一个连字符。
Public Function GetRandom() As String
...
If i > 0 AndAlso (i Mod 4) = 0 Then sb.Append("-")
...
End Function
摘要
Ordermate 可用作利用 XMLSerializer 类自动创建发票表的简单示例。本文的目的是让您了解使用 XMLSerialization 技术构建 WindowsForm 应用程序的经验。我希望您发现构建 XML 平面文件应用程序并不难。
在本文中,我们仅考察了 .NET 框架和组件库使用中最基本的功能。