首页 > 代码库 > 数据绑定和数据网格视图(DataGridView)控件

数据绑定和数据网格视图(DataGridView)控件

数据绑定和数据网格视图(DataGridView)控件

 

数据网格视图控件,不像我们前面看到的控件,它可以显示多个列,但是,数据必须格式化,使数据网格知道要显示哪一列。有两种实现方法:一个是把数据网格视图绑定到数据表(DataTable),另一个是把网格到绑定对象列表,对象有许多属性,不同的属性就成为网格的列。

下面的例子是一种简单的解决方案,绑定到数据集(DataSet):

 

open System

open System.Collections.Generic

open System.Configuration

open System.Data

open System.Data.SqlClient

open System.Windows.Forms

 

// creates a connections then executes thegiven command on it

let createDataSet commandString =

  //read the connection string

  letconnectionSetting =

    ConfigurationManager.ConnectionStrings.["MyConnection"]

  //create a data adapter to fill the dataset

  letadapter = new SqlDataAdapter(commandString, connectionSetting.ConnectionString)

  //create a new data set and fill it

  letds = new DataSet()

  adapter.Fill(ds)|> ignore

  ds

 

// create the data set that will be boundto the form

let dataSet = createDataSet "selecttop 10 * from Person.Contact"

 

// create a form containing a data bounddata grid view

let form =

  lettemp = new Form()

  letgrid = new DataGridView(Dock = DockStyle.Fill)

  temp.Controls.Add(grid)

  grid.DataSource<- dataSet.Tables.[0]

  temp

 

// show the form

Application.Run(form)

 

 

运行前面的代码,可以看到如图 9-2 显示的结果。

图 9-2 绑定了数据的数据网格

 

若不使用 DataSet,还可以使用 F# 记录类型。这样,通常需要创建一个泛型函数(generic

function),通过反射(reflection)创建并发布强类型集合。下面的代码演示了这种泛型函数,把它包装在模块中,这样,就能更方便地把它用于其他代码,然后,用这个模块执行对数据库的查询:

 

module Strangelights.DataTools

open System

open System.Collections.Generic

open System.Configuration

open System.Data

open System.Data.SqlClient

open Microsoft.FSharp.Reflection

 

// a command that returns dynamicallycreated stongly typed collection

let execCommand<‘a> commandString :seq<‘a> =

  //the opener that executes the command

  letopener() =

    //read the connection string

    letconnectionSetting =

      ConfigurationManager.ConnectionStrings.["MyConnection"]

    //create the connection and open it

    letconn = new SqlConnection(connectionSetting.ConnectionString)

    conn.Open()

    //excute the command, ensuring the read will close the connection

    letcmd = conn.CreateCommand(CommandType = CommandType.Text,

                               CommandText = commandString)

    cmd.ExecuteReader(CommandBehavior.CloseConnection)

 

// the generator, that generates anstrongly typed object for each row

let generator (reader : IDataReader) =

  ifreader.Read() then

    //get the type object and its properties

    lett = typeof<‘a>

    //get the values for the row from the reader

    letvalues = Array.create reader.FieldCount (new obj())

    reader.GetValues(values)|> ignore

    letconvertVals x = match box x with | :? DBNull -> null | _ -> x

    letvalues = Array.map convertVals values

    //create the record and return it

    Some(FSharpValue.MakeRecord(t, values) :?> ‘a)

  else

    None

 

// generate the sequence

Seq.generate

  opener

  generator

  (funr -> r.Dispose())

 

例子代码的第一行使用了一个我们之前尚未用到过的方法,显式声明了函数的类型参数:

 

let execCommand<‘a> commandString :seq<‘a>

 

这样做,能够显式给定泛型参数‘a,这个类型参数然后用于创建类型对象,再对它做反射:

 

let t = typeof<‘a>

 

这个函数是用来处理 F# 记录类型,它的字段完全匹配查询结果的字段;如果不满足这个先决条件,代码就失败;然而,这个先决条件通常是在应用程序中以反射的形式使用的。

前面已经定义的泛型函数 execCommand,能够用于任何查询,匹配记录类型。下面的代码演示如何应用:

 

open System

open System.Windows.Forms

open Strangelights.DataTools

 

// a type that mirrors the type of rowbeing created

type Contact =

  {ContactID: Nullable<int>;

   NameStyle:Nullable<bool>;

   Title:string;

   FirstName:string;

   MiddleName:string;

   LastName:string;

   Suffix:string;

   EmailAddress:string;

   EmailPromotion:Nullable<int>;

   Phone:string;

   PasswordHash:string;

   PasswordSalt:string;

   AdditionalContactInfo:string;

   rowguid:Nullable<Guid>;

   ModifiedDate:Nullable<DateTime> }

 

// a form containing a data bound data grid

let form =

  lettemp = new Form()

  letgrid = new DataGridView(Dock = DockStyle.Fill)

  temp.Controls.Add(grid)

  letcontacts =

    execCommand<Contact>"select top 10 * from Person.Contact"

  letcontactsArray = contacts |> Seq.to_array

  grid.DataSource<- contactsArray

  temp

 

// show the form

Application.Run(form)

 

最重要的是下面一行:

 

let contacts =

  execCommand<Contact>"select top 10 * from Person.Contact"

 

为泛型函数 execCommand 显式声明了类型参数。这个例子的结果同前面的例子,如图 9-2 所示。

 

注意

使用对象-关系映射,比例NHibernate,执行这种任务已经相当普遍,这些工具提供了高度的灵活性,但它们往往过度依赖于纯正 C# 的功能,要想在 F# 中使用,体验并不良好,因此,我在书中没有讨论任何相关内容。但是,许多人,包括我自己正在努力解决这个问题;同时,建议你关注我的博客:http://strangelights.com/blog。