VFP CursorAdapter 起步三(作者:Doug Hennig 譯者:fbilo)

可重用數據類

VFP 的程序員們想要一個可重用的數據類已經很久了。儘管在過去的版本中也有許多解決這個問題的辦法,不過總是有點美中不足。現在在 VFP 8裏,我們有了真正的可重用數據類。這個月,Doug 爲我們演示了怎樣通過建立 CursorAdapter 和 DataEnvironment 的子類來建立可重用的數據類、以及怎樣在表單和報表中使用它們。
正文
××
在過去的兩期雜誌中,我們討論了在 VFP 8 中新增的 CursorAdapter 基礎類。我個人的觀點是,這是 VFP 8 中最重要的改動之一,因爲它向我們提供了一個對象SQL Server這樣的非VFP數據源的簡單易用、統一的接口。此外,如你本月所能見到的那樣,它們還形成了可重用數據類的基礎。
在講述可重用數據類之前,讓我們先來看一下我建立的一些 CursorAdapter 和 DataEnvironment 的子類,我給它們增加了一些額外的功能,它們將成爲我們的可重用數據類的起點。
SFCursorAdapter
***************
SFCursorAdapter (在附件 SFDataClasses.vcx 中) 是 CursorAdapter 的一個子類,它擁有一些額外增加的功能,如下:
※ 它可以自動處理參數化查詢:你可以靜態(一個常量)也可以動態(一個表達式,例如“=Thisform.txtName.value”,當 Cursor 被打開或者刷新的時候,這個表達式會被運算)的定義一個參數值。
※ 它可以在 Cursor 被打開以後自動在該 Cursor 上建立索引。
※ 對於 ADO,它還會執行一些特殊的工作,例如把 DataSource 屬性設置爲一個 ADO RecordSet,把這個 RecordSet 的 ActiveConnection 屬性設置爲一個 ADO Connection 對象,當用到一個參數化查詢的時候,它還會建立一個 ADO Command 對象並把這個對象傳遞給 CursorFill 方法。
※ 它提供了簡單的錯誤處理(cErrorMessage 屬性裏會有錯誤的信息)。
※ 它還有 CursorAdapter 中缺少的 Update 和 Release 方法。
這個類的 INIT 方法建立兩個集合(使用新的 Collection 基礎類,它是維護某些東西的集合用的),一個是爲 SelectCmd 屬性可能會用到的參數而準備的,另一個是用於在 Cursor 被打開以後應該自動建立的標記。它還會 SET MULTILOCK ON,因爲這是 CursorAdapter Cursor 的需求。
This.oParameters = CreateObject('Collection')
This.oTags = CreateObject('Collection')
Set multilocks on
AddParameter 方法象 parameters 集合添加一個參數。給這個方法傳遞參數的名稱(這個名稱應該與該參數出現在 SelectCmd 屬性中的那個名稱相一致),根據需要也可以付上參數的值(如果你現在不給它傳遞參數的值,也可以以後再調用 Getparameter 方法來傳遞)。這段代碼演示了一對 VFP 8 中的新功能:新的 empty 基礎類,它沒有任何屬性、事件或者方法,因此是建立一個輕量級的對象的理想選擇;還有 AddProperty() 函數,它的作用跟 AddProperty 方法類似,區別是它用於那些沒有這個方法的對象。
lparameters tcName, tuvalue
local loParameter
loParameter = createobject('Empty')
addproperty(loParmeter, 'Name', tcName)
addproperty(loParmeter, 'value', tuvalue)
This.oParameters.Add(loParameter, tcName)
使用 GetParmeter 方法來返回一個特殊的 parameter 對象——通常是用在需要設置用於參數的值的時候。
lparameters tcName
local loParameter
loParameter = This.oParameters.Item(tcName)
return loParameter
SetConnection 方法用於將 DataSource 屬性設置爲希望的連接。如果 DataSourceType 是 “ODBC”,就給這個方法傳遞一個連接句柄;如果是“ADO”,DataSource 必須是一個ADO Recordset 對象,而且該對象的 ActiveConnection 屬性必須要設置爲一個活動 ADO Connection 對象,所以,我們需要向 SetConnection 方法傳遞這個 ADO Connection 對象, SetConnection 會建立一個 RecordSet,並且把這個 RecordSet 的 ActiveConnection 設置爲被傳遞的 ADO Connection 對象。
lparameters tuConnection
with this
do case
case .DataSourceType = 'ODBC'
.DataSource = tuConnection
case .DataSourceType = 'ADO'
.DataSource = Createobject('ADODB.RecordSet')
.DataSource.ActiveConnection = tuConnection
endcase
endwith
爲了建立 Cursor,我們調用 GetData 方法而不是 CursorFill 方法,因爲 GetData 能夠自動處理參數和錯誤。如果你想要建立一個不帶數據的 Cursor,那麼就給 GetData 方法傳遞一個 .T.。這個方法建立的第一樣東西,是與定義在 parameters 集合中的參數們同名的私有變量(在這裏調用了 GetParametervalue 方法,該方法會返回參數對象的值,如果該對象的值是一個以“=”開頭的表達式,那麼返回的將是運算該表達式之後所獲得的值。)下一步,如果我們是在使用 ADO 並且已經有了一些參數,這段代碼會建立一個 ADO Command 對象,並把該對象的 ActiveConnection 屬性設置爲 Connection 對象,然後把這個 Connection 對象傳遞給 CursorFill 方法——這是 CursorAdapter 處理 ADO 參數化查詢的需要。如果我們不是在用 ADO 、或者沒有任何參數,那麼代碼會簡單的調用 CursorFill 來填充 Cursor。注意,如果給 GetData 方法傳遞了 .T.,並且 CursorSchema 已經被填寫好了,那麼就是告訴 GetData 方法去使用 CursorSchema(這也是我想讓 CursorAdapter 基類擁有的功能)。現在如果 Cursor 被建立起來了,代碼會調用 GreateTags 方法來爲該 Cursor 建立想要的索引;如果 Cursor 沒有被建好,那麼它會調用 HandleError 方法來處理任何發生了的錯誤。
lparameters tlNoData
local loParameter, ;
lcName, ;
luvalue, ;
llUseSchema, ;
loCommand, ;
llReturn
with This
* tlNoData參數指定是否要向 Cursor 中填充數據,如果要填充的話,
* 我們就要把建立存儲所有參數的變量的活在這裏就做掉而不是放到一個其它的方法中去。
* 因爲我們希望這些變量的有效範圍是私有的。
if not tlNoData
for each loParameter in .oParameters
lcName = loParameter.Name
luvalue = .GetParametervalue(loParameter)
store luvalue to (lcName)
next loParameter
endif not tlNoData
* 如果我們正在使用 ADO,並且有了一些參數,那麼就需要有一個處理這些參數的 Command對象
llUseSchema = not empty(.CursorSchema)
if '?' $ .SelectCmd and (.DataSourceType = 'ADO' or (.UseDEDataSource and ;
.Parent.DataSourceType = 'ADO'))
loCommand = createobject('ADODB.Command')
loCommand.ActiveConnection = iif(.UseDEDataSource, ;
.Parent.DataSource.ActiveConnection, .DataSource.ActiveConnection)
llReturn = .CursorFill(llUseSchema, tlNoData, .nOptions, loCommand)
else
* 填充這個 cursor.
llReturn = .CursorFill(llUseSchema, tlNoData, .nOptions)
endif '?' $ .SelectCmd ...
* 如果 Cursor 建立成功,則爲之定義所有的 Tag,否則則處理髮生的錯誤。
if llReturn
.CreateTags()
else
.HandleError()
endif llReturn
endwith
return llReturn
還有一些方法這裏我們就不說了,你可以自己去研究它們。HandleError 方法使用 Aerror() 來判斷髮生了什麼錯誤,並把錯誤數組的第二個元素放到 cErrorMessage 屬性中去。Requery 方法與 GetData 類似,不過它是用來刷新 Cursor 中的數據。調用這個方法而不是 CursorRefresh 方法的原因就象 GetData 一樣:它能夠處理參數和錯誤。Update 方法很簡單:它就是調用 TableUpdate() 來提交當前數據源的更新,如果提交更新失敗,則調用 HandleError 方法來處理錯誤。AddTag 用於在 Cursor 被建立後將你想要建立的索引的信息添加到 Tags 集合中,而 GetData 方法會調用的 CreateTags 方法則會在自己的 Index ON 語句中用到這個集合中的信息。
這裏是使用這個類的一個例子,是從附件中的 TestCursorAdapter.prg 中拿來的。它從 SQL Server 自帶的 Northwind 數據庫的 Customers 表中取得數據。它的 SelectCmd 屬性裏是一個參數化查詢的 Select 語句,向你演示了怎樣用 AddParameter 方法來處理參數,以及怎樣用 AddTag 方法來自動地爲 Cursor 建立索引標識。
local loCursor as SFCursorAdapter of SFCursorAdapter, ;
loConnMgr as SFConnectionMgrODBC of SFRemote, ;
loParameter as Empty
lnHandle = sqlstringconnect('driver=SQL Server;server=(local);' + ;
'database=Northwind;uid=sa;pwd=;trusted_connection=no')
&& change password to appropriate value for your database
loCursor = newobject('SFCursorAdapter', 'SFDataClasses')
with loCursor
.DataSourceType = 'ODBC'
.Alias = 'Customers'
.SelectCmd = 'select * from customers where country = ?pcountry'
.SetConnection(lnHandle)
.AddParameter('pcountry', 'Brazil')
.AddTag('CustomerID', 'CustomerID')
.AddTag('Company', 'upper(CompanyName)')
.AddTag('Contact', 'upper(ContactName)')
if .GetData()
messagebox('Brazilian customers in CustomerID order')
set order to CustomerID
go top
browse
messagebox('Brazilian customers in Contact order')
set order to Contact
go top
browse
messagebox('Canadian customers')
loParameter = .GetParameter('pcountry')
loParameter.value = 'Canada'
.Requery()
browse
else
messagebox('Could not get the data. The error message was:' + ;
chr(13) + chr(13) + .cErrorMessage)
endif .GetData()
* Now try to do an invalid SELECT statement.
.SelectCmd = 'select * from customersx'
if .GetData()
browse
else
messagebox('Could not get the data. The error message was:' + ;
chr(13) + chr(13) + .cErrorMessage)
endif .GetData()
endwith
sqldisconnect(lnHandle)

SFDataEnvironment
*****************
在附件 SFDataClasses.vcx 中的這個數據和環境類要比 SFCursorAdapter 簡單的多。但它增加了一些非常有用的功能:
×× GetData 方法會調用所有在這個數據環境類裏面的 SFCursorAdapter 成員類的 GetData 方法,這樣你就不需要自己去一個個的調用它們。與此類似的是,Requery 方法和 Update 方法也會調用每個 SFCursorAdapter 成員類的 Requery 和 Update 方法。
×× 象 SFCursorAdapter 一樣,SetConnection 方法會把 DataSource 設置爲一個 ADO Recordset,並把這個 Recordset 的 ActiveConnection 屬性設置爲一個 ADO Connection 對象。不過,它還會調用所有 UseDEDataSource 屬性被設置爲 .F. 的 SFCursorAdapter 成員類的 SetConnection 方法。
×× 它提供了一些簡單的錯誤處理(cErrorMessage 屬性會被填入錯誤信息)
×× 它有一個 Release 方法。
現在我們看看這個類的一對方法。GetData 非常簡單:如果這個數據環境類的任何成員對象擁有 GetData 方法,則調用該方法:
lparameters tlNoData
local loCursor, ;
llReturn
for each loCursor in This.Objects
if pemstatus(loCursor, 'GetData', 5)
llReturn = loCursor.GetData(tlNoData)
if not llReturn
This.cErrorMessage = loCursor.cErrorMessage
exit
endif not llReturn
endif pemstatus(loCursor, 'GetData', 5)
next loCursor
return llReturn
SetConnection 方法稍微複雜一點:如果它的任何成員對象有 SetConnection 方法、並且該成員對象的 UseDEDataSource 屬性被設置爲 .F.,則調用該成員對象的 SetConnection 方法;然後,如果有任何一個 CursorAdapter 對象的 UseDEDataSource 屬性被設置爲了 .T.,則使用類似於 SFCusrorAdapter 中的那樣的代碼來設置自己的 DataSource:
lparameters tuConnection
local llSetOurs, ;
loCursor, ;
llReturn
with This
* Call the SetConnection method of any CursorAdapter that isn't using our
* DataSource.
llSetOurs = .F.
for each loCursor in .Objects
do case
case upper(loCursor.BaseClass) <> 'CURSORADAPTER'
case loCursor.UseDEDataSource
llSetOurs = .T.
case pemstatus(loCursor, 'SetConnection', 5)
loCursor.SetConnection(tuConnection)
endcase
next loCursor
* If we found any CursorAdapters that are using our DataSource, we'll need to
* set our own DataSource.
if llSetOurs
do case
case .DataSourceType = 'ODBC'
.DataSource = tuConnection
case .DataSourceType = 'ADO'
.DataSource = createobject('ADODB.RecordSet')
.DataSource.ActiveConnection = tuConnection
endcase
endif llSetOurs
endwith
TestDE.prg 做了一個演示,它使用 SFDataEnvironment 作爲容器,該容器中有一對 SFCursorAdapter 類。因爲這個例子用的是 ADO,所以每個 SFCursorAdapter 都需要它自己的 DataSource,因此它們的 UseDEDataSource 屬性都被設置成了 .F.(默認設置)。注意:只要簡單的調用一下 SFDataEnvironment 的 SetConnection 方法就能搞定爲每個 CursorAdapter 設置好 DataSource 屬性的事情。
local loConn as ADODB.Connection
loConn = createobject('ADODB.Connection')
loConn.ConnectionString = 'provider=SQLOLEDB.1;data source=(local);' + ;
'database=Northwind;uid=sa;pwd='
&& change password to appropriate value for your database
loConn.Open()
set classlib to SFDataClasses
loDE = createobject('SFDataEnvironment')
with loDE
.AddObject('CustomersCursor', 'SFCursorAdapter')
with .CustomersCursor
.Alias = 'Customers'
.SelectCmd = 'select * from customers'
.DataSourceType = 'ADO'
endwith
.AddObject('OrdersCursor', 'SFCursorAdapter')
with .OrdersCursor
.Alias = 'Orders'
.SelectCmd = 'select * from orders'
.DataSourceType = 'ADO'
endwith
.SetConnection(loConn)
if .GetData()
select Customers
browse nowait
select Orders
browse
else
messagebox('Could not get the data. The error message was:' + ;
chr(13) + chr(13) + .cErrorMessage)
endif .GetData()
endwith
loConn.Close()

可重用數據類
************
現在我們已經有了可用的 CursorAdapter 和 DataEnvironment 的子類,讓我們來談談可重用數據類的事情。
一件 VFP 程序員們已經向 Microsoft 要求了很久的事情是可重用數據類。例如,你可能有一個表單和一個報表,它們使用的是完全相同的一套數據,然而你卻不得不重複的去手動向數據環境中添加一個個表或者視圖——因爲數據環境是不可重用的。某些程序員(以及幾乎所有的應用程序框架提供商)採用了通過代碼來建立可重用數據環境(那時候數據環境是不能可視化的建立子類的)的方法,並且在表單上使用一個 “loader”對象來建立 DataEnvironment 子類的實例。不管怎麼說,這種方法總不是那麼正規,並且無法用於報表。
現在,在 VFP8 裏,我們不僅擁有了建立“能夠向任何需要數據的對象提供來自任何數據源的數據”的可重用數據類的能力,還有了建立“能夠寄宿數據類的”數據環境類的能力。在我寫這篇文章的時候,你還不能在報表中使用 CursorAdapter 或者 DataEnvironment 的子類,不過你可以編程的添加 CursorAdapter 子類(例如把這些代碼寫在 DataEnvironment 的 INIT 方法中)來利用可重用類的優點。
讓我們爲 Northwind 數據庫的 Customers 和 Orders 表建立一些數據類。CustomersCursor (在 NorthwindDataClasses.vcx 中)是 SFCursorAdapter 的一個子類,其屬性如表1:
表 1. CustomersCursor 的屬性
屬性 值
Alias        Customers

CursorSchema    CUSTOMERID C(5), COMPANYNAME C(40), CONTACTNAME C(30), CONTACTTITLE C(30),
          ADDRESS C(60), CITY C(15), REGION C(15), POSTALCODE C(10), COUNTRY C(15),
           PHONE C(24), FAX C(24)
KeyFieldList     CUSTOMERID
SelectCmd       select * from customers
Tables         CUSTOMERS
UpdatableFieldList  CUSTOMERID, COMPANYNAME, CONTACTNAME, CONTACTTITLE, ADDRESS, CITY,
            REGION, POSTALCODE, COUNTRY, PHONE, FAX
UpdateNameList   CUSTOMERID CUSTOMERS.CUSTOMERID, COMPANYNAME CUSTOMERS.COMPANYNAME,
            CONTACTNAME CUSTOMERS.CONTACTNAME, CONTACTTITLE CUSTOMERS.CONTACTTITLE,
           ADDRESS CUSTOMERS.ADDRESS, CITY CUSTOMERS.CITY, REGION CUSTOMERS.REGION,
          POSTALCODE CUSTOMERS.POSTALCODE, COUNTRY CUSTOMERS.COUNTRY,
          PHONE CUSTOMERS.PHONE, FAX, CUSTOMERS.FAX

你不會以爲我會是手動在屬性窗口中輸入所有這些屬性的值吧?當然不是!我是用 CursorAdapter 的生成器來乾的。這裏的技巧是打開“Use connection settings in builder only(只使用在生成器中的連接設置)”,填入連接信息以獲得一個活動連接,再填好 SelectCMD 以後,最後再用生成器來生成其它的屬性設置。
現在,任何時候你需要 Northwind 的 Customers 表中的數據,只要簡單的放一個 CustomersCursor 類就夠了。當然,我們還沒有定義任何連接信息,不過做到這樣就已經很不錯了,有了這個類就不需要擔心怎麼獲得數據(ODBC、XML、ADO)、使用哪種數據引擎(比如 SQL Server、Access 以及 VFP 8中都有 Northwind 數據庫)之類的事情了。
不過,要注意的是這個 Cursor 對付的是 Customers 表中所有的記錄。可有時候,你又只想要一個客戶的數據。那麼,CustomerByIDCursor 是 CustomersCursor 的一個子類,它的 SelectCmd 已經被改成 “Select * from customers where customerid = ?pcustomerid”,還有下面增加的 INIT 方法的代碼:
lparameters tcCustomerID
dodefault()
This.AddParmeter('pCustomerID', tcCustomerID)
這段代碼會建立一個叫做 pCustomerID 的參數(它跟在 SelectCmd 中指定的是同一個),並且被設置成傳遞進來的任何值。如果沒有值被傳遞進來的話,那麼使用 GetParameter 方法來爲這個參數返回一個對象,並在調用 GetData 之前設置它的 value 屬性。
OrdersCursor 類類似於 CustomersCursor,只是它返回的是 Orders 表中的所有數據,而 OrdersForCustomerCursor 是它的一個子類,用於返回一個指定客戶的所有訂單。
要測試一下的話,請運行 TestCustomersCursor.prg,它會從 SQL Server 版本的 Northwind 數據庫中 Customers 表的一個客戶,然後做到 Access 版本的 Northwind 數據庫所做的同樣的事情。這個示例演示了怎樣不爲類指定連接信息,這個類自己就能靈活的完成任務,因此,它的可重用性是很強的。
示例:表單
**********
現在我們已經有了一些可重用類,讓我們來用用它們。首先,我們來建立 SFDataEnvironment 的一個子類 CustomersAndOrdersDataEnvironment (哈哈,名字可有夠長的,D.H牌冰糖葫蘆!),它包含着一個 CustomerByIDCursor 類和一個 OrdersForCustomerCursor 類。由於我們希望在打開表之前設置連接信息,因此把它的 AutoOpenTables 屬性設置爲了 .F.,而且把前面兩個 CursorAdapter 的 UseDEDataSource 屬性都設置爲了 .T.。現在,這個數據環境類已經可以被用來在某個表單中顯示關於一個指定客戶的信息以及他的訂單了。
讓我們來建立這麼一個表單。附件中的 CustomerOrders.scx 表單的 DEClass 和 DEClassLibrary 屬性已經被設置爲了CustomersAndOrdersDataEnvironment 和 NorthwindDataClasses.vcx,這樣就用上了我們的可重用數據環境類。這個表單的 Load 方法裏面的代碼有點多,不過這是因爲它要支持 ADO、ODBC、以及 XML 數據源,並且它還要建立自己的連接,所以大多數代碼都是處理這些問題的。如果它只支持一種數據源的話,比如只用 ODBC,再比如由另一個對象來管理連接(實際的應用程序開發中常常就是這樣的),代碼就會簡單多了:
with This.CustomersAndOrdersDataEnvironment
* 獲得連接
lnHandle = oApp.oConnectionMgr.GetConnectionHandle()
.SetConnection(lnHandle)
* 指定從 CustomerID 文本框中取得 cursor 參數的值
loParameter = ;
.CustomerByIDCursor.GetParameter('pCustomerID')
loParameter.value = '=Thisform.txtCustomerID.value'
loParameter = ;
.OrdersForCustomerCursor.GetParameter('pCustomerID')
loParameter.value = '=Thisform.txtCustomerID.value'
* 建立一個空的 cursor,如果失敗的話則顯示錯誤信息
if not .GetData(.T.)
messagebox(.cErrorMessage)
return .F.
endif not .GetData(.T.)
endwith
這段代碼用上了那兩個 CursorAdapter 對象的 GetParameter 方法來把 pCustomerID 參數設置爲表單上一個文本框中的值。注意在值裏面用到的'=',它表示在你需要 value 屬性的時候再去運算它的值,所以我們實際上有了一個動態的參數(這樣就順應了當用戶在文本框中輸入了新的值以後要將改動反應到參數中去的需要)。調用 GetData 方法是爲了建立一個空的 Cursor,這樣才能安頓那些數據綁定的控件。
txtCustomerID 文本框沒有綁定什麼數據。它的 Valid 方法先調用數據環境的 Requery 方法,然後再調用表單的 Refresh 方法。這樣,當用戶輸入一個新的客戶ID的時候,就能夠 Requery 那個 Cursor,接着表單上其它控件也會被刷新。表單上的其它文本框被綁定到由 CustomersByIDCursor 對象建立的 Customers cursor 的字段中。那個 Grid 被綁定到由 OrdersForCustomerCursor 對象建立的 Orders Cursor。
運行這個表單,並輸入一個 Customer ID 爲“ALFKI”(見圖1)。當你按下 Tab 鍵跳出這個文本框的時候,你會看到該客戶的地址信息以及他的訂單就出現了。試着改動一些這個客戶的數據或者訂單數據,然後關閉表單再打開,再輸入一次“ALFKI”,你會看到你沒做什麼工作這些改動就都已經被寫到後臺數據庫中了。

此主題相關圖片如下:
圖1、
酷吧,嗯?跟建立一個基於本地表或者視圖的表單相比,並沒多多少工作。更棒的是:試試把定義在 Load 方法中的 ccDATASOURCETYPE 常量改變爲 “ADO”或者“XML”,然後這個表單無論是看起來還是實際工作起來都跟沒改過之前一摸一樣(如果你想用 XML,你需要象上個月的文章中所說的那樣爲 Northwind 數據庫設置一個 SQLXML 虛擬目錄,並把本月附件中的 XML 模板文件拷貝到那個目錄裏)。
示例:報表
**********
我們來試試報表。這裏最大的問題是:與表單不同,我們既不能告訴報表去使用一個數據環境子類,也不能拖放一個 CursorAdapter 子類到數據環境中去。所以我們不得不向報表放入一些代碼以將 CursorAdapter 添加到數據環境。儘管從邏輯上來看應該把這些代碼放到報表數據環境的 BeforeOpernTables 事件中去,不過事實上這樣做卻是行不通的,因爲——由於某些我不能理解的原因—— BeforeOpenTables 事件只會在你預覽報表的每一頁的時候纔會觸發。所以,我們只好把代碼放在 Init 方法裏。因爲演示的需要,報表 CustomerOrders.frx 跟表單 CustomerOrders.scx 一樣,要比實際開發的情況下會用到的代碼更復雜一些。如果沒有這些演示的需求的話,實際上可以簡化到下面這樣:
with This
set safety off
* 獲得連接
.DataSource = oApp.oConnectionMgr.GetConnectionHandle()
* 建立客戶和訂單的 CursorAdapter 對象
.NewObject('CustomersCursor', 'CustomersCursor', ;
'NorthwindDataClasses')
.CustomersCursor.AddTag('CustomerID', 'CustomerID')
.CustomersCursor.UseDEDataSource = .T.
.NewObject('OrdersCursor', 'OrdersCursor', ;
'NorthwindDataClasses')
.OrdersCursor.AddTag('CustomerID', 'CustomerID')
.OrdersCursor.UseDEDataSource = .T.
* 取得數據,如果失敗,則顯示錯誤信息
if not .CustomersCursor.GetData()
messagebox(.CustomersCursor.cErrorMessage)
return .F.
endif not .CustomersCursor.GetData()
if not .OrdersCursor.GetData()
messagebox(.OrdersCursor.cErrorMessage)
return .F.
endif not .OrdersCursor.GetData()
* 從 Customers 設置一個與 Orders 的關係
set relation to CustomerID into Customers
endwith
這裏的代碼比表單示例的要多一些,這是因爲我們不能使用自定義的數據環境類導致不得不自己手動編碼來代替。
現在,我們怎樣才能儘可能簡單的就把那些字段放到報表上去呢?由於 CursorAdapter 對象是我們用代碼在運行時才添加到數據環境中去的,在設計時就沒辦法享受到拖放字段到報表上的方便了。這裏有個小技巧:建立一個會建立這些 Cursor 的 PRG文件,並讓這些 Cursor 處於有效範圍內(可以採用掛起 PRG 的運行或者把 CursorAdapter 對象聲明成 Public 的辦法),然後使用快速報表功能來把那些字段放到報表上,這樣報表控件的大小也設置好了。
圖2展示了當你預覽報表的時候該報表的情況。如果結合表單一起使用的話,你可以試試改動表單數據環境中的 #DEFINE 語句來換用其它類型的數據源。
總結
*****
我們對新的 CursorAdapter 基礎類的研究就到這裏了。我個人對 CursorAdapter 的出現感到非常的興奮,並計劃給我的應用程序框架中的數據處理部件升升級以更充分的利用它的優點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章