程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi程序設計.3

Delphi程序設計.3

編輯:Delphi

4.5定義公共體系結構:使用對象庫
Delphi使應用程序的開發變得容易,以前,要花費很大的精力用於建立應用程序的體系結構,但現在可以輕松多了。問題是,很多開發者往往急於寫代碼而很少考慮應用程序的結構,這使得一個項目往往以失敗而告終。

4.5.1考慮應用程序的體系結構
本書不打算專門講述體系結構或面向對象的分析和設計。不過,我們認為這是非常重要的。附錄C"參考讀物"列出了一些關於面向對象的主題。在開始編寫代碼之前最好先閱讀附錄C的內容。
下面列出了一些應當考慮的問題:
1. 體系結構支持代碼重用嗎?
2. 應用程序中的模塊、對象等能夠本地化嗎?
3. 修改體系結構非常容易嗎?
4. 用戶界面和後端可以本地化嗎?
5. 體系結構支持團隊開發嗎?或者說,團隊的成員可以工作於各自的模塊嗎?
上面這幾個問題其實只是開發過程中要考慮的一部分問題。
關於相關內容的書籍很多,我們無意與它們競爭。下面將舉例說明怎樣設計一個數據庫應用程序的通用用戶界面。

4.5.2Delphi固有的體系結構
你可能經常聽到這樣一句話,即作為一個Delphi開發者,沒必要是一個組件編寫者。盡管這句話是正確的,但下面這句話也是正確的:如果你是一個組件編寫者,就一定是一個更優秀的Delphi開發者。
這是因為,組件編寫者清楚地知道對象模式和Delphi應用程序的體系結構,這意味著組件編寫者能夠更好地發揮它們的優勢。你可能已經聽說過,事實上Delphi本身就是用組件編寫的。Delphi本身就是一個運用體系結構的例子。
即使並不想編寫一個組件,但掌握體系結構還是有好處的。應當像熟悉Win32操作系統那樣熟悉VCL和Object Pascal模型。

4.5.3體系結構的例子
為了證明窗體繼承以及對象庫的能力,下面將定義一個通用的應用程序體系結構。重點是代碼重用性、修改的靈活性、一致性和易於團隊開發。
窗體繼承,更准確地說是框架,它們的典型應用是在數據庫應用程序中。窗體應當對數據庫的操作(編輯、添加或浏覽)具有感知能力。窗體還應當包含一些通用控件,例如工具欄和狀態欄,以便對數據庫表進行操作。這些控件隨窗體狀態變化。另外,這些窗體還應當提供事件,以便跟蹤窗體模式的變化。
應用程序的框架應當允許團隊開發,每個成員可以各自工作於應用程序的一部分,而不至於出現重復和覆蓋。
框架分為3個層次,後面將詳細介紹這3個層次。表4-4描述了框架中每個窗體的用途。表4-4框架中的數據庫窗體


4.5.4子窗體TChildForm
TChildForm是那些能夠被單獨打開的模式窗口無模式窗體並能成為其他窗口的子窗口的基類。
TChildForm支持團隊開發,每個成員可以工作於應用程序的一部分。同時,TChildForm也實現了漂亮的用戶界面,用戶可以在應用程序內打開一個窗體,作為一個單獨的實體。清單4-3是TChildForm的源代碼。清單4-3TChildForm的源代碼


上述代碼演示了下列技術:首先是重載,這是對Object Pascal語言的擴展;其次是怎樣使一個窗體成為另一個窗口的子窗口。
1.提供第二個構造器
你可能注意到了,上述代碼中聲明了兩個構造器(constructor)。第一個構造器用於創建一個普通的窗體,它需要傳遞一個參數。第二個構造器需要傳遞兩個參數,它重載了第一個構造器。如果要使窗體成為子窗口,應當使用第二個構造器。其中,AParent參數用於傳遞父窗口。注意,這裡用了reintroduce指示符,這樣編譯器就不會發出警告了。
第一個構造器只是簡單地把FAsChild變量設為False,以保證創建的是一個普通的窗體。第二個構造器把這個變量設為True,並且把FTempParent設為AParent參數的值,這個值將在Loaded()方法中作為父窗口。
2.使一個窗體成為子窗口
要使一個窗體成為子窗口,有幾件事情需要做。首先,要確保窗體的屬性已經正確設置,正如在TChildForm.Loaded()中看到的那樣。清單4-3的代碼能保證窗體變成一個子窗口而不是一個對話框,這是通過把邊框隱去來實現的。如果這個窗體只用做子窗口,可以在設計時設置這些屬性。如果這個窗體有可能要用作一個普通的窗體,應在FAsChild變量設為True的情況下才設置這些屬性。
還要覆蓋CreateParams(),以告訴Windows把窗體作為子窗口。要實現這一點,需要把Params.Style屬性設為WS_CHILD風格。
TChildForm並不只限於數據庫應用程序。事實上,可以把它用在任何需要把窗體作為子窗口的場合。

