|
前言 譯者言 這篇文章來自著名的VFUG網(wǎng)站1999年10月的新聞郵件。VFUG是Virtual FoxPro User Group的縮寫,主頁地址是:http://www.,他們每個月發(fā)行一份新聞郵件,內(nèi)容包括VFP專題文章、技巧、網(wǎng)絡資源以及新聞等等,當然,是全英文的。你可以自由加入VFUG,以訂閱新聞郵件。 自從99年11月收到這份新聞郵件開始,我就對這篇文章產(chǎn)生了興趣。我的E語還湊合,再靠著金山詞霸幫忙,慢慢的讀完了它。在閱讀過程中,我不止一次的感嘆:這篇文章真是太好了,一定要把她介紹給中國的VFP愛好者們。 這 篇文章里提供了一種完全異類的思路和方法,相信會對讀者大有啟發(fā)的。國內(nèi)的VFP研究學習總是離不開程序員指南和幫助文件那點內(nèi)容,(最可恨的是,國內(nèi) VFP書籍很多都是完全或大部分抄襲程序員指南的?。。。┒鴼W美的文章里卻有很多關于編程原理性的東西,很值得我們學習借鑒。 由于個人水平所限,翻譯起來比較慢,也未必準確,請大家原諒。對原文感興趣可以去VFUG網(wǎng)站加盟,或給我發(fā)封Email,我會給你寄來的。
第一部分:對象和記錄(原文如此) 為紀錄使用對象 過去(面對對象編成出現(xiàn)以前的時候)許多人都知道SCATTER 和 GATHER MEMVAR命令的使用。VFP現(xiàn)在給了我們通過對象來做同樣的事情的方法,而且更加簡單。 現(xiàn)在假設你有一張客戶表(Customer)只包括“名字”和“姓”兩個字符型字段,你不妨建立一張表試一下。 從命令窗口執(zhí)行 隨便建立一張自由表,包括兩個字段“名字”字符型(30)、“姓”字符型(30)。 瀏覽這張表并隨便輸入幾個紀錄。在瀏覽的時候按下ctrl+Y鍵來增加一個新紀錄并輸入隨便什么人的姓名。 現(xiàn)在新建一個表單,給表單添加一個自定義屬性\'thi\0srec\'。 向數(shù)據(jù)環(huán)境中添加入你的客戶表(Customer)。 在表單的load方法中,選擇客戶表(Customer),并用scatter命令分散空白紀錄到對象。 *************************************************************************** 【譯者注:scatter命令的用法: 這有點像SCATTER TO MEMVAR命令只不過使用了表單的自定義屬性。作為面對對象方法的先進之處在于在內(nèi)存中可以同時有多個紀錄。另外,這個方法建立的對象可以被傳遞到另外的方法,比如表單,另外,象我們將會看到的那樣,還有com 對象。 下 一步我們需要得到紀錄并顯示它們。添加一個列表框list1到表單上并使用生成器來選擇客戶表(Customer)作為數(shù)據(jù)源。如果碰到超過一個人同姓的 情況,你可以把兩個字段都選中。同樣,你也可以查看和修改紀錄,添加兩個文本框Textbox1、Textbox2并把它們綁定到我們的紀錄對象。 把Textbox1文本框的controlsource屬性設置為:thisform.thisrec.firstname 記住thisrec是一個儲存我們的紀錄的對象,在程序開始時我們把它清空了。 【譯者注:原來屬性還可以這么用!屬性變成了對象,竟然還會有自己的屬性!】 在列表框list1的Click方法中加入以下代碼以向對象添入數(shù)據(jù): List1 Click 方法: ***************************************************** That is it!現(xiàn)在你可以用建立的對象來對紀錄進行更改、比較等無論什么操作,但等你干完了以后,必須象下面一樣把修改送回表中: 添加一個命令按鈕Command1,設置它的Caption屬性為:\"Save\"、Enabled屬性為False。 回到list1的Click方法,添加下面一行代碼: Thisform.Command1.enabled = .T. 現(xiàn)在list1的Click方法代碼應該是這樣的: ****************************************************** 在Command1的Click方法中加入以下代碼: Command1.Click方法: ******************************************************* 我想你已經(jīng)理解了,可以添加你自己的\"添加新紀錄\"按鈕或\"刪除紀錄\"的按鈕。 下一步,我們將看一下怎樣傳遞這些對象到別的方法。
第二部分:對象和記錄 傳遞對象到其他的方法和表單: 如 果你已經(jīng)照著第一步那么做了,那么你已經(jīng)有了一個收集和更改紀錄的表單,它通過使用一個放置紀錄的對象來做到這一點?,F(xiàn)在我們來看看怎樣傳遞對象到另一個 方法。盡管當對象對任何表單方法是可視的時,這種辦法看起來沒什么用。不過我們先來試著做一下,我們將給表單添加一個交換名和姓的自定義方法。 給表單添加一個名叫Swapfields的自定義方法,并輸入以下的代碼: 表單的 Swapfields 方法 ******************************************************* 添\0加一個命令按鈕command2,設置它的Caption屬性為Swap names、Enabled屬性為False。 Comand2.Click方法 ******************************************************* [譯者注:注意到了嗎?向swapfields方法傳遞的是從屬性變成對象的thisrec!玩了那么久的VFP,只聽說過傳遞變量、數(shù)組、字符串的,沒想到竟然還能傳遞對象!真是開了眼界了。] 在list1控件的Click方法代碼中加入下面一行: thisform.command2.enabled = .T. List1的Click方法代碼現(xiàn)在看起來應該是這樣的: ****************************************************** 現(xiàn) 在讓我們看看發(fā)生了什么:姓和名被交換了一圈,并且結果可以被儲存。象我說過的那樣,對這個簡單的示例程序來說,這看起來沒什么用,因為我們能夠直接對 Swapfields方法中使用的對象進行操作。但是如果你要同時操作多個紀錄,也許你在比較紀錄并且你的表單上有兩個或多個紀錄對象。 那么用這種方法,這個表單自定義方法對任何一個對象而言都是相同的,并且你的命令按鈕也可以對任何一個相關聯(lián)的對象進行操作。只要字段相同,Swapfields方法始終不會在乎。你可以更進一步建立一個自定義方法同時對兩個紀錄對象操作并同時發(fā)送結果。 下面我們來看看怎樣發(fā)送對象到另一個表單。
第三部分:對象和其他表單 傳遞對象到其他的表單 這 里有更多的技巧。如果你看看我們的文本框對象,你將看到它們的ControlSource屬性都被從我們的\'thisrec\'對象賦值。因為我們在文 本框初始化之前已經(jīng)讓我們的對象分散(Scattered)了數(shù)據(jù),所以這樣做是正確的。這里我們在表單的Load方法中用Scatter name thisform.thisrec收集了數(shù)據(jù),當我們調(diào)用另一個表單時,在新表單的文本框有控制源(ControlSource)以前我們不能對這些文本 框做任何事。另外會發(fā)生一個錯誤并且新表單不能初始化。有許多辦法可以解決這種情況,但你選擇怎么做必須依情況而定。一種辦法是讓文本框的 ControlSource為空。另外也可以把它們的值指向一個虛假的屬性或者把它們當成一個直到表單的紀錄對象已經(jīng)被生成時才添加的一個容器對象的一部 分。下面使用了第一種方法。 要開始下一步,建立一個新的表單,表單上有兩個文本框和一個命令按鈕,設置命令按鈕的caption屬性為\'Swap Fields\'。建立一個表單屬性\'recobject\',它將被用來保存從初始表單傳遞過來的對象。最后,把表單設置為模式表單。 表單的Init事件中: ********************************************************* 【譯 者注:有沒有搞錯?。窟@個方法擺明了是要從上一個表單接受thisrec對象了,可thisrec是對象呀!它被自己所在的表單的自定義方法使用還可以理 解,可要說把一個對象傳遞到另一個表單的屬性中,然后那個屬性也變成了對象了,這真是聞所未聞。這是面對對象編程,還是艾博拉病毒感染?:-))】 在Init事件中,我們傳遞了紀錄對象,現(xiàn)在它是這個表單的一個表單屬性并且設置了文本框的數(shù)據(jù)源ControlSource。因為現(xiàn)在表單尚未激活,在這里不需要刷新表單。 下面是命令按鈕的代碼: Command1.Click ********************************************************* 看起來這個表單(被調(diào)用表單)有自己的紀錄對象。而實際上這是和調(diào)用它的表單使用的是同一個對象。我們現(xiàn)在來證明一下。編輯調(diào)用它的表單的Swap fields命令按鈕的Click事件代碼: Command2.Click ******************************************************** 現(xiàn)在當表單運行時,如果按下了Swap fields命令按鈕,將彈出新的表單,在這 下面我們快速的看一下assign方法。
第四部分:對象和 _Assign 對象和assign方法 VFP6里新增了Access 和 Assign方法。當一個屬性被查詢時access方法被觸發(fā)。在這里我只做一個簡單的注釋,不會談很多的關于一個屬性被查詢或更改時存在的\"this_access\"方法的內(nèi)容。 查看幫助以獲得更多的信息。目前我感興趣的是Assign方法。原因是這將有益于幫助我們掌握這一系列描述一個VFP COM dll觸發(fā)的事件的文章?,F(xiàn)在,讓我們看一個例子:一個Assign方法得到一個從Swap fields表單中返回的錯誤。 因 為在這個例子中需要一個對象,第一件事情是建立一個用戶自定義類,從菜單上選擇“新建”——“類”,建立一個名叫\(zhòng)"callclass\"的自定義類并 保存在cclass.vcx類庫中。第二步,給這個自定義類添加一個名叫callerror的自定義屬性,并選中Assign方法復選框。在 callerror_assign方法中輸入以下代碼。 Callerror_assign方法: **************************************************** 關閉類設計器。 現(xiàn)在給主調(diào)用表單添加一個新的屬性SwapError。在表單的load方法中設置類庫。 Form Load **************************************************** 【譯者注:作者在這里有一個疏忽,類庫的文件名是cclass.vcx,因此應該把上面的代碼中的CCALL都改為CCLASS】 在表單的init事件中添加錯誤對象(error object). 表單 Init事件: ***************************************************** 現(xiàn)在給command2.click方法中的do form命令添加一個參數(shù) Command2.Click **************************************************** 現(xiàn)在編輯被調(diào)用表單。添加一個用于接受新錯誤對象的屬性HasError。在init事件中,添加Lparameters語句給表單的一個屬性賦值一個錯誤對象參數(shù)。 Form Init ********************************************************* 下面我們所有在這里做的只是如果姓為空或長度小于2的情況則給出一個返回路徑,以避免給姓賦值一個單字母數(shù)據(jù)。象下面一樣編輯Command1.click方法: Command1 Click方法: ********************************************************* 就 是這樣。現(xiàn)在修改一個紀錄的數(shù)據(jù),使它的姓為單字母數(shù)據(jù),然后試著交換(swap)它。被調(diào)用的表單將改變HasError對象的callerror屬性 (它有一個可被觸發(fā)的Assign方法)。雖然對話框(messagebox)看起來出自被調(diào)用表單,事實上是主調(diào)用表單的對象觸發(fā)它的。以后當我們模擬 一個來自VFP dll的事件時,記住這一點是重要的。 第五部分:VFP6 COM dll 建立一個COM dll 繼續(xù)同樣的交換字段的主題,這個部分是在VFP6中建立一 個COM dll。第一步是建立一個新的項目swapcom,并且給它添加一個自定義類swapcom保存在cswapcom.vcx類庫中。編輯這個類,選擇類/ 類信息菜單,從彈出的窗口中選中“OLE 公共”復選框。下一步給類添加一個自定義方法swapfields,代碼如下: swapfields 方法: **************************************************** 那 就是OLE COM準備做的。關閉類,然后項目窗口中選擇“連編”。連編為一個單線程或多線程COM服務器。【譯者注:在“連編選項”的“操作”單選按鈕組中選擇“連 編 COM dll”】產(chǎn)生的dll現(xiàn)在準備注冊并可以被任何能調(diào)用COM 服務器的應用程序使用了。當然,你最好需要一個在COM 服務器中的錯誤檢查,不過,那不是這篇文章的內(nèi)容。 我認為在這里錯誤檢查比普通的程序更重要。如果你正在與ASP的COM dll一起使用你自己的COM dll,最多只是停止反應或鎖定服務器。就是那樣——沒有更多的損失。 要 看看發(fā)生了什么,在連編了動態(tài)連接庫后,在命令窗口中輸入MyCom = createobject(\"swapcom.swapcom\"),接著輸入MyCom.swapfields(),回車,將會發(fā)生一個錯誤。下一步 是打開我們的主調(diào)用窗體,然后編輯代碼,通過調(diào)用我們的新的COM dll來代替調(diào)用第二個窗體。 這很簡單。注銷(REM)在command2.click方法中的do form語句,使代碼看起來是下面這樣: ****************************************************** 運行表單,它將字段交換一圈,除非我們的例外發(fā)生或名字少于兩個字符。下一步,我們將結束給COM dll添加錯誤事件。 第六部分: 總 結 使用assign從VFP COM dll中得到返回 這 一系列文章的目的,是顯示一個VFP COM dll能夠給調(diào)用它的表單一個返回信息。作為一個COM部件,VFP看來沒有事件。這意味著Microsoft站點上的VFPCOM.dll能夠被大多數(shù) COM應用程序使用,除了VFP COM!!!當COM已經(jīng)做完了自己的工作而整個程序需要一個返回來作為終點時,我認為這簡直是個恥辱。在我的方案里,我希望模塊化我的程序并且需要得到 返回。在文章的最后部分(第七部分)我將表露希望得到返回的真實原因。在這里,先講錯誤返回。我們已經(jīng)有了錯誤的代碼,所以我們所需要做的是把輸出錯誤的 do form版本改編成我們新的COM版本。 打開調(diào)用表單,并且編輯Command2.click事件代碼來包含錯誤對象。 Command2 click ****************************************************** 打開COM dll項目并且打開自定義類swapcom,添加LParameters參數(shù)和if/endif語句的其它部分,使它看起來象下面這樣: **************************************************** 重新連編COM dll并且運行表單。就是那樣。我還添加了一個最后部分以說明我想做的一件事。 第七部分 結 尾 這是關于Assign方法和COM dll事件傳奇的最后部分。在這里,我將對實時工作的事件以及程序并不是象看起來的那樣已經(jīng)執(zhí)行完畢的加個證明。這些仍然很簡單但有助于思考。 首 先建立一個新的COM dll。建立一個新的項目Countercom,添加一個自定義類“counter\"。打開類菜單下的“類信息”菜單項,選中“OLE 公共”復選框。給自定義類添加一個自定義屬性“countclass”,它將被用來保存從調(diào)用表單傳遞過來的對象\0;給自定義類添加一個自定義方法 “countdown”,在方法里,輸入下面的代碼: countdown method ***************************************************************** 隨便連編這個COM dll為單線程或多線程dll,關閉這個項目。 下 一步,是用來掌握已賦值的屬性(assigned property)的類,在“countclass”類庫中建立一個自定義類“Countreq”。在那個類里建立一個屬性“countdown”同時選 中Assign復選框。現(xiàn)在你會有一個名叫countdown_assign的自定義方法,向該方法中輸入以下代碼: countdown_assign ****************************************************************** 最后是主調(diào)用表單。建立一個名叫\(zhòng)"Countcomfrm\"的新表單,表單上有一個命令按鈕。給表單添加下面的代碼段: Form Load ****************************************************************** Form Init ****************************************************************** Form Unload ****************************************************************** 還有就是命令按鈕的代碼 Command1 Click ******************************************************************thisform.currentx = 10 thisform.currenty = 10 thisform.print(\"Starting\") if !type(\"countobject\") == \"O\" countobject = createobject(\"countercom.counter\") endif countobject.countdown(thisform.countcom) thisform.currentx = 10 thisform.currenty = 10 thisform.print(\"Complete\") |
|
|