PowerBuilder程式設計新思維5:包裝(界面美化與WebUI+React)
前一節,分析了三種界面美化方案,都是控件級的美化。今天再來分析一下視窗級的美化。上一次講的DirectUI,大家反響一般,效果和創新都不足。那是沒有講完的原故。今天……還是沒有講完,不過效果應該還行吧:)。
PB界面美化方案四:DirectUI包裝
Hook的方式使用DirectUI,是局限在控件裡。如果建立一個類似Canvas的可視控件,把所有控件包裝在一個(或幾個)控件裡面,我把這種方式稱之包裝(Wrap)。
在現有界面上改造,不需要改動原有代碼,隻需在界面open事件中打開u_dui控件,并of_load("ui.xml")即可,所有消息轉到相應名稱的控件的事件中。ui.xml是DirectUI的界面定義檔案,如下圖(所有資源來源于網絡)

PB界面美化方案五:Sciter(WebUI)包裝
相比DirectUI的界面包裝,WebUI的界面包裝更為流行。使用Web控件替代原有控件,可以做出非常好的效果。
Sciter及其前身HtmlLayout,都是大名鼎鼎的WebUI控件,使用廣泛。Sciter的Powerbuilder封裝,在PowerFramework中有提供。
Sciter是最早一批專業的WebUI解決方案,相比Cef近百兆的體積,它的隻有幾兆。是以在通用軟體中廣泛使用。
Sciter的缺點就是不完全與Js相容,在體積與速度方面有優勢,在相容性方面就有劣勢了。由于前端飽受IE的折磨,建議對體積與速度方面不太敏感的應用不要使用。
Sciter的效果還是很贊的,下圖是QQ動态登入界面。
PB界面美化方案六:Miniblink(WebUI)包裝
Miniblink是這兩年冒出的嵌入式Web新秀,更新非常快,是以用起來很放心。它的大小20多兆,比Cef小很多,而且使用的是比較新的Chrome核心版本,相比Sciter,Miniblink幾乎沒有相容性的問題。可以順利運作幾乎所有的前端架構。
PowerFramework也封裝了Miniblink,而且還全面導出了Powerbuilder的JS接口,使用友善。在PowerPlume裡面,也封裝了Miniblink(u_wui),但是沒有像n_lua對象,或者PowerFramework一樣導出JS接口。至于為什麼,往下看就知道了。使用效果如下圖:
PB界面美化方案七:DirectUI自動包裝
前面所說的包裝(Wrap)全部都手工進行的,就是手工編寫相應的XML,或者HTML。對于個别的界面美化是可行的,如果對于大型管理軟體做這樣的美化修改是不可能的。界面的數量太多,手工編寫工作量太大,而且測試工作量同樣巨大。這時,我們要求對現有界面進行自動包裝,生成相應的XML或者HTML。
DriectUI的自動包裝工作,比較好做。隻需在控件的相應位置,添加一個控件,把原有控件置為底層即可。
u_dui控件使用方法如下:
1 if not isvalid(iu_dui) then
2 cb_unwrap.event clicked()
3 OpenUserObject(iu_dui, "u_dui", 0, 0)
4 iu_dui.of_load("ui2.xml")
5
6 iu_dui.of_wrap(cb_dui, "")
7 iu_dui.of_wrap(cb_wui, "")
8 iu_dui.of_wrap(cb_unwrap, "")
9
10 iu_dui.of_wrap(st_1, "")
11 iu_dui.of_wrap(st_2, "")
12 iu_dui.of_wrap(sle_1, "")
13 iu_dui.of_wrap(sle_2, "")
14 end if
對于Datawindow這樣的控件,暫時沒有包裝方案,是以不動即可。
PB界面美化方案八:WebUI自動包裝
這個部分最難。涉及的方面非常多。
要求生成的代碼必須要有通用性、可定制化、簡單清晰。要達到這個要求,必須是動态頁面,而非靜态HTML。是以選型的時候,重點考慮了React、Vue、Angular,而不是傳統的JQuery+Bootstrap。
React作為大熱的前端架構,這裡就不詳細介紹了。React隻負責View的部分,這正是我們需要的,相比大而全的Angular更适合WebUI包裝。而基于MVVM模型的Vue,則可能在接口方面有更底層的修改需求,是以最終選擇了React。
完整的MVC架構,應該是React+Redux組合,則使用PB替換Redux就是我們的目标。首先,我們來分析一下Redux。下面是簡化版Redux:
// Mini Redux
const createStore = (reducer) => {
let state;
let listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener);
}
};
dispatch({});
return { getState, dispatch, subscribe };
};
const reducer = function (state, action) {
...
return null;
};
const store = createStore(reducer);
const state = store.getState();
由于Redux的靈感來自于Flux的單向資料流,所有的資料隻有一個入口Reducer。是以,最終設計的JS接口隻有一個(又雙叒叕是一個接口?!)
pbSend( { type: 'clicked', payload: {name: 'button', value: ''} } );
u_wui的使用方式與u_dui類似,隻多了一個of_render(template, output)接口
1 OpenUserObject(iu_wui, "u_wui", 0, 0)
2 iu_wui.width = parent.width
3 iu_wui.height = parent.height
4
5 iu_wui.of_wrap(cb_dui, "")
6 iu_wui.of_wrap(cb_wui, "")
7 iu_wui.of_wrap(cb_unwrap, "")
8
9 iu_wui.of_wrap(st_1, "")
10 iu_wui.of_wrap(st_2, "")
11 iu_wui.of_wrap(sle_1, "")
12 iu_wui.of_wrap(sle_2, "")
13
14 iu_wui.of_render("res/html/wui.tpl", "res/html/wui.html")
WebUI在使用時,加載會有一段時間的空白頁面, 可以使用各種技術手段來解決,比如加載動畫,比如服務端渲染(緩存的方式)等等,本節就不深入了。
有人留言說,PB9能不能支援,當然能支援(多了pblua.pbd,pbdui.pbd,pbwui.pbd三個檔案)。PB8及以下的由于不支援PBNI,是以不能支援。
WindowsXP不能運作的問題,沒有測試過,本次試着降級VC++版本為VC2010進行編譯。
源碼: PowerPlumeDemoV0.5.3.rar
提供了PB9.0 PB10.5 PB11.5 PB12.5四個版本
Miniblink node.dll: node.rar
Miniblink由于太大,是以沒有包含在源碼内,采用的是miniblink-190226版本,使用其它版本可能會找不到API。
<本節完>