4.5.5數據庫基礎模式窗體TDBModeForm
TDBModeForm是從TChildForm繼承下來的。它能夠感知數據庫的狀態(浏覽、插入、編輯)。TDBModeForm還提供了一個事件,以跟蹤數據庫狀態的變化。
清單4-4列出了TDBModeForm的源代碼。清單4-4TDBModeForm

TDBModeForm的實現比較簡單。盡管這裡使用了一些目前還沒有介紹的技術,但你應當能看出它的作用。首先,這裡聲明了一個枚舉類型TFormMode,用於表示窗體的狀態。其次,TDBModeForm提供了一個FormMode屬性以及它的讀寫方法。關於屬性和讀寫方法將在第21章"編寫自定義組件"中詳細介紹。

4.5.6數據庫導航/狀態窗體TDBNavstatForm
TDBNavstatForm具有框架的許多功能。TDBNavstatForm中包含了一些用於數據庫應用程序的通用組件。特別是,它包含一個導航欄和狀態欄,能夠隨數據庫的狀態而發生變化。例如,當數據庫處於fsBrowse狀態,導航欄上的Accept按鈕和Cancel按鈕就被禁止。當用戶使數據庫進入fsInsert狀態或fsEdit狀態,這兩個按鈕將生效。狀態欄上將顯示數據庫的狀態。
下面的清單4-5列出了TDBNavstatForm的源代碼。注意,這裡去掉了組件的列表。當打開這個范例項目時會看到這些列表。
這裡主要處理了一些TToolButton組件的事件,用於設置窗體的當前狀態。這實際上是調用覆蓋SetFormMode(),再由SetFormMode()調用SetButtons()和SetStatusBar()實現的。SetButtons()能夠根據窗體的狀態來決定按鈕的可用或不可用。清單4-5TDBNavstatForm

 

 

你可能注意到了,上面的代碼中有兩個過程用於修改TToolBar組件和TStatusBar組件的父窗體。當窗體作為子窗口打開時,應當把TToolBar組件和TStatusBar組件的父窗體設為主窗體。當運行隨書附帶光盤上\FormFramework目錄中的項目時,就會知道這樣做的意義。
正如前面提到的那樣,TDBNavStatForm既可以是一個獨立的窗體,也可以是一個子窗口。下面的代碼演示了怎樣把TDBNavstatForm作為一個獨立的窗體調用:

下面的代碼演示了怎樣把TDBNavStatForm作為子窗口調用:

上面這個過程不僅把TDBNavStatForm作為pnlParent(TPanel組件)的子窗口,同時還使主窗體成為TToolBar組件和TStatusBar組件的父窗體。另外,TMainForm.mmMainMenu.Merge()這一行的作用是把TDBNavstatForm實例上的菜單合並到主窗體的菜單中。當然,當釋放TDBNavStatForm的實例時,必須這樣調用TMainForm.mmMainMenu.UnMerge():

看一看隨書光盤中的范例。圖4-l顯示了TDBNavStatForm作為一個獨立的窗體和作為一個子窗口的情況。實際上,這裡可以用TImage組件來代替子窗口。
以後,我們將把這個框架擴展為功能齊全的數據庫應用程序。圖4-1TDBNavstatForm作為一個獨立的窗體和作為一個子窗口


4.5.7使用框架進行應用程序結構設計
Delphi5提供框架功能,可以創建能被嵌入到其他窗體中的組件容器。這一點和我們用TChildForm進行的演示類似。然而,框架允許在設計時使用組件容器,並且可以把這些組件容器加到組件板中,以便將來重用。清單4-6演示了框架的功能。清單4-6框架演示

在清單4-6的代碼中,我們顯示了一個主窗體,它包含兩個由獨立的面板構成的長方塊。右邊的面板用於包容框架。我們已經定義了兩個獨立的框架。在private段,FFrame被定義為TFrame類型。由於兩個框架都是直接來自TFrame,所以FFrame能夠直接訪問它們。位於主窗體上的兩個按鈕,各自創建一個不同的TFrame並且將其賦給FFrame。這和TChildForm的效果相同。

4.6一些項目管理的功能
下面將介紹一些項目管理的功能,這對許多使用Delphi5的開發者是有幫助的。

4.6.1在項目中添加資源
前面講過.res文件是應用程序的資源文件,以及什麼是Windows資源。要在項目中添加資源,可以創建一個單獨的.res來存儲要加到應用程序中的位圖、圖標、光標等資源。
必須使用專門的資源編輯器來創建.res文件。創建了.res後,只要在項目文件中加上下面這行語句,就能使資源鏈接到應用程序中:

上面這行語句可以緊接在下面這行語句的後面。下面這行語句的作用是,把一個與項目文件同名的資源文件鏈接到應用程序中:

如果已經這樣做了,這時可以通過TBitmap.LoadFromResourceName()或TBitmap.LoadFromReourceID()來調入資源文件中的資源。清單4-7演示了怎樣從資源文件中調入一個位圖、圖標和光標。注意,這裡用到了一些WindowsAPI函數,例如LoadIcon()和LoadCursor(),可以在WindowsAPI的幫助中找到它們的說明。
注意:WindowsAPI中有一個LoadBitmap()函數,它可以調入一個位圖,但它不能返回調色板,也就是說,它無法調入256色的位圖。因此,建議使用TBitmap.LoadFromResourceName()或TBitmap.LoadFromResourceID()。清單4-7從資源文件中調入資源的例子

 

4.6.2改變屏幕光標
Cursor屬性可能是TScreen最常用的屬性之一,它的作用是改變應用程序的光標。例如,下面的代碼把光標改為砂漏狀,表示現在正在進行一個較長時間的操作。

crHourGlass是一個預定義的常量,其他預定義的常量有crBeam和crSize等。這些常量值的范圍是從0到-20(crDefault到crHelp)。可以從在線幫助中查找Cursors屬性的詳細說明,那裡列出所有的光標常量。要改變光標形狀,只要把一個常量賦值給Screen.Cursor。
也可以創建一個自定義的光標,然後把它加到Cursors數組中。為此,必須聲明一個光標常量,這個光標常量的值不能與已有的光標常量重復。預定義的光標常量的值是從0到-20,而自定義的光標常量最好用正數,負數是Borland保留的。例如:

可以使用資源編輯器(例如Delphi5附帶的ImageEditor)來創建自定義的光標。創建的光標必須保存到一個資源文件中。要注意的是,Delphi5會為一個項目自動創建一個資源文件。因此,自定義的資源文件不能與項目文件原有的資源文件重名。另外,自定義的資源文件要放在與項目文件相同的目錄中,這樣編譯器才會找到這個資源文件。要使Delphi5能夠把資源文件鏈接到應用程序中,可以參照下面這行語句:

最後,可以參照下面的代碼把自定義的光標調入,加到Cursors數組中,並指定使用這個光標:

這裡使用了LoadCursor()函數來調入光標。LoadCursor()需要傳遞兩個參數:一個是需要使用這個光標的模塊的句柄,另一個是在.res文件中指定的光標的名字(必須全部大寫)。
hInstance代表當前運行的應用程序。接著將從FormCreate返回的值賦給Cursor屬性中由crCrossHair指定的位置。最後將當前光標賦給Screen.Cursor。
如果需要的話,可以使用Toolsl|ImageEditor菜單命令打開Image Editor,然後打開資源文件CrossHairRes.res,看看這個光標到底是怎麼創建的的。

4.6.3避免創建一個窗體的多個實例
如果使用Application.CreateForm()或TForm.Create()來創建窗體的實例,最好確保當前沒有相同的實例存在。下面的代碼演示了這一點:

上面的代碼中,必須在釋放SomeForm變量後把它賦值為nil,否則,Assigned()函數將無法正常工作。不過,上面的代碼不適用於無模式窗體,因為對於無模式窗體來說,程序代碼並不知道什麼時候刪除窗體實例。因此,必須在處理onDestroy事件的處理方法中把窗體的實例賦值為nil。本章前面介紹過這種方法。

4.6.4在DPR文件中增加代碼
可以主窗體創建之前向項目文件中增加一些代碼。這常用於做一些初始化工作,也可以根據需要終止應用程序。清單4-8列出了一個項目文件,它要求用戶輸入一個口令。清單4-8演示項目初始化的Initialize.dpr文件


4.6.5覆蓋應用程序的異常處理
Win32系統具有強大的異常處理能力。缺省情況下,當一個異常發生時,應用程序會自動處理,並顯示一個標准的錯誤框。
當開發一個大型的應用程序時,可能需要定義自己的異常。Delphi5默認的異常處理不能滿足需要,因為應用程序往往需要對異常進行特殊的處理。這種情況下,需要覆蓋TApplication的默認異常處理,用自己的方法來代替默認的異常處理方法。
TApplication提供了一個OnException事件,可以響應這個事件並加入代碼。當一個異常發生時,就會觸發這個事件,這樣就可以進行特殊的處理,同時,原有的標准錯誤框不會出現。
但是,由於TApplication的屬性和事件都無法在Object Inspector上列出來,必須在應用程序中使用TApplicationEvents組件增加指定的異常處理方法。
清單4-9演示了怎樣覆蓋應用程序的默認異常處理。清單4-9演示覆蓋異常處理的主窗體


在清單4-9中,appevnMainException()方法對TApplicationEvent組件的OnException事件進行處理。首先使用RTTI檢查異常的類型,然後各自進行特殊的處理。代碼中的注釋介紹了處理的過程。
提示:如果選中DebuggerOptions對話框(通過Options|Debugger Options菜單項進入)的Language Exceptions頁上的Stop on Delphi Exceptions復選框,當一個程序在調試運行時,如果出現異常,調試器將先報告這個異常,應用程序中的異常處理再起作用。盡管對於調試這很有用,但在查看自定義的異常處理時這很煩人。關閉這個選項,讓項目正常運行。

4.6.6顯示一個封面
假設要為項目創建一個封面。封面能夠在應用程序啟動時顯示,並在應用程序初始化期間一直停留在屏幕上。顯示封面是一件很簡單的事情。
下面是創建一個封面的基本步驟:
1)在創建了主窗體後,再創建一個窗體來作為封面。把這個窗體叫做SplashForm。
2)使用Project|Options菜單命令,確保SplashForm沒有出現在Auto-Create列表中。
3)把SplashForm的BorderStyle屬性設為bsNone,BorderIcons屬性設為[]。
4)把一個TImage組件放到SplashForm上,把它的Align屬性設為alClient。
5)選擇Picture屬性,在TImage組件中調入一個位圖。
現在已經設計好這個封面了,只要在項目文件中加入代碼來顯示它。清單4-10列出了一個項目文件,其中包含了顯示封面的代碼。清單4-10一個帶有封面的項目文件

注意代碼中有這樣一個循環:

這個循環是為了造成延時。窗體上有一個TTimer組件,它的Interval屬性設為3000。當TTimer組件的OnTimer事件發生時,就執行下面這行代碼:

上面這行代碼使while循環的條件為False,並結束循環。

4.6.7使窗體尺寸最小
為了說明怎樣改變窗體的尺寸,下面將介紹一個項目,它的主窗體具有藍色的背景,上面放有一個面板。當用戶改變窗體的尺寸時,這個面板總是位於窗體的中心,並且不允許用戶把窗體的尺寸設得比面板還要小。清單4-11列出了有關代碼。清單4-11模板窗體的源代碼


上面的代碼演示了怎樣捕捉Windows消息,特別是WM_WINDOWPOSCHANGING消息。這個消息是當窗口的尺寸將要改變時發生的,這樣就有機會阻止尺寸的改變。第5章"理解Windows消息"將進一步介紹Windows消息。

4.6.8運行沒有窗體的項目
窗體是Delphi5應用程序的焦點。不過,完全可以創建一個沒有窗體的應用程序。為此要創建一個新的項目,然後使用Project|Remove From Project菜單命令把主窗體移走。此時的項目文件如下所示:

事實上,甚至可以把uses子句、Application.Initialize和Application.Run都刪掉:

當然,這麼簡單的項目肯定是沒有任何實際意義的,但要記住,可以在begin..end之間加入代碼,這將成為Win32控制台程序的起點。

4.6.9退出Windows
有些情況下往往需要退出Windows。例如,應用程序可能修改了系統配置,而這些配置要在Windows重新啟動後才有效。當然可以提示用戶自己去重新啟動Windows,但常規的做法是詢問用戶要不要重新啟動Windows;如果要的話,就通過程序重新啟動Windows。不過要記住,系統重啟不是很好的行為,應當盡量避免。
要退出Windows,需要用到下面兩個API函數中的一個:ExitWindows()或ExitWindowsEx()。
ExitWindows()函數是從16位Wndows移植過來的。在16位Wndows中,需要設置有關選項,以允許退出Windows後再重新啟動Windows。不過,在Win32中,這個函數只是注銷當前用戶,然後讓其他用戶登錄。
現在最好用ExitWindowsEx()函數,這個函數能夠注銷當前用戶、關閉Windows,或者在關閉Windows後重新啟動它。清單4-12演示上述函數的用法。清單4-12用ExitWindows()或ExitWindowsEx()退出Windows

上面的代碼用一組單選按鈕來讓用戶選擇退出Windows的方式。第一個選項將調用ExitWindows()注銷當前用戶,然後以另外一個用戶身份登錄。剩下的兩個選項都是使用ExitWindowsEx()函數。第二個選項退出Windows然後重新啟動計算機。第三個選項退出並關閉系統,這樣用戶就可以斷開電源。第四個選項與第一個選項相同,但它使用的是ExitWindowsEx()函數。
不論是ExitWindows()還是ExitWindowsEx()函數,如果調用成功就返回True,否則就返回False。
可以使用SysUtils.pas中的Win32Check()函數,它將調用Win32API函數GetLastError()來顯示錯誤信息。
注意:如果運行在WindowsNT環境下,使用ExitWindowsEx()函數並不會關閉系統;這需要特殊的權限。必須使用Win32API函數AdjustTokenPrivleges()授予SE_SHUTDOWN_NAME權限。有關這方面內容的詳細介紹請查找Win32的在線幫助。

4.6.10防止關閉Windows
有時候不允許關閉Windows。例如,假設一個應用程序正在編輯一個文件而且還沒有存盤,這時如果另一個應用程序調用了ExitWindowsEx(),則會關閉Windows,從而導致數據丟失,除非應用程序知道Windows將要退出。這其實很簡單,只要響應主窗體的OnCloseQuery事件,然後參照下面的代碼進行處理:

如果把CanClose設為False,表示不允許關閉Windows。如果把CanClose設為True,將提示用戶保存文件。可以在光盤中找到一個示例程序NoClose.dpr。
提示:如果運行的是一個無窗體的程序,那麼應當捕捉WM_QUERYENDSESSION消息。只要有一個應用程序調用了ExitWindows()或ExitWindowsEx(),每個應用程序就會收到WM_QUERYENDSESSION消息。如果應用程序從這個消息返回非零值,表示允許Windows關閉。如果返回零,表示不允許Windows關閉。在第5章"理解Windows消息"中將進一步講解有關Windows的消息處理。

4.7總結
本章重點介紹了項目管理技術和體系結構。主要討論了組成Delphi5項目的關鍵要素:TForm、TApplication和TScreen。我們通過建立一個通用的體系結構來演示怎樣開始設計一個應用程序。這一章還介紹了一些其他有用的例程。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved