VFP 愛用者社區 首頁 VFP 愛用者社區
本討論區為 Visual Foxpro 愛用者經驗交流的地方, 請多多利用"搜尋"的功能, 先查看看有無前例可循, 如果還有不懂的再發問. 部份主題有附加檔案, 須先註冊成為社區居民才可以下載.
 
 常見問題常見問題   搜尋搜尋   會員列表會員列表   會員群組會員群組   會員註冊會員註冊 
 個人資料個人資料   登入檢查您的私人訊息登入檢查您的私人訊息   登入登入

[轉貼] Visual FoxPro9.0 中擴展報表的系統功能

 
發表新主題   回覆主題    VFP 愛用者社區 首頁 -> VFP 討論區
上一篇主題 :: 下一篇主題  
發表人 內容
garfield
Site Admin


註冊時間: 2003-01-30
文章: 2157


第 1 樓

發表發表於: 星期三 四月 13, 2005 4:33 pm    文章主題: [轉貼] Visual FoxPro9.0 中擴展報表的系統功能 引言回覆

資料來源: http://www.pcom.cn/f/2004/12/8/1102398898.html
英文原版: http://www.code-magazine.com/focus/article.aspx?quickid=0404032&page=1

微軟在2004年歲末發佈最新版Visual Foxpro 9.0,這則消息讓我們回想起曾經風光無限的Foxpro,現在它已經被Java、Delphi、VB等眾多明星遮掩了光芒,有人認為如果不是誕生在豪門微軟,它肯定已經消失。但瞭解Visual Foxpro的都知道,Visual Foxpro的卓越性能是無法代替的。新版Visual Foxpro 9.0在Foxpro社區引起了轟動,新版中將包含一個更快的本地數據引擎,支持更多的數據類型,SQL語句執行中有更大的一致性,一個完全重新設計的可擴展報表編寫器,以及一系列效率和功能增強特性。

在Visual FoxPro 9.0的報表系統出現了難以置信的改進。在本文中,我只討論幾個新增功能中的一個——擴展運行時報表引擎(runtime reporting engine)的能力。

Visual FoxPro小組在處理運行時改進工作時一直緊記著幾個目標,包括:
處理打印和預覽之外的多種報表輸出
使用GDI+進行報表輸出。這會帶來很多顯著的改進,例如精確地顯示、圖像和字體的平滑調整,以及一些附加的能力(例如文本旋轉)

提供一個更加靈活和可擴展的報表系統

你可以同時訪問舊的和新的報表引擎,因此你可以根據需要選擇在哪種引擎下運行報表。但是一旦你看到新的報表引擎的優點,你就再也不希望使用舊式報表了。

報表系統的架構

Visual FoxPro 9以前版本中的報表系統類似於單片電路:它處理所有細節信息,只有少量的例外(用戶定義的函數、報表帶條的OnEntry和OnExit表達式等等),在報表運行的時候,你是不能與它交互操作的。

新的報表引擎把報表的功能分成了兩部分:現在的報表引擎只處理數據和對像定位;一種新對像(報表監聽器)處理顯示和輸出的事務。由於報表監聽器是類(class),因此我們現在可以使用以前夢寐以求的方式與報表進程交互操作。

新的報表語法

Visual FoxPro 9支持使用舊的報表引擎運行報表;你可以像以前一樣使用REPORT命令(儘管你可以使用新命令重載REPORT的行為)。為了得到新式的報表行為,必須使用REPORT命令的OBJECT子句。OBJECT子句支持兩種使用方法:指定報表監聽器和指定報表樣式。微軟把它歸納為對像輔助(object-assisted)報表。

報表監聽器是提供新式報表行為的對象。報表監聽器是基於Visual FoxPro 9的新的基礎類ReportListener的。為了讓Visual FoxPro 9使用報表指定的監聽器,需要實例化監聽器類,並在REPORT命令的OBJECT子句中指出該對象的名稱。下面是一個例子:


loListener = createobject(『MyReportListener『)
report form MyReport object loListener

如果不希望手動實例化監聽器,可以通過指定報表類型讓Visual FoxPro自動為你完成實例化過程,例如:


report form MyReport object type 1

已經定義好的類型有:

0——從打印機輸出
1——預覽
2——某個時刻的頁面信息模式,但不會輸出到打印機
3——所有頁面模式,但不會調用預覽窗口
4——XML輸出
5——HTML輸出
當然我們還可以使用其它的用戶自定義類型。

