在数据库应用程序开发中,我们知道数据操作的主要任务是:“浏览、编辑、删除、添加记录”,利用VB.NET的“数据窗体向导”可以帮助我们迅速创建实现上述功能的Windows 窗体及相关代码,这种自动生成的代码完全具有实用性和可借鉴性。然而笔者在使用中发现,在“数据窗体向导”的操作过程中,如果我们选择的是“单个控件的单个记录”显示样式,假如控件绑定到的数据源中的字段又有不允许为Null值的话,运行自动生成的窗体,单击“添加”后会出现错误。
为了避免该错误,已有的解决方法是:“添加”功能不使用“数据窗体向导”生成的代码,而是新建一窗体然后采用非数据绑定结构来单独实现“添加”功能。笔者经过探索发现完全没有必要另建窗体,只需对“数据窗体向导”生成的代码进行适当的改进就可以在同一个窗体中同时实现“浏览、编辑、删除、增加”功能,如下通过实例说明(注:开发工具为Microsoft Visual Studio .NET 2003 ,示例数据库是SQL Server 2000中的“pubs”;以下代码虽然以VB为例,但方法同样适用于VS.NET中的其它开发语言)。
一、 有不允许为Null值的字段绑定到非TextBox类控件
如下图一所示就是属于这类情况,窗体中“contract”旁的复选框控件绑定到的列字段“contract”就不允许有Null值。运行该窗体并“加载”后,单击“添加”可以“新增”记录(编号为24的记录),但单击导航按钮(比如最后一条“>>”),记录位置不能移到该新增记录。
图一窗体及代码是由“数据窗体向导”自动生成(菜单操作:“文件/添加新项”,然后选 “数据窗体向导”),数据窗体实例的名称是“DEMO”,数据集的名称是“DS”,数据连接是以SQL Server 2000中的“pubs” 示例数据库为例,选择“authors”表,选择“单个控件的单个记录”显示样式,其它都采用“数据窗体向导”的默认值。图一这一类情况的改进方法是首先修改“添加”按钮的单击事件处理代码,如下所示。
Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAdd.Click
Try
Me.BindingContext(objDS, "authors").EndCurrentEdit()
Me.BindingContext(objDS, "authors").SuspendBinding() '临时挂起数据绑定
Me.BindingContext(objDS, "authors").AddNew() '增加新记录
'如下两层循环先找出authors表中不允许为null的列字段,然后找出绑定到该字段的binding对象,
'依据绑定到的控件属性不同来为新增记录中不能为空的字段赋不同初始值
Dim i As Integer
For i = 0 To objDS.authors.Columns.Count - 1 '遍历authors表中所有列字段
'找出authors表中不允许为null的列字段
If objDS.Tables("authors").Columns(i).AllowDBNull = False Then
Dim j As Integer
For j = 0 To Me.BindingContext(objDS, "authors").Bindings.Count - 1 '遍历所有Binding对象
'找出绑定到不允许为null字段的binding对象
If Me.BindingContext(objDS, "authors").Bindings(j).BindingMemberInfo.BindingField = _
objDS.authors.Columns(i).ColumnName Then
'如果绑定到的控件的属性为Text
If BindingContext(objDS, "authors").Bindings(j).PropertyName() = "Text" Then
'新增加记录中不能为null的字段赋初始值"",该语句还可用紧挨着的注释语句代替!
Me.BindingContext(objDS, "authors").Current(j) = ""
'BindingContext(objDS, "authors").Current(i) = BindingContext(objDS, "authors"). _
' Bindings(j).Control.Text()
Exit For '退出当前For循环,寻找下一个不允许为null的列字段
Else
'如果绑定到的控件的属性为Checked(其它绑定属性…,应该可以自己仿照搞定了.)
If BindingContext(objDS, "authors").Bindings(j).PropertyName = "Checked" Then
'新增加记录中不能为null的字段赋初始值True, 该语句可用紧挨着的4行注释语句代替!
Me.BindingContext(objDS, "authors").Current(j) = True
'Dim tmpobj As Object
'tmpobj = BindingContext(objDS, "authors").Bindings(j).Control
'tmpobj = CType(tmpobj, CheckBox)
'Me.BindingContext(objDS, "authors").Current(j) = tmpobj.checked
Exit For '退出当前For循环,寻找下一个不允许为null的列字段
End If
End If
End If
Next
End If
Next
'如新增加行不需利用row_changing事件进行数值验证,可启用下边注释行
'Me.BindingContext(objDS, "authors").EndCurrentEdit()
Me.BindingContext(objDS, "authors").ResumeBinding() '恢复数据绑定
'移动表中位置到新增加记录
Me.BindingContext(objDS, "authors").Position = Me.BindingContext(objDS, "authors").Count - 1
Catch eEndEdit As System.Exception
System.Windows.Forms.MessageBox.Show(eEndEdit.Message)
Me.BindingContext(objDS, "authors").CancelCurrentEdit() '出错,取消新增加行
Me.BindingContext(objDS, "authors").ResumeBinding()
End Try
Me.objDS_PositionChanged()
End Sub
以上btnAdd.Click事件处理代码改进的实质是:临时挂起数据绑定,新增一记录,然后对记录中不允许为Null值的列字段赋值(如不需利用rowchanging事件进行新增记录数据检验,也可仅对不允许null值且不能绑定到文本框TextBox的字段赋初值),最后恢复数据绑定。
此外,还要对四个导航按钮(第一条、上一条、下一条、最后一条)的单击事件处理程序作如下修改(为了简化,此处只以上一条“<”按钮的单击事件处理程序为例,其它按钮仿照)。
Private Sub btnNavPrev_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNavPrev.Click
Try '采用try语句进行错误处理(Null及唯一性)
Me.BindingContext(objDS, "authors").Position = (Me.BindingContext(objDS, "authors").Position - 1)
Catch ex As Exception
MessageBox.Show(ex.Message) '显示错误信息
Exit Sub '如有错误,不许移动位置
End Try
Me.objDS_PositionChanged()
End Sub
如果想利用rowchanging事件对新增加或编辑…记录进行数据检验(建议),则还需要增加如下rowchanging事件处理代码。
Private Sub row_changing(ByVal sender As Object, ByVal e As DS.authorsRowChangeEvent)
'对新增加行进行数值验证
If e.Action = DataRowAction.Add Then
'此处为了简单,以字段au_id不能为""为例
If editau_id.Text = "" Then
Throw (New Exception("数据不能为空"))
End If
End If
'还可以对记录的编辑值进行验证
'If e.Action = DataRowAction.Change Then
'此处填入编辑验证代码
'End If
'其它验证,依照上面加入吧
End Sub
要启动rowchanging事件处理,请在程序的适当位置加入如下语句(本人是加在LoadDataSet()方法中的语句objDS.Merge(objDataSetTemp)后面)。
AddHandler objDS.authors.authorsRowChanging, AddressOf row_changing
二、 虽然有不允许为Null值的字段,但全部都能绑定到TextBox类控件
为了说明方便,我们再利用“数据窗体向导”新建一窗体(注:新窗体可以建在上一个窗体的同一个项目中,数据窗体的名称是“demo2”,新建数据集名称为“ds2”),该数据窗体是绑定到“pubs”数据库中的“employee”表。如图二所示,该表中的所有不允许为Null值的字段都能绑定到TextBox类控件。
运行该窗体后,单击“加载”后可以新增记录(编号为44的记录),但是如果新增记录后不输入任何值而单击任一导航按钮(比如上一条“<”),会出现如图三所示错误。
这一类错误较轻,完全可以采用第一种情况的解决方法,当然如果你嫌多的话,还可以采用如下更为简单的方法: 在“数据窗体向导”自动生成的代码中,只需对四个导航按钮(第一条、上一条、下一条、最后一条)的单击事件处理程序代码进行适当修改(注意:还是按照第一种情况中四个导航按钮的代码修改方法进行修改),其它代码不变也不增加,一切就0k了,此时再试试,你会见到如图四友好的界面,提示你必须对不允许空值的列字段进行输入,然后才能改变记录位置。
三、 总结
当有不许Null值的字段绑定到数据窗体上时,为了避免出现新增记录错误,在上面笔者介绍了可以在代码中找到解决办法,另外我们还可以在数据集中找到解决办法:“在数据集的架构中,对不允许Null值的列字段给它设置一默认值即可”,当然具体采用哪种解决方法还是请你根据实际情况灵活选择。
上面给出了第一种情况的主要解决代码,末尽之处请大家参照下边完整代码。
Public Class DEMO
Inherits System.Windows.Forms.Form
'为了简化,“Windows 窗体设计器生成的代码”去掉了,记住在“数据窗体向导”执行时按照以下要求即可。
'数据窗体实例的名称是“DEMO”,数据集的名称是“DS”,数据连接是以SQL Server 2000中
'的“pubs” 示例数据库为例,选择“authors”表、“单个控件的单个记录”显示样式,其它都采用的默认值。
Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
Me.BindingContext(objDS, "authors").CancelCurrentEdit()
Me.objDS_PositionChanged()
End Sub
Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click
If (Me.BindingContext(objDS, "authors").Count > 0) Then
Me.BindingContext(objDS, "authors").RemoveAt(Me.BindingContext(objDS, "authors").Position)
Me.objDS_PositionChanged()
End If
End Sub
Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAdd.Click
Try
Me.BindingContext(objDS, "authors").EndCurrentEdit()
Me.BindingContext(objDS, "authors").SuspendBinding()
Me.BindingContext(objDS, "authors").AddNew()
Dim i As Integer
For i = 0 To objDS.authors.Columns.Count - 1
If objDS.Tables("authors").Columns(i).AllowDBNull = False Then
Dim j As Integer
For j = 0 To Me.BindingContext(objDS, "authors").Bindings.Count - 1
If Me.BindingContext(objDS, "authors").Bindings(j).BindingMemberInfo.BindingField = _
objDS.authors.Columns(i).ColumnName Then
If BindingContext(objDS, "authors").Bindings(j).PropertyName() = "Text" Then
Me.BindingContext(objDS, "authors").Current(j) = ""
'BindingContext(objDS, "authors").Current(i) = BindingContext(objDS, "authors"). _
' Bindings(j).Control.Text()
Exit For
Else
If BindingContext(objDS, "authors").Bindings(j).PropertyName = "Checked" Then
Me.BindingContext(objDS, "authors").Current(j) = True
'Dim tmpobj As Object
'tmpobj = BindingContext(objDS, "authors").Bindings(i).Control
'tmpobj = CType(tmpobj, CheckBox)
'Me.BindingContext(objDS, "authors").Current(i) = tmpobj.checked
Exit For
End If
End If
End If
Next
End If
Next
Me.BindingContext(objDS, "authors").ResumeBinding()
Me.BindingContext(objDS, "authors").Position = Me.BindingContext(objDS, "authors").Count - 1
Catch eEndEdit As System.Exception
System.Windows.Forms.MessageBox.Show(eEndEdit.Message)
Me.BindingContext(objDS, "authors").CancelCurrentEdit()
Me.BindingContext(objDS, "authors").ResumeBinding()
End Try
Me.objDS_PositionChanged()
End Sub
Private Sub btnUpdate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpdate.Click
Try
'尝试更新数据源。
Me.UpdateDataSet()
Catch eUpdate As System.Exception
'在此处添加错误处理代码。
'显示错误信息(如果有)。
objDS.RejectChanges() '我增加的语句
System.Windows.Forms.MessageBox.Show(eUpdate.Message)
End Try
Me.objDS_PositionChanged()
End Sub
Private Sub btnLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoad.Click
Try
'尝试加载数据集。
Me.LoadDataSet()
Catch eLoad As System.Exception
'在此处添加错误处理代码。
'显示错误信息(如果有)。
System.Windows.Forms.MessageBox.Show(eLoad.Message)
End Try
Me.objDS_PositionChanged()
End Sub
Private Sub btnNavFirst_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNavFirst.Click
Try
Me.BindingContext(objDS, "authors").Position = 0
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
Me.objDS_PositionChanged()
End Sub
Private Sub btnLast_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLast.Click
' Me.BindingContext(objDS, "authors").Position = (Me.objDS.Tables("authors").Rows.Count - 1)
Try
'注意下一条语句我进行了修改
Me.BindingContext(objDS, "authors").Position = Me.BindingContext(objDS, "authors").Count - 1
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
Me.objDS_PositionChanged()
End Sub
Private Sub btnNavPrev_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNavPrev.Click
Try '采用try语句进行错误检查(Null及唯一性)
Me.BindingContext(objDS, "authors").Position = (Me.BindingContext(objDS, "authors").Position - 1)
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub '如有错误,不许移动位置
End Try
Me.objDS_PositionChanged()
End Sub
Private Sub btnNavNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNavNext.Click
'If BindingContext(objDS, "authors").Position = BindingContext(objDS, "authors").Count - 1 Then '可不要
' MessageBox.Show("数据到顶了!")
' Exit Sub
'End If
Try
Me.BindingContext(objDS, "authors").Position = (Me.BindingContext(objDS, "authors").Position + 1)
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
Me.objDS_PositionChanged()
End Sub
Private Sub objDS_PositionChanged()
Me.lblNavLocation.Text = (((Me.BindingContext(objDS, "authors").Position + 1).ToString + " 的 ") _
+ Me.BindingContext(objDS, "authors").Count.ToString)
End Sub
Private Sub btnCancelAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancelAll.Click
Me.objDS.RejectChanges()
Me.objDS_PositionChanged() '我增加的语句
End Sub
Public Sub UpdateDataSet()
'创建一个新数据集来保存对主数据集所做的更改。
Dim objDataSetChanges As DS = New DS
'停止当前的任何编辑。
Me.BindingContext(objDS, "authors").EndCurrentEdit()
'获取对主数据集所做的更改。
objDataSetChanges = CType(objDS.GetChanges, DS)
'检查是否做了任何更改。
If (Not (objDataSetChanges) Is Nothing) Then
Try
'需要做一些更改,所以尝试通过调用 update 方法
'和传递数据集以及任何参数来更新数据源。
Me.UpdateDataSource(objDataSetChanges)
objDS.Merge(objDataSetChanges)
objDS.AcceptChanges()
Catch eUpdate As System.Exception
'在此处添加错误处理代码。
Throw eUpdate
End Try
'添加代码以检查返回的数据集中是否有任何可能已被
'推入到行对象错误中的错误。
End If
End Sub
Public Sub LoadDataSet()
'创建一个新数据集以保存从 FillDataSet 调用返回的记录。
'使用了一个临时数据集,这是因为填充现有的数据集
'需要重新绑定数据绑定。
Dim objDataSetTemp As DS
objDataSetTemp = New DS
Try
'尝试填充临时数据集。
Me.FillDataSet(objDataSetTemp)
Catch eFillDataSet As System.Exception
'在此处添加错误处理代码。
Throw eFillDataSet
End Try
Try
'清空数据集中的旧记录。
objDS.Clear()
'将记录合并到主数据集中。
objDS.Merge(objDataSetTemp)
'我在此处加入rowchanging事件
AddHandler objDS.authors.authorsRowChanging, AddressOf row_changing
Catch eLoadMerge As System.Exception
'在此处添加错误处理代码。
Throw eLoadMerge
End Try
End Sub
Public Sub UpdateDataSource(ByVal ChangedRows As DS)
Try
'在有挂起的更改时,只需要更新数据源即可。
If (Not (ChangedRows) Is Nothing) Then
'打开连接。
Me.OleDbConnection1.Open()
'尝试更新数据源。
OleDbDataAdapter1.Update(ChangedRows)
End If
Catch updateException As System.Exception
'在此处添加错误处理代码。
Throw updateException
Finally
'无论是否引发了异常都关闭连接。
Me.OleDbConnection1.Close()
End Try
End Sub
Public Sub FillDataSet(ByVal dataSet As DS)
'在填充数据集前关闭约束检查。
'这允许适配器填充数据集而不用考虑
'表之间的依赖项。
dataSet.EnforceConstraints = False
Try
'打开连接。
Me.OleDbConnection1.Open()
'尝试通过 OleDbDataAdapter1 填充数据集。
Me.OleDbDataAdapter1.Fill(dataSet)
Catch fillException As System.Exception
'在此处添加错误处理代码。
Throw fillException
Finally
'重新打开约束检查。
dataSet.EnforceConstraints = True
'无论是否引发了异常都关闭连接。
Me.OleDbConnection1.Close()
End Try
End Sub
Private Sub row_changing(ByVal sender As Object, ByVal e As DS.authorsRowChangeEvent)
'对新增加行进行数值验证
If e.Action = DataRowAction.Add Then
'此处为了简单,以字段au_id不能为""为例
If editau_id.Text = "" Then
Throw (New Exception("数据不能为空"))
End If
End If
'还可以对记录的编辑值进行验证
'If e.Action = DataRowAction.Change Then
'此处填入编辑验证代码
'End If
'其它验证,依照上面加入吧
End Sub
End Class
以上代码在Microsoft Visual Studio .NET 2003 + SQL Server 2000中的“pubs” 示例数据库中调试通过。