開發一個嚮導程序

通常向導程序有這樣一些特徵:
1.         都是對話框,從一個對話框跳到另一個對話框,還可以根據選擇再跳回來,是一種以路徑爲導向的。
2.         嚮導的下部包括控制路徑的幾個按鈕:上一步、下一步、取消,等等。
3.         通常向導中會包含一道長時間的操作,在這個操作之前需要配置該操作的環境。
最典型的嚮導就是InstallShield安裝程序或者Microsoft Installer程序。
有時候我們需要做一些數據遷移工作,將數據從一個平臺遷移到另外一個平臺。這時候我們可以做一個嚮導程序來完成。最簡單的思路或者說設計就是將每個對話框做成一個獨立的Form,然後Application會根據用戶的選擇在多個對話框之間流轉。這種設計存在一定的缺陷:每個對話框中都必須包含流轉控制的代碼,也必須包含整個頁面,包括一些相同的頁面元素。對於很多人來說,這是不能接受的。所以,下面的設計比較符合他們的思路。
定義一個Form,這個Form上包含了所有的公共元素:
其中包括一個預留的區域,用於加載每個幀(TFrame,在.net中是UserControl)。現在不考慮幀的事情,先想想在這個Form中有哪些內容是由幀來控制的。
1.         標題。即標題欄中的RzLabel1RzLabel2兩個部分。第一個標籤用於描述該頁的內容,第二個標籤爲該頁行爲的具體描述,還可以描述一些具體操作的指導。
2.         按鈕狀態。即什麼時候“上一步”有效、什麼時候“下一步”有效。如果嚮導程序完成,“取消” 按鈕應該變成“完成”按鈕。
3.         按鈕行爲。即按下不同的按鈕會產生什麼行爲。
4.         如果可能的話,對話框左側的圖片和標題欄右側的圖片也需要由每個幀來提供。
按照這個目標,我們來設計一下這個幀應該提供什麼功能。
1.         一個Caption屬性和一個Description屬性,字符串類型,分別用來填充Form上的RzLabel1RzLabel2
2.         一個PreviousPage屬性和一個NextPage屬性,先不考慮他們什麼類型。他們的功能就是在按下“上一步”和“下一步”按鈕時,執行什麼動作。
3.         一個IsComplete屬性,布爾類型,說明此頁是否爲結束頁。如果是結束頁則修改“取消”按鈕爲“完成”按鈕。
4.         一個CanAbort屬性,布爾類型,說明該頁是否可以被用戶終止。如果返回爲False時則Disabled那個“取消”按鈕。
5.         一個ShowPage方法,沒有任何參數,用於被激活時由Form主動調用。
6.         一個Abort方法,沒有任何參數,用於在長時間處理中,用戶按下“取消”按鈕時頁面的響應。
7.         一個OnPageChange事件,參數就是幀,也就是我們設計的目標類型。用於頁面內部調用Form的功能,例如,但頁面上某個編輯框內容爲空時,將“上一步”或“下一步”按鈕置爲Disabled,當這個編輯框內容不爲空時再將按鈕置爲Enabled。本質上,就是一個回調。
現在來研究一下,PreviousPageNextPage屬性採用什麼類型。通常我們這樣的設計要求達到這樣一個目標:每個幀之間、幀與Form之間儘量不要耦合,就是不必知道對方是如何實現的。爲了這個目標,我們需要花點時間研究這個機制。
首先是數據機制。嚮導的目標就是用戶對數據的選擇。也就是說,在某些頁面(例如執行導入的頁面)上必須知道另外的頁面所設置的數據。這些數據可以使用一個專門的單元來處理,這個單元對每個頁面都是開放的。往往這種耦合是必要的。在這個單元中設置一些全局變量,讓相關的頁面來訪問。
其次是頁面機制。在實施前,往往需要畫一下流轉圖,在什麼條件下由一個頁面轉到另外一個頁面。通常都是這種形式:
u       開始頁(StartPage)
u       設置源(SourcePage)
u       設置目標(DestinationPage)
u       確認(ReadyPage)
u       執行導入(ImportingPage)
有了這張表,現在簡單了。我們爲每個頁面賦一個別名(例如StartPage),然後設計一個專門的別名管理器,每個頁面都自己向這個頁面管理器註冊自己。然後所有的頁面都可以通過這個別名來獲得頁面的實例。所以,最後確定,PreviousPageNextPage屬性採用字符串類型。那麼,這個別名管理器用什麼來實現?最好的選擇就是採用一個TStrings(如果在.net中可以使用System.Collections.Hashtable)。每個項就是一個別名對應一個頁面的實例。不要考慮用頁面的類名來作爲頁面的別名,這樣你就不可以通過不同的頁面名稱來實例化相同的頁面類型了。考慮一下這個別名管理器誰來負責其生命週期?很簡單。在這個頁面框架所在的單元的初始化部分(initialization)創建,然後在清理部分(finalization)釋放。同樣,在每個頁面所在單元的初始化部分(initialization)創建並向別名管理器註冊相關的實例;而在清理部分(finalization)註銷這些實例。如果是.net就不必管這些實例什麼時候被釋放了。我們將這個頁面的基類命名爲TPage(.net中爲Page),創建的方式是新建一個TFrame(.net中爲UserControl),什麼內容也沒有。然後加上上面的設計。剩下的事情就是一個個地建立這些頁面。你可以直接NewOther→您的項目名→TPage(如果在.net中則更簡單,在項目中直接通過上下文菜單選擇添加→添加繼承的用戶控件)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章