使用這種方式運行報表的時候,將調用新的_REPORTOUTPUT系統變量(默認情況下它位於Visual FoxPro主目錄的ReportOutput.APP中)中指定的應用程序來決定指定該類型使用哪種監聽器類來實例化。它是通過查看APP中內建的監聽器註冊表(儘管你可以指定它使用一個外部表)中的監聽器類型來實現的。如果它找到了需要的類,它就實例化該類並傳遞該監聽器對象的一個引用到報表引擎中。因此,在REPORT命令中使用OBJECT TYPE 某種類型的效果與下面的代碼的效果是相同的:


loListener = .NULL.
do (_ReportOutput) with SomeType, loListener
report form MyReport object loListener

報表監聽器

在報表運行的過程中,伴隨著報表事件的發生,Visual FoxPro把這些事件暴露給基於ReportListener基類的對象。Visual FoxPro幫助文件記錄了ReportListener的屬性、事件和方法(PEMs),但是在本文中我只討論其中最有用的一些。

表1列舉了ReportListener類的最常使用的一些屬性。

表1:ReportListener類的一些有用屬性


屬性
描述

CurrentDataSession
報表數據的數據對話ID

FRXDataSession
FRX游標的數據對話ID

GDIPlusGraphics
用於顯示的GDI+繪圖對像句柄

ListenerType
監聽器生成的報表輸出類型。默認值是-1(無輸出),你應該把它改成符合需要的值。它的值與REPORT 命令的OBJECT TYPE 子句中規定的值是相同的。

OutputPageCount
被顯示的頁面的數量

QuietMode
如果它的值為.T. (默認值是 .F.) 就支持進度信息


表2顯示了經常使用的ReportListener的事件和方法。

表2:ReportListener的一些有用的事件和方法


事件/方法
描述
LoadReport
在FRX被載入和打印機假脫機操作被打開前調用
UnloadReport
在報表運行之後調用
BeforeReport
在FRX被載入但是報表運行前調用
AfterReport
報表運行後調用
BeforeBand
處理某個報表條帶前調用
AfterBand
處理某個報表條帶後調用
EvaluateContents
顯示某個字段前調用
Render
顯示對象的時候調用
OutputPage
向特定的設備輸出指定顯示的頁面

ReportListener子類

Visual FoxPro主目錄中的FFC(FoxPro基礎類)子目錄包含了_ReportListener.VCX文件,該文件包含了ReportListener的一些子類,而這些子類的功能比基類更多。這些子類中最有用的是_ReportListener。

_ReportListener最重要的特性之一是對繼承(successors)的支持。當你運行報表的時候,你可以希望使用多個報表監聽器。例如,如果你希望預覽某個報表,同時輸出為HTML,就會涉及到一個以上的報表監聽器。_ReportListener通過提供Successor(它包含一個引用另一個監聽器的對象)屬性允許我們構建監聽器鏈。

例如,假設ListenerA和ListenerB都是_ReportListener的子類,它們各自執行某些事務,並且你希望在某個報表上同時使用這兩個監聽器。下面是把這些監聽器鏈接起來的代碼:


loListener = createobject(『ListenerA『)
loListener.Successor = createobject(『ListenerB『)
report form MyReport object loListener

報表引擎只與REPORT或LABEL命令中指定的監聽器(稱為lead listener,頭監聽器)通訊。當報表引擎引發報表事件的時候,頭監聽器調用它的後繼者的適當方法,而後繼者又調用自己的後繼者的適當方法,這樣一直沿著鏈進行下去。這種架構就是響應鏈,鏈中的任何監聽器都可以決定執行某些操作或者把消息傳遞給鏈中的後繼節點。

_ReportListener的另外一種有趣的能力是鏈接報表。AddReport方法把一個報表添加到定制的ReportFileNames集合中。你給這個方法傳遞報表名、可選參數還有將要使用的報表子句(例如RANGE子句)和另一個監聽器對象的引用。RemoveReports方法從集合中刪除所有的報表。RunReports運行報表;傳遞進去的第一個參數是.T.的時候將在報表運行後從集合中刪除報表,第二個參數為.T.時將忽略AddReport指定的任何監聽器。下面是一個示例,它運行了兩個報表,但是表面看起來好像是一個報表:


loListener = newobject(『_ReportListener『, home() + 『ffc_ReportListener.vcx『)
loListener.ListenerType = 1
loListener.AddReport(『MyReport1.frx『, 『nopageeject『)
loListener.AddReport(『MyReport2.frx『)
loListener.RunReports()

HTML和XML輸出

由於開發小組的設計目標之一是提供更多的報表輸出類型,所以Visual FoxPro 9包含了_ReportListener的兩個子類,叫做HTMLListener和XMLListener,分別用來來提供HTML和XML輸出。這些監聽器都內建在ReportOutput.APP中,但是在_ReportListener.VCX中也可以使用。監聽器類型5指定為HTML輸出、4指定為XML輸出,因此你可以使用下面的命令把輸出指定為HTML:


report form MyReport object type 5

但是如果這樣操作你將無法控制將要建立的文件名和其它一些設置。作為替代,調用ReportOutput.APP可以讓你得到需要的監聽器引用、設置需要的屬性、接著告訴REPORT命令使用該監聽器。

下面的代碼從MyReport報表中建立了一個叫做MyReport.HTML的HTML文件。當你指定為類型5時,ReportOutput.APP使用自己內建的HTMLListener類提供輸出。


loListener = .NULL.
do (_reportoutput) with 5, loListener
loListener.TargetFileName = 『MyReport.html『
loListener.QuietMode = .T.
report form MyReport object loListener

下面的代碼從MyReport報表中建立MyReport.XML文件,只包含了數據。在這種情況下,代碼片斷使用了XMLListener類(類型4)。


loListener = .NULL.
do (_reportoutput) with 4, loListener
loListener.TargetFileName = 『MyReport.xml『
loListener.QuietMode = .T.
loListener.XMLMode = 0
&& 0 = data only, 1 = layout only, 2 = both
report form MyReport object loListener

HTML輸出實際上使用XML監聽器生成XML,接著使用XSLT來生成最終的HTML。

這兩個監聽器類都有一些附加的屬性,你可以使用這些屬性進一步控制輸出。我推薦你查閱一下Visual FoxPro文檔。此外,由於它們是_ReportListener的子類,所以監聽器類支持_ReportListener類的能力,包括鏈接監聽器和運行多個報表。下面是一個同時輸出XML和HTML的示例:




use _samples + 『NorthwindOrders『
loListener1 = .NULL.
do (_reportoutput) with 4, loListener1
loListener1.TargetFileName = 『MyReport.xml『
loListener1.QuietMode = .T.
loListener1.XMLMode = 0
&& 0 = data only, 1 = layout only, 2 = both
loListener2 = .NULL.
do (_reportoutput) with 5, loListener2
loListener2.TargetFileName = 『MyReport.html『
loListener2.QuietMode = .T.
loListener1.Successor = loListener2
report form MyReport object loListener1

建立自己的監聽器

由於報表監聽器是類,所以報表運行的時候,你可以建立子類來改變報表系統的行為。

例如,我一直希望在運行時動態地格式化字段。在某些條件下,我希望字段用紅顏色打印,其它條件下用黑顏色打印。一個字段有時需要加粗而其它時候則不需要。

改變字段在報表中的顯示樣式的關鍵是EvaluateContents方法。這個方法在字段被顯示之前調用每個字段對象,賦予監聽器改變字段樣式的權力。該方法的第一個參數是被處理的字段對象的FRX記錄號,第二個參數是包含屬性和字段對像信息的對象(請查看Visual FoxPro幫助文件中該對像包含的屬性列表)。你可以修改任何屬性來改變報表中字段的樣式。如果你是這樣做的,那麼還需要把該對象的Reload屬性設置為.T.,以通知報表引擎你已經改變了一個或多個屬性。

列表1顯示了定義_ReportListener的一個子類(叫做EffectsListener)的代碼片斷,該子類處理可能應用於報表中的字段的不同效果類型。這些效果通過效果處理對像來應用,而這些對象都存儲在EffectsListener的oEffectsHandlers屬性的集合中。每種效果處理對像處理一種效果。

在報表被處理的時候,監聽器需要確定哪些字段應用了效果。它在EvaluateContents方法中查看每個將要顯示的字段,實現這種功能。EvaluateContents調用SetupEffectsForObject,它調用每個效果處理程序的GetEffect方法來決定是否給該字段應用某種效果。GetEffect查看FRX中的字段記錄的USER備註來指令應用哪種效果。如果該字段需要某種特定的處理程序,該處理程序就被添加到處理該字段的處理程序集合中(因為每個字段可能應用多個效果)。

這意味著在每條記錄的每個字段上都會調用EvaluateContents,可是沒有必要在一個特定字段上進行多次效果檢查(這樣做將導致報表性能下降)。因此,BeforeReport建立了一個數組,它存儲了FRX中記錄的行。如果該數組的第一列為默認值.F.,說明監聽器還沒有檢測將要顯示的字段的效果,因此EvaluateContents做出檢測並把該數組的第一列設置為.T.,這樣FRX就不會再次檢測了。

在檢測某個字段是否應用了效果後,EvaluateContents進入到該字段的效果處理程序集合中,調用每個程序的Execute方法執行必要的操作。

DynamicForeColorEffect就是一個效果處理程序。它用下面的格式查看報表中某個字段的USER備註:


*:EFFECTS FORECOLOR = expression
(你可以從某個對象的屬性對話框中的「其它」選項頁中看到該對象的USER備註。)

列表1中使用的TestDynamicFormatting報表的ORDERDATE字段的USER備註中有下面的代碼片斷指令;它告訴EffectsListener:DynamicForeColorEffect對像應該調整字段的顏色,當裝運時間大於訂單時間10天以上就用紅顏色顯示,否則就用黑顏色顯示:




*:EFFECTS FORECOLOR = iif(SHIPPEDDATE > ORDERDATE +10, rgb(255, 0, 0), rgb(0, 0, 0))





圖1:TestDynamicFormatting報表。列表1中的代碼生成這個報表,它演示了對裝運日期和裝運形式列的動態格式化。

DynamicForeColorEffect的Execute方法通過把傳遞到EvaluateContents中的字段屬性對象的PenRed、PenGreen和PenBlue屬性設置為適當的顏色,並把Reload設置為.T.(告訴報表引擎已經做了一些修改)來改變字段的顏色。

DynamicStyleEffect使用類似的指令來改變字體樣式。此處使用的樣式必須是一個數值:0是正常體、1是粗體、2是斜體、3是粗斜體。TestDynamicFormatting報表中的SHIPVIA字段的USER中有下面的指令,它引起SHIPVIA為3(因為該字段的表達式實際上顯示為Mail)的字段顯示為粗體,否則為正常體。


*:EFFECTS STYLE = iif(SHIPVIA = 3, 1, 0)

DynamicStyleEffect的工作方式與DynamicForeColorEffect類似,只是改變了字段屬性對象的Style屬性。

運行TestDynamicFormatting.PRG將出現圖1所示的輸出結果。

自定義顯示

你不僅可以改變字段的外形——你還幾乎可以在報表監聽器中執行自己需要的任何事務。ReportListener的Render方法負責在報表頁面上繪製每個對象。你可以重載這個方法來實現各式各樣的輸出。

實現自定義顯示的監聽器當然需要使用GDI+函數。GDI+是執行圖像操作和輸出的數百個Windows API函數的集合。

為了更方便使用GDI+函數,Visual FoxPro的FFC目錄中包含了_GDIPlus.VCX。_GDIPlus由新西蘭Cornerstone軟件公司的Walter Nicholls編寫,它由GDI+函數的包裝類組成,使這些函數更易於使用,同時還是面向對象的。Visual FoxPro幫助文件中的「GDI+ API包裝基礎類」主題列舉了這些類,並提供了它們的少量背景信息。這個類庫對於執行GDI+顯示有很大的幫助,因為你在使用它們的時候,不需要知道GDI+的太多相關信息。我也不太瞭解GDI+的很多信息,但是仍然在幾個小時之內建立了接下來要討論的監聽器類。



圖2:設計時的TestColumnChart.FRX樣式

列表2中的代碼來自TestColumnChart.PRG,它運行了圖2中所示的TestColumnChart.FRX報表,建立了圖3所示的輸出。請注意,輸出結果與報表佈局之間有很大的差別,字段和形狀(shape)沒有顯示出來,而繪製示例Northwind數據庫中的Category_Sales_For_1997視圖的內容的條狀圖卻顯示出來了。這部分原因是字段上的Print When子句防止它們被打印出來,但最大的原因在於這個報表使用的監聽器類(ColumnChartListener)把Summary(匯總)報表條帶中的形狀對像更替為列條狀圖。

下面讓我們看看這個監聽器是如何實現這種功能的。

ColumnChartListener的Init方法把aColumnColors數組初始化為報表中的列將會使用到的顏色。請注意,GDI+的顏色與RGB()函數返回的值有一點點不同,因此它使用CreateColor方法來進行必要的轉換。如果你希望使用不同的顏色集,你可以從ColumnChartListener衍生出子類或者實例化ColumnChartListener之後,在數組中存儲另一組顏色集合。請注意,我們只定義了八種顏色,如果報表中的列多於八個,每種顏色可能用於多個帶條。



圖3:列表2中的代碼生成這個報表,它建立了帶狀圖而不是傳統的輸出。

BeforeReport方法實例化一個GPGraphics對像到自定義的oGDIGraphics屬性中。GPGraphics是_GDIPlus.VCX中的一個類。它和其它_GDIPlus類都被用在DrawColumnChart方法中來繪製條狀圖的組件。

GPGraphics需要一個將要顯示的GDI+表面的句柄。幸運的是監聽器已經有這樣一個句柄,存儲在GDIPlusGraphics屬性中。唯一的複雜因素是該句柄在每個頁面上都會改變,因此當標題或頁面頭部帶條被處理的時候,BeforeBand方法(在報表條帶被處理前調用)調用GPGraphics對象的SetHandle方法來賦予它句柄。

在報表被處理的時候,監聽器必須確定圖表中的標籤和值來自於何處。在字段將要被顯示的時候,它在EvaluateContents方法中通過查看每個字段得到這些信息。如果該字段在FRX中的USER備註包含了LABEL(與CategoryName字段中一樣),就表明該字段應該用於條狀圖的標籤。USER備註中的DATA(例子中是CategorySales字段)表明這個字段用作圖表的值。有了前面討論過的EffectListener類之後,根本就不需要多次檢查USER備註,因此在示例中使用了相同的機制——在一個數組屬性中存儲標識以表明某個字段是否被處理過。

如果監聽器仍然沒有檢測某個將顯示的字段的USER備註,EvaluateContents將執行這種檢測,設置數組中的標識以表明該字段是否用作標籤或值,並把數組的第一列設置為.T.,這樣FRX記錄就不會再次檢查了。如果某個字段用作標籤或值,EvaluateContents相應地更新aValues數組。

AdjustObjectSize與EvaluateContents類似,除了它在形狀(shape)上而不是在字段上調用。AdjustObjectSize檢查當前形狀的FRX記錄的USER備註中是否存在COLUMNCHART,如果存在就表明這個形狀應該被條狀圖代替。有了EvaluateContents之後,監聽器只需要檢查一次,因此它也使用了相似的邏輯。

Render方法負責在報表上繪製對象。如果將被繪製的對象是一個被條狀圖代替的形狀,它就調用自定義的DrawColumnChart方法,帶上NODEFAULT防止該形狀被繪製出來。否則,該對像會被正常地繪製出來(請注意,如果沒有DEDEFAULT(),初始的行為是繪製對象,因此這個參數是必要的)。

DrawColumnChart計算出圖表顯示的最大值,這樣它才知道條帶應該多大,接著它從_GDIPlus類中建立一些執行繪製操作的對象。它調用DrawLine方法繪製出圖表的垂直和水平邊界,接著進入aValues數組,使用DrawRectangle繪製出每個值的條帶並使用FillRectangle用適當的顏色填充。DrawColumnChart使用相同的DrawRectangle和FillRectangle方法繪製方框、使用DrawStringA繪製標籤,給圖表添加了一個方框和標籤圖例。

其中一些繪圖屬性來自於自定義屬性中的值,這使得繪製圖表更加靈活。例如,屬性cLegendFontName和nLegendFontSize指定了圖例標籤使用的字體和大小,nLegendBoxSize指定了將要繪製的方框的大小。你可以在代碼列表2開始處看到這些屬性的註釋。

微軟已經揭開了Visual FoxPro報表系統的面紗!通過給ReportListener對像傳遞報表事件,我們可以與這些事件交互作用來執行一些自己希望實現的事務,其範圍從提供各式各樣的輸出類型到動態地改變被顯示的對象。Visual FoxPro團體使用這些新特性會實現什麼樣的效果?難以想像!
[/img]

_________________
利用>>搜尋<<的功能會比問的還要快得到答案.
回頂端
檢視會員個人資料 發送私人訊息 發送電子郵件
jerryclt



註冊時間: 2009-03-10
文章: 334
來自: 佛心來的

第 2 樓

發表發表於: 星期一 七月 08, 2013 10:50 am    文章主題: 引言回覆

恕引: Visual FoxPro 9以前版本中的報表系統類似於單片電路:它處理所有細節信息,只有少量的例外(用戶定義的函數、報表帶條的OnEntry和OnExit表達式等等),在報表運行的時候,你是不能與它交互操作的。

請教上述的限制,
如果在有必要性使用到的時候,
有無方法解決?
回頂端
檢視會員個人資料 發送私人訊息
從之前的文章開始顯示:   
發表新主題   回覆主題    VFP 愛用者社區 首頁 -> VFP 討論區 所有的時間均為 台北時間 (GMT + 8 小時)
1頁(共1頁)

 
前往:  
無法 在這個版面發表文章
無法 在這個版面回覆文章
無法 在這個版面編輯文章
無法 在這個版面刪除文章
無法 在這個版面進行投票
無法 在這個版面附加檔案
無法 在這個版面下載檔案


Powered by phpBB © 2001, 2005 phpBB Group
正體中文語系由 phpbb-tw 維護製作