定義菜單數據結構
要實現動態菜單,首先要設計壹個合理的菜單數據結構,它的數據源可以是任何DBMS甚至TXT文本文件(只要能建立合理的層次結構)。
菜單有樹狀控件壹樣的層次序列結構,所以在定義數據結構的時候,要選擇壹個能形象地表示父子兄弟關系的模型,最能體現菜單結構的控件就是樹狀控件treeview,按照兩位數級數定義數據結構形式,即按級別確定層數,按序號確定兄弟關系,按兩位數級數確定父子關系。例如,如圖所示的菜單對應的數據結構如下。
這種菜單結構在建立菜單結構時非常適合遞歸算法,所以我們可以根據樹遍歷算法建立壹個具有樹結構的菜單對象
接下來,定義菜單數據結構。菜單數據結構應包括以下基本元素:菜單名稱、菜單類型、菜單編號、菜單項文本、菜單項id、菜單項執行代碼和菜單顯示樣式。下表顯示了。
2.動態創建菜單
流程圖
生產過程說明
如上圖,建立菜單的整個過程分為兩部分:初始化菜單和設置菜單屬性。初始化菜單是通過遞歸算法從數據源讀取菜單數據,每讀取壹個菜單項就建立壹個菜單項對象。首先,使用powerbuilder中的create方法定義壹個菜單實例對象。這裏的菜單指的是主菜單而不是彈出菜單。兩者的區別在於彈出。菜單的處理在後面介紹。菜單建立的核心原理很簡單。創建菜單對象只有四句話。菜單項先隱藏,然後顯示。菜單對象如下
整數?Ai_item_serial_no //序列號作為遞歸函數傳入參數菜單。am_obj?//菜單對象傳入參數作為遞歸函數?M_menu_item lam_root //菜單對象M_menu_item是壹個預定義的菜單對象//這個對象沒有菜單項//創建菜單對象lam _ root item[ai _ item _ serial _ no]=創建m _ menu _ item//將新創建的菜單對象附加到已有的菜單對象am _ obj = lam _ root item[ai _ item _ serial _ no]//下面兩句話用來表示已建立的菜單lam_root Hide()//隱藏菜單對象?Lam_root Show()//顯示菜單對象。
把上面的語句放在壹個遞歸的過程中就可以建立整個菜單結構?
在建立菜單的過程中,需要獲取菜單的itemid。該屬性是用於捕獲菜單響應動作的唯壹標記。只有已知菜單的itemid知道哪個菜單項觸發了事件。
經過不同系統的反復測試,發現父菜單的itemid從頭開始增加,子菜單的itemid從頭開始增加,這樣每壹層的每個菜單項的itemid都是按照遞歸算法生成並存儲在數據庫中。
設置菜單顯示樣式是在菜單建立後,設置三種顯示樣式,文字樣式、圖片樣式和文字圖片混合顯示方式。為了提高效率,在設置每個菜單樣式時,父菜單的所有不可見菜單項和沒有定義顯示圖片的菜單項都不設置,因為文本樣式是默認樣式,所以這部分不需要更改。程序員主要使用三個API函數。
Getsubmenu用於獲取指定菜單項的句柄。SetMenuItemBitmaps用於設置文本顯示樣式或設置圖片樣式。兩種情況的區別是,如果這個函數的後兩位數是0,則菜單項上的位圖被去掉;如果圖片句柄為0,則bitmap ModifyMenu被添加到菜單項以設置圖片顯示樣式。
經過反復測試發現,如果指定的顯示圖片命名為***bmp等非法名稱,顯示的效果就是壹個分隔符。
在菜單建立的整個過程中,需要設計的是程序算法數據存取和錯誤控制的方式。
程序算法主要指遞歸算法。壹般有兩種遞歸算法,即FOR循環法和DO…while循環法。兩者都是循環算法,只是效率不同。建議用戶不要根據自己的能力選擇方法。控制循環中遍歷的次數,然後調用自身實現遞歸調用,簡單直觀。DO…while循環方法主要是判斷循環中的葉子或分支(即父節點)來分別處理葉子和分支,同時也調用自身來實現遞歸調用。
選擇合理安全的數據訪問方式對於菜單的穩定建立也是非常重要的。定義壹個datastore對象,將初始化菜單時從數據庫中提取的所有數據存儲在datastore對象中,然後不再對數據庫做任何操作,直到需要結束,以datastore update的形式提交更改後的菜單數據(如itemid)。在數據庫之前,所有需要從datastore中獲取的數據都是通過過濾獲得的,也就是setfilter()和filter()函數。必須註意的是,在根據配對編程的規則對數據存儲中的數據進行過濾和使用之後,必須編寫壹對過濾條件為空的過濾器,如下所示:setfilter (condition) filter()...過程...設置過濾器()過濾器()。
這也可以及時將數據恢復到初始狀態,以供下壹個模塊調用。
使用datastore不僅可以保證菜單建立過程中的數據安全,還可以提高效率,節省數據庫的重復讀寫操作。
由於菜單的重要性,錯誤控制在菜單建立中尤為重要。當遞歸建立菜單時,我們應該考慮盡可能多的潛在錯誤。沒有人能保證數據庫中的菜單結構數據沒有錯誤。雖然正確的定義並不是建立模塊的事情,尤其是菜單的兩位數遞進層次數據結構,如果出現錯誤,整個建立過程可能會失敗,更嚴重的是程序會異常退出,所以程序是為了處理錯誤而設計的。我們要考慮是終止流程還是跳過錯誤的環節繼續。我建議在設計程序時要有壹定的冗余和糾錯能力,即遇到錯誤的數據可以根據環境修正為正確的值,壹些可以忽略的小問題不處理,提高效率
需要指出的是,經過反復測試發現,如果菜單屬性是字符型,則不能賦空值;如果不是,應該是空字符串;如果是整型,就不能賦空值;如果不是,應該是默認整數;否則,程序將報告異常並退出。
可見反復測試非常重要,不僅要發現語法錯誤,保證算法的正確性,還要發現很多我們難以推斷的錯誤
三對彈出菜單的特殊處理
因為彈出菜單的對象定義和調用方式與主菜單不同,所以需要壹些特殊的處理。首先,定義壹個菜單實例對象,它只需要有壹個根菜單。所有的彈出菜單項都鏈接到根菜單項,彈出菜單要在窗口的鼠標右鍵事件中調用,而主菜單需要在窗口初始化事件調用彈出菜單之前知道彈出點的X Y坐標,然後用popmenu()函數顯示出來。
處理四個菜單響應事件
由於菜單的響應事件是在數據結構中定義的,所以當用戶在菜單建立後點擊壹個菜單時,只需要獲取菜單的句柄就可以知道是哪個菜單被觸發了,然後在數據庫中找到對應的事件定義就可以開始執行動作。重要的是句柄如何獲取菜單的句柄,即itemid在菜單所在的窗口中定義了壹個自定義事件ue_mouse_clicked EV。ENT_ID pbm_menuselect該事件中有兩個參數可用:itemid和flag itemid,即被觸發的菜單對象的句柄。flag是對應於windows消息號的標誌。當這個標誌不相等時,菜單事件被觸發,所以我們可以定義壹個實例變量來保存itemid並調用菜單事件。還有壹個關鍵問題:什麽時候?觸發事件ue_mouse_clicked呢?如果在自定義菜單實例對象的clicked事件中,編寫下面的代碼?Isvalid(iw_win)?那麽message string parm = this is _ ItemID?//將itemid作為消息傳遞給iw _ winpostevent(UE _ menuitem clicked)?//觸發窗口事件處理消息窗口實例變量是end定義的_ItemID如果用iw_win保存menu itemid的字符串類型的實例變量postEvent,將響應處理放在menu事件的末尾,以免妨礙前面定義的動作。
Lishi Xinzhi/Article/program/SQL/201311/16396