Visual Studio 2005中的新DataSet特性(2)

构建以数据为中心的窗体

  生成了类型化 DataSet 之后,接下来要构建一个显示其数据的窗体。对于 .NET Framework 2.0 和 Visual Studio 2005 中的 WinForms 以及数据绑定,我不会深入研究它们的所有细节和新特性(因为可研究的东西太多了),但如果要了解以下这方面所取得的一些进展 — 如何能更简单、更灵活地建立功能上以数据为中心的窗体,这到是一个难得的机会。

  工具箱中的数据组件

  如果您常常使用工具箱“Data”选项卡上的标准数据组件来构建以数据为中心的代码,那么打开 Visual Studio 2005 后,可能会有些沮丧和担心,因为您无法找到它们。这当然是“设计”好的,Microsoft 希望引导用户利用新的类型化 DataSets 和 TableAdapters。如果实在想使用以前没有类型化的组件,您可以手动将它们添加到工具箱中。我强烈建议您不要将新的 TableAdapters 与部分类技术(新的类型化 DataSets)一起使用;TableAdapters 的使用和扩展更容易。

  在 Visual Basic 的团队日记中,可以找到更多各种各样的设计决策信息以及这些决策的深层思想。

  在 Visual Studio 设计器中打开一个窗体时,Visual Studio 工具箱会出现一个以项目名为标签的选项卡。在项目中添加一个数据源并至少将其编译一次后,该选项卡中将出现创建的 DataSets 和 TableAdapters。您可以将这些组件拖放到窗体设计器上,在使用设计器实现数据访问组件的情况下,这是很好的做法,但通常情况下往往不这么做。而是使用以下三种不同方法中的一种来构建以数据为中心的窗体。首先,逐步介绍第一种方法,这种方法最简单,甚至可能最普遍。它就是所谓的“一次拖动”数据绑定。

1.双击解决方案资源管理器中的 Form1.vb,打开 Visual Studio 窗体设计器中的 Form1。

2.展开数据源窗口中的 Orders 表节点。

注意,DataSet 中的每个表和每行列都有一个与之相关的图标。在将表或列拖放到窗体上时,这些图标表示用于绑定数据的 WinForm 控件类型(或者是“放置”类型)。从相关联的下拉列表中选择某个控件类型选项,再选择一个该类型的控件,可以改变控件类型。注意,该列表还包含 None(显示为空)和 自定义(指定您喜欢的任何控件)这两个选项。

仅在当前活动窗口是窗体(或组件)设计器时,这些图标和放置类型列表可见。此外,您不能在数据源窗口中拖放控件,图标的改变会指示出这一点。

3.将 Orders 表的控件类型从 DataGridView 更改为 Details。这就是说,将整个 Orders 表拖动到窗体上时,会构建一个每次显示一行详细信息的窗体,而不是显示网格中所有数据(一次显示所有行)的窗体。Details 视图会为每个列都添加一个标签和一个控件,控件类型是在数据源窗口中指定的类型。

4.将 Orders 表从数据源窗口拖动到设计器中的 Form1。

5.选择后七个 (7) 字段及其标签,将它们拖放到与前七个字段并排的位置上,这时的窗体外观如图 4 所示。



图 4. 设计器中的 Form1

6.启动应用程序,使用窗体顶部工具栏上的导航按钮逐个浏览记录,启动并确认应用程序可以正常运行。

接下来回顾一下将数据源表拖动到窗体上时 Visual Studio 的动作。查看窗体下面的组件栏,会看到它向窗体添加了四个组件。其中的两个组件 NorthwindDataSet 和 OrdersTableAdapter 我们已经熟悉了(您喜欢它们吗?)。OrdersTableAdapter 用于将数据库中的数据填写到 NorthwindDataSet 的 OrdersDataTable。甚至连执行 Fill 的一行代码也已经编写好,并自动添加到 Form1 的 Load 事件处理程序中了。

Me.OrdersTableAdapter.Fill(Me.NorthwindDataSet.Orders)

  对于数据绑定至关重要的类是 BindingSource 类,在当前示例中,它被命名为含义清晰的 OrdersBindingSource。BindingSource(在 Beta 1 版本中称为 DataConnector)提供将控件绑定到窗体所需的服务。它在数据源和绑定到其上的控件之间提供了一个中间层。通过设置 BindingSource 的 DataSource 和 DataMember 属性,可将它连接到数据源,然后将控件添加到控件的 DataBindings 集合中,从而将控件绑定到 BindingSource。所有与数据的交互(例如,记录导航、排序、筛选和编辑)均通过 BindingSource 完成。此外,还允许通过 List、Item 和 Current 属性访问底层数据。

  添加的另外一个组件是 OrdersBindingNavigator。BindingNavigator 类是提供标准用户界面的工具栏,用于导航和操作窗体上的数据。BindingNavigator(Beta 1 版本中称为 DataNavigator)是一个具有一组预配置按钮的 ToolStrip 控件。它可以连接到 BindingSource,将其作为自身的数据源,并且可以提供控制导航可用数据的工具栏按钮。如果您希望响应一些导航事件(而不是控制导航),那么应该挂钩 BindingSource 对象的事件。

  构建主-从窗体

  有了显示单个表数据的窗体之后,如果要以主-从形式显示另一个相关表,还会很容易吗?还是变得很难了?不如继续在 Form1 上完成下列步骤吧:

1.现在我们将使用“连接点”数据绑定来构建窗体 — 将一个控件从工具箱中拖动到窗体并调整它的位置,然后将一个元素从数据源窗口中拖放到该控件上,从而将两者互相连接起来.

2.选择工具箱中 All Windows Forms 选项卡上的 DataGridView 控件。将其拖置到 Form1 并调整它的位置,使其占据窗体下半部分空间。

3.返回到图 5 所示的数据源窗口,注意 Order Details 表实际上同时出现在该窗口中的两个位置。第一个位置是 NorthwindDataSet 的第一级子节点上,而且是 Orders 表的同辈节点。另一个位置是 Orders 表的子节点上,这表示它是相关的表。如果希望单独在窗体上显示 Order Details 表,可以选择 NorthwindDataSet 正下方 Order Details 表的匹配项。但在本示例中,我们希望显示 Order Details 表,因为它与主 Orders 表有关,所以我们选择 Orders 表正下方 Order Details 表的匹配项。



图 5. 数据源窗口中的 Order Details 表

4.选择 Orders 表下方出现的 Order Details 表,将其拖动到 Form1 上的 DataGridView。

5.这时 Order_DetailsBindingSource 和 Order_detailsTableAdapter 已经添加到 Form1 的组件栏中。

6.运行应用程序,使用 BindingNavigator 移动 Orders 表中的记录,如图 6 所示。注意观察 DataViewGrid 中显示的 Order Details 记录如何自动调整来只显示那些与当前 Orders 记录相关的记录。



图 6. Orders 表中 BindingNavigator

 

自定义生成代码

  在前文中查看 DataSet 的类代码时,您可能注意到了事实上有两个 Visual Basic 代码文件 — NorthwindDataSet.Designer.vb 和 NorthwindDataSet.vb。如果没有 NorthwindDataSet.vb 文件,请返回到 DataSet Designer 并双击设计器背景,创建该文件。

  这些文件都用于实现组成 DataSet 的类。使用两个文件是为了利用简单且功能非常强大的新特性 — partial classes。它是一个编译器功能,它允许在几个声明之间拆分类(或结构)的定义。不同的声明可能保存在不同的源代码文件中,只要声明都属于同一个程序集和命名空间就可以。Visual Studio 广泛采用了该功能,从而将由设计器生成的某个类的代码与开发人员编写的该类的代码区分开来。在 Visual Studio 2002/2003 中,所有窗体代码都是该窗体类声明的一部分,例如:

Public Class Form1
    Inherits System.Windows.Forms.Form

  该窗体(包括窗体上放置的任何控件)的初始化代码由 Visual Studio 生成。这些代码位于 InitComponent() 方法中,默认情况下,该方法出现在名为“Windows 窗体设计器生成代码”的代码区域中用户编写的代码之前。该区域通常关闭,这是为了尽量避免与您编写的代码搞混而分散您的注意力。在 Visual Studio 2005 中,代码保存在 Form1.Designer.vb 这一完全不同的文件中,这将更有助于您集中注意力。此外,如果想查看该文件的内容,在该窗体中就可以 — 单击解决方案资源管理器工具栏上的 Show All Files,展开 Form1.vb 节点,然后双击 Form1.Designer.vb,代码编辑器中将显示代码。文件 Form1.vb 只包含您(作为开发人员)为 Form1 类编写的代码。

  至于 DataSet 及其相关类的代码,使用部分类和不同文件区分设计器代码和开发人员编写的代码则具有更重大的意义。这种区分不止可使代码更整洁,而且解决了在 Visual Studio 2002/2003 中使用类型化 DataSets 时存在的一个主要问题。

  通常,您还希望在为 DataSet 及其相关类自动生成的代码基础上进行扩展或添加,例如附加属性或自定义验证代码。那么,您可以放心地在生成代码中添加。只要不更改架构就可以,否则您需要重新生成 DataSet 代码。在 Visual Studio 2002/2003 中,由于代码和生成代码都添加在某一个文件中,因此重新生成代码时将清除添加的代码。正是由于使用了部分类,Visual Studio 2005 才得以避免出现这种情况。新生成的代码将覆盖现有的设计器代码并保存在以 .Designer.vb 为扩展名的文件中,而 .vb 文件中开发人员编写的代码仍然完整无缺。

  要扩展使用部分类的 DataSet 功能,一种方法是添加自定义的验证代码。这时可以在生成的类型化 DataSet 中添加一些应用程序逻辑。我们将自定义的验证代码和初始化添加到 NorthwindDataSet 中 Orders 表内增加的新行中。添加一个新行时,我们希望检查传递的邮政编码值是否与实际传递的城市相匹配。如果不是,则将 ShipPostalCode 字段的值更改为 Invalid。假定实现了以下这样一个函数,该函数在给定的邮政编码与给定城市匹配的情况下返回 True:

Function IsPostalCodeInCity (ByVal PostalCode as string, ByVal City as string) As Boolean

  通过以下方式可将该检验条件添加到 NorthwindDataSet:

1.选择数据源窗口工具栏中的 Edit DataSet with Designer,打开 DataSet 设计器。

2.双击设计器背景的空白区域。在代码编辑器中打开文件 NorthwindDataSet.vb。

3.输入以下代码来替换默认代码:

Partial Public Class NorthwindDataSet
    Partial Class OrdersDataTable
        Protected Sub ValidateNewRow(ByVal sender As Object, _
               ByVal e As System.Data.DataTableNewRowEventArgs) _
               Handles Me.TableNewRow
            ' Create a strongly typed instance of the row
            ' This helps us avoid code in quotes, 
' eg, e.Row("ShipPostalCode")
            Dim ordersRow As NorthwindDataSet.OrdersRow
            ordersRow = e.Row
            If Not ordersRow.IsShipPostalCodeNull And _
                    Not ordersRow.IsShipCityNull Then
                If Not IsPostalCodeInCity(ordersRow.ShipPostalCode, _
                        ordersRow.ShipCity) Then
                    ' Set the value of the Ship Postal Code
                    ordersRow.ShipPostalCode = "Invalid"
                    ' Typically, changing a users data is a bad user experience
  ' So indicate an error with the ErrorProvider
                    ' We are illustrating both approaches here
                    ordersRow.SetColumnError( _
                        ShipPostalCodeColumn.ColumnName, "Invalid Postal Code")
                Else
                    ' we always need to reset the error when the value is valid
                    ordersRow.SetColumnError( _
                        ShipPostalCodeColumn.ColumnName, String.Empty)
                End If
            End If
        End Sub
    End Class 

    Private Shared Function IsPostalCodeInCity(ByVal postalCode As String, _
ByVal city As String) As Boolean
        ' This is a stub, just to check functionality
        If city = "Rio de Janeiro" Then
            Return False
        Else
            Return True
        End If
    End Function
End Class

  注意,DataSet 的所有相关类(例如,OrdersDataTable)在其内部均作为嵌套类实现。前面代码中的部分类声明就反映了这一实现过程。

  为了让错误提供程序来提示错误,以补充或代替将邮政编码值更改为 Invalid 这一操作,可执行以下步骤:

1.将 Error Provider 控件从工具箱拖放到 Form1,将其放置在“Ship Postal Code”文本框右侧。

2.在 Error Provider 属性窗口中,将 DataSource 属性设置为 OrdersBindingSource。

  要查看验证代码的执行情况,可以运行应用程序并导航到城市为“里约热内卢”的那条记录,图 7 对此做出说明



图 7. 错误提供程序控件

   该示例仅示范如何轻松地扩展类型化 DataSet 及其相关类的功能,这里的相关类指由 Visual Studio 自动生成的类。您或许还希望在生成的类中添加一些其他的方法和属性。当设计应用程序以及使用 DataSets 时,您会想到多种多样的可能性。关键是要记住,由于在 Visual Studio 2005 中可以使用部分类,您编写的代码是保存在另一个单独文件中的,因此重新生成 DataSet 类时您的代码不会受影响。

  小结

  使用 Visual Studio 2005 生成的类型化 DataSets 从未如此之简单而灵活。DataSetDesigner 为定义 DataSets 而提供了一个更简单、更自然的工具。在 DataSetDesigner 内就可以配置新的 TableAdapter 类,使用该类提供的单一而集中的机制,能够轻松地维护和执行针对某个特定数据表的多个不同查询和命令。利用部分类编译器功能,可以分离设计器生成的代码和开发人员编写的代码,同时在重新生成 DataSet 类时,避免了对为扩展这些类而编写好任意自定义代码的影响。此外,新的 .NET 数据绑定类和机制在结合了 Visual Studio 2005 内部提供的工具,从而使开发以数据为中心的应用程序更加快速、简便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值