Visual Basic.NET快速开发MIS系统
【摘 要】 本文介绍微软最新技术Visual Basic.NET在数据库开发方面的应用。结合数据库系统开发的知识,介绍了物理表操作的方法,利用Visual Basic.NET的面向对象的特征,利用类的继承知识,简化了数据库系统开发过程。
引言
以前版本的Visual Basic虽然号称自己是一种OOP(面向对象)编程语言,但却不是一个地地道道的OOP编程语言,最多只是半个面向对象的编程语言。但Visual Basic.NET已经是一种完全的面向对象的编程语言。他支持面向对象的所有基本特征:继承、多态和重载。这使得以前在Visual Basic中很难或根本实现不了的问题,在Visual Basic.NET中可以顺利的用简单的方法实现。
自定义数据操作类
定义一个数据访问的基类,并编写有关数据库操作的必要方法。
定义一个数据访问类,类名为CData。定义连接Oracle数据库的方法ConnOracle,获取数据集的方法GetDataSet, 获取物理表的方法GetDataTable, 向物理表中插入一行数据的方法Insert, 向物理表中删除数据的方法Delete, 向物理表中更新数据的方法Update。其实现方法不是本文的重点,在此仅给出代码,不作详细分析。代码如下:
Public Class CDataBase
Dim OleCnnDB As New OleDbConnection()
'连接Oracle数据库,ServerName:服务器名,UserId:用户名,UserPwd:用户密码
Public Function ConnOracle(ByVal ServerName As String, ByVal UserId As String, ByVal UserPwd As String) As OleDbConnection
Dim OleCnnDB As New OleDbConnection()
With OleCnnDB
.ConnectionString = "Provider=MSDAORA.1;Password='" & UserPwd & "';User ID='" & UserId & "';Data Source='" & ServerName & "'"
Try
.Open()
Catch er As Exception
MsgBox(er.ToString)
End Try
End With
mOleCnnDB = OleCnnDB
Return OleCnnDB
End Function
'获取数据集。TableName:表名,strWhere:条件
Public Overloads Function GetDataSet(ByVal TableName As String, ByVal strWhere As String) As DataSet
Dim strSql As String
Dim myDataSet As New DataSet()
Dim myOleDataAdapter As New OleDbDataAdapter()
myOleDataAdapter.TableMappings.Add(TableName, TableName)
strSql = "SELECT * FROM " & TableName & " where " & strWhere
myOleDataAdapter.SelectCommand = New OleDbCommand(strSql, mOleCnnDB)
Try
myOleDataAdapter.Fill(myDataSet)
Catch er As Exception
MsgBox(er.ToString)
End Try
Return myDataSet
End Function
'获取物理表。TableName:表名
Public Overloads Function GetDataTable(ByVal TableName As String) As DataTable
Dim myDataSet As New DataSet()
myDataSet = GetDataSet(TableName)
Return myDataSet.Tables(0)
End Function
'获取物理表。TableName:表名,strWhere:条件
Public Overloads Function GetDataTable(ByVal TableName As String, ByVal strWhere As String) As DataTable
Dim myDataSet As New DataSet()
myDataSet = GetDataSet(TableName, strWhere)
Return myDataSet.Tables(0)
End Function
'向物理表中插入一行数据。TableName:表名,Value:行数据,BeginColumnIndex:开始列
Public Overloads Function Insert(ByVal TableName As String, ByVal Value As Object, Optional ByVal BeginColumnIndex As Int16 = 0) As Boolean
Dim myDataAdapter As New OleDbDataAdapter()
Dim strSql As String
Dim myDataSet As New DataSet()
Dim dRow As DataRow
Dim i, len As Int16
strSql = "SELECT * FROM " & TableName
myDataAdapter.SelectCommand = New OleDbCommand(strSql, mOleCnnDB)
Dim custCB As OleDbCommandBuilder = New OleDbCommandBuilder(myDataAdapter)
myDataSet.Tables.Add(TableName)
myDataAdapter.Fill(myDataSet, TableName)
dRow = myDataSet.Tables(TableName).NewRow
len = Value.Length
For i = BeginColumnIndex To len - 1
If Not (IsDBNull(Value(i)) Or IsNothing(Value(i))) Then
dRow.Item(i) = Value(i)
End If
Next
myDataSet.Tables(TableName).Rows.Add(dRow)
Try
myDataAdapter.Update(myDataSet, TableName)
Catch er As Exception
MsgBox(er.ToString)
Return False
End Try
myDataSet.Tables.Remove(TableName)
Return True
End Function
'更新物理表的一个字段的值。strSql:查询语句,FieldName_Value:字段及与对应的值
Public Overloads Sub Update(ByVal strSql As String, ByVal FieldName_Value As String)
Dim myDataAdapter As New OleDbDataAdapter()
Dim myDataSet As New DataSet()
Dim dRow As DataRow
Dim TableName, FieldName As String
Dim Value As Object
Dim a() As String
a = strSql.Split(" ")
TableName = a(3)
a = FieldName_Value.Split("=")
FieldName = a(0).Trim
Value = a(1)
myDataAdapter.SelectCommand = New OleDbCommand(strSql, mOleCnnDB)
Dim custCB As OleDbCommandBuilder = New OleDbCommandBuilder(myDataAdapter)
myDataSet.Tables.Add(TableName)
myDataAdapter.Fill(myDataSet, TableName)
dRow = myDataSet.Tables(TableName).Rows(0)
If Value <> Nothing Then
dRow.Item(FieldName) = Value
End If
Try
myDataAdapter.Update(myDataSet, TableName)
myDataSet.Tables.Remove(TableName)
Catch er As Exception
MsgBox(er.ToString)
End Try
End Sub
'删除物理表的数据。TableName:表名,strWhere:条件
Public Overloads Sub Delete(ByVal TableName As String, ByVal strWhere As String)
Dim myReader As OleDbDataReader
Dim myCommand As New OleDbCommand()
Dim strSql As String
strSql = "delete FROM " & TableName & " where " & strWhere
myCommand.Connection = mOleCnnDB
myCommand.CommandText = strSql
Try
myReader = myCommand.ExecuteReader()
myReader.Close()
Catch er As Exception
MsgBox(er.ToString)
End Try
End Sub
End Class
[责任编辑:editor]
定义一操作数据库中物理表的类CData,此类继承CDataBase,即:
Public Class CData:Inherits CDataBase
此类应该由供用户提供所操作的物理表的表名,指定了表名就可取得该表的所有性质。该表主要完成插入、删除、更新功能。定义其属性、方法如下:
申明类CData的变量:
'所要操作的表名
Private Shared UpdateTableName As String
'所要操作的表对象
Public Shared UpdateDataTable As New DataTable()
'对应表的一行数据197
Public Shared ObjFields() As Object
'表的字段数
Public Shared FieldCount As Int16
'主关键字。我们假设每个物理表都有一个主关键字字段fSystemID
Public Shared SystemID As String
说明:Shared 关键字指示一个或多个被声明的编程元素将被共享。共享元素不关联于某类或结构的特定实例。可以通过使用类名或结构名称或者类或结构的特定实例的变量名称限定共享元素来访问它们。
申明类CData的属性UpdateTable,当向UpdateTable赋给了一个已知表的表名,就可确定表的字段数,定义出数据行。这里,先打开表,再重新定义数据行.
Public Property UpdateTable() As String
Get
UpdateTable = UpdateTableName
End Get
Set(ByVal Value As String)
UpdateTableName = Value.Trim
UpdateDataTable = DB.GetDataTable(UpdateTableName)
UpdateTableFieldNames = UpdateDataTable.Clone
FieldCount = UpdateDataTable.Columns.Count
ReDim ObjFields(FieldCount - 1)
End Set
End Property
'删除由主关键值fSystemID指定的数据行
Public Sub Delete()
Dim strSQL As String
strSQL = "Delete from " & UpdateTableName & " where fSystemID=" & SystemID
DB.Delete(strSQL)
UpdateDataTable.Rows.Remove(GetRow)
End Sub
'向表UpdateTableName中插入一行数据。数据由ObjFields给出
Public Function Insert() As Boolean
DB.Insert(UpdateTableName, ObjFields)
End Function
'更新表UpdateTableName所指定的行
Public Shadows Sub Update()
Dim SetField As String
Dim i As Int16
For i = 1 To FieldCount - 1
SetField = UpdateTableFieldNames.Columns(i).ColumnName & "=" & ObjFields(i)
UpdateField(SetField)
Next
End Sub
Public Sub UpdateField(ByVal SetField As String)
Dim StrSQL As String
StrSQL = "select * from " & UpdateTableName & " where fSystemID= " & SystemID
DB.Update(StrSQL, SetField)
End Sub
'填充网络数据
Public Overloads Sub FillGrid(ByVal GridName As DataGrid)
GridName.DataSource = UpdateDataTable
End Sub
'把数据网格的当前行数据定写入到输入控件中
Public Sub DataGridToText(ByVal frm As Form)
Dim RowIndex, i As Int16
Dim value
Dim obj As Control
Dim DataGrid As New DataGrid()
If FieldCount = 0 Then Exit Sub
For Each obj In frm.Controls
If obj.GetType.Name = "DataGrid" Then
DataGrid = obj
Exit For
End If
Next
RowIndex = DataGrid.CurrentRowIndex
For i = 1 To FieldCount - 1
value = DataGrid.Item(RowIndex, i)
If IsDBNull(value) = True Then
value = ""
End If
For Each obj In frm.Controls '
If obj.TabIndex = i Then
obj.Text = value
Exit For
End If
Next
Next
End Sub
[责任编辑:editor]
窗体继承及实现
所处理的物理表
有了上面的类,我们就可应用它来实现MIS系统中关于物理表的操作功能。
例如我们有一个tInfo表、tUser表、tChange表、tSub表,表中都定义了主关键字字段FSYSTEMID, 表结构如下:


tChange表

tSub表
生成窗体模板
我们要向tInfo表、tUser二个表里插入数据,删除数据,更新数据,它们应该分别在二个Form里完成。这里,我们可先定义一Form模板,命名为FrmModule,在FrmModule上增加工具箱ToolBar1,在ToolBar1增加"增加"、"保存"按钮 、"删除"、"退出"等按钮,增加数据网络DataGrid1,文本盒及下拉列表盒。布置好后如下图:
[责任编辑:editor]

把在继承的Form中可能会重新设置控件的控件对象的友元属性改为公有属性:
要使模板FrmModule中的控件能够在继承的窗体中能够修改其所继承的控件属性,必须把其友元属性改为公有属性
Public WithEvents Label2 As System.Windows.Forms.Label
Public WithEvents txt4 As System.Windows.Forms.ComboBox
…
定义FrmModule类的全局变量
FrmModule直接调用CData类的方法,完成所需功能。
Public Shared gCls As New CData()
Public gIsUpdate as Boolean
定义FrmModule的方法
在FrmModule的Load事件中连接数据库,并填充网格数据,即:
Public Shadows Sub FrmModel_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
gCls.DB.ConnOracle("GDZC", "liyu", "123")
gCls.FillGrid(Me.DataGrid1)
End Sub
当单击DataGrid1时,应把DataGrid1的当前行写入输入控件中供用户修改数据,取得当前行的标识,这里用SystemID记录,同时表示可以更新。这个过程在DataGrid1的_CurrentCellChanged事件中完成, 即:
Private Sub DataGrid1_CurrentCellChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataGrid1.CurrentCellChanged
Dim RowIndex As Int16
RowIndex = DataGrid1.CurrentRowIndex
gCls.DataGridToText(Me)
gCls.SystemID = DataGrid1.Item(RowIndex, 0)
gIsUpdate = True
End Sub
编写ToolBar1的ButtonClick事件。由按钮文本识别所单击的对象.当时单击"增加"时表示不能更新,当时单击"保存"时要识别是更新还是插入数据。执行完后要重新填充网格, 即:
[责任编辑:editor]
Private Sub ToolBar1_ButtonClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ToolBarButtonClickEventArgs) Handles ToolBar1.ButtonClick
Select Case e.Button.Text
Case "增加"
gIsUpdate = False
Case "保存"
WriteDateToArray()
If gIsUpdate Then
gCls.Update()
Exit Select
Else
gCls.Insert()
End If
Case "删除"
If MsgBox ("真的删除该数据吗?(Y/N)") = 7 Then
Return
End If
gCls.Delete()
Case "退出"
Me.Close()
Exit Sub
End Select
gCls.FillGrid(DataGrid1)
End Sub
'把输入的数据用对象数组保存起来
Public Overridable Sub WriteDateToArray()
Dim i As Int16
Dim obj As Control
For i = 1 To gCls.FieldCount - 1
For Each obj In Me.Controls
If obj.TabIndex = i Then
gCls.ObjFields(i) = obj.Text
Exit For
End If
Next
Next
gCls.ObjFields(0) = gCls.DB.GetMaxID(gCls.UpdateTable, "FSYSTEMID") + 1
End Sub
生成继承窗体
生成继承窗体
我们的编程工作大部分都完成了,现在只需要增加新的继承窗体Form就可,方法是在Form项或项目菜单或工具栏选择添加继承的窗体,在弹出的Form中输入窗体名frmUser按打开,在选择继承器中选择FrmModule确定就生成了一新的窗体。 继承的继承如下:
Public Class frmUser:Inherits frmModel
在生成的窗体中增加一些必要的控件,同时与根据表中字段对应关系设置的TabIndex的值。
通过设置控件TabIndex的值,可以把该控件与物理表中的字段关系对应起来,如TabIndex为1,则对应物理表的第1个字段,如TabIndex为2,则对应物理表的第2个字段,如此例推。这方便把输入控件的数据写入表中,把网格数据写入输入控件中,这是技巧,其实现过程见方法:WriteDateToArray(),DataGridToText()。
指定物理表
在类frmUser的New方法中指定所操作的物理表名tUser。为什么要在这里指定表名呢?是因为程序在调用frmUser前,先必须申请frmUser的实类,同时进行实类化,自动执行构造函数New(),执行New()时,就把我们所要完成的功能代码放在这里,让它们自动执行。
[责任编辑:editor]
Public Sub New()
MyBase.New()
'该调用是 Windows 窗体设计器所必需的。
InitializeComponent()
gCls.UpdateTable = "tUser"
'在 InitializeComponent() 调用之后添加任何初始化
End Sub
更改控件的属性,隐藏不需要的控件
在 frmUser的Load事件中更改DataGrid1的标题并隐藏不需要的控件:
Private Sub frmUser_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
DataGrid1.CaptionText = "用户列表"
cbo.Visible = False
Me.Label6.Visible = False
End Sub
按同样方法,生成下面的窗体frmInfo,在此窗体上增加一些新控件,如下:

窗体frmInfo类继承frmModel:
Public Class frmInfo:Inherits frmModel
在类frmInfo的New方法中指定所操作的物理表名tInfo。
Public Sub New()
…
gCls.UpdateTable = " tInfo "
End Sub
现在,ToolBar1上增加了新的方法"导入",应该怎样处理呢?事实上只要在frmInfo的ToolBar1_ButtonClick事件中识别时"导入",写要处理的任务就可,如下:
Private Sub ToolBar1_ButtonClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ToolBarButtonClickEventArgs) Handles ToolBar1.ButtonClick
Select Case e.Button.Text
Case "导入"
…
End Select
End Sub
通过上面的窗体继承方法,凡是对物理表的操作都可以通过这些方法来完成,而不必再重复编写代码。
新的要求
现在假设有物理表tChange与tInfo表是一关联表,通过关键字段fID关联,既表tChange的字段fID的数据是通过tInfo表提供,其它来源于用户的输入,要生成tChange表数据处理的窗体。要实现这些功能,我们同样通过窗体的继承来完成(有物理表tSub也如此)。
[责任编辑:editor]
.先定义窗体模板frmModul2,此frmModul2继承frmModul, 即:Public Class frmModul2: Inherits Asset.frmModel。 在frmModul2中加入列表盒控制ListBox1和其它控制,布置好后如下图:

.在frmModul2的Load事件中填充ListBox的数据并隐藏不必要的控件。
Private Sub frmModul2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
FillListBox(Me.ListBox1)
Me.tlbAdd.Visible = False
End SubPublic
Sub FillListBox(ByVal ListBox As ListBox)
Dim StrSQL As String
StrSQL = "select Distinct fID || ' ' || fName as a FROM tInfo "
Dim dr As OleDb.OleDbDataReader
dr = DB.SQLDataReader(StrSQL)
While dr.Read
ListBox.Items.Add(dr.Item("a"))
End While
dr.Close()
End Sub
.当单击ListBox1时选择当前数据并填充至txt1, txt3中
Public Overridable Sub ListBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
Dim i, txt
i = ListBox1.SelectedIndex
txt = ListBox1.Items(i)
txt1.Text = txt.Substring(0, 15)
txt3.Text = txt.Substring(16, Len(txt) - 16).trim
txtID.Text = gCls.GetMaxSystemID + 1
gCls.FillGrid(DataGrid1)
gIsUpdate = False
End Sub
好了,我们的窗体模板又做好了,下面我们通过窗体模板frmModul2生成继承窗体frmChange,即:
Public Class frmChange:Inherits Asset.frmModul2
生成的窗体如下图:

依照前面设置所操作的表tChange和其它,不必写其它代码。
[责任编辑:editor]
Public Sub New()
…
gCls.UpdateTable = "tChange"
End Sub
同样可生成操作表tSub的窗体frmSub。

总结
通过以上面事例的介绍,可以生成若干同类似的窗体。不论操作多少表,都可通过窗体继承得到,物理表的操作一次性完成,不必在每个窗体中写代码,所完成的功能及界面布局全在模板窗体中完成。这样极大地降低了编写代码的工作量,同时为设置窗体整体外观提供以方便,提高了开发效率,也降低了软件的测试和维护的工作量。继承是面向对象的开发语言的一个重要组成部分,在程序设计中充分的利用继承关系,可以最大范围的提高程序的可利用性,使得用最少的代码开发比较大的应用程序。
[责任编辑:editor]