個標題好似唔係咁易明 🙂
不過,如果有用過 Visual WebGUI 嘅朋友就可能估到係講咩。
起因係我想替我用 Visual WebGUI 寫嘅其中一個 application 補寫一個功能,俾啲用戶自己更改啲 login users 嘅 security level,原本嘅 security level 用一隻 XML 檔案嚟做,記住不同 security level 喺某個功能有咩 CRUD permission。可以睇一下隻 XML 就易明好多:
<?xml version="1.0" encoding="utf-8" ?> <root> <guest> <item Caption="Order List" Tag ="jbs_OrderList" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Job List" Tag ="jbs_JobList" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Pending" Tag="jbs_Pending" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Schedule" Tag="jbs_Schedule" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Completed List" Tag="jbs_Completedlist" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Packing" Tag="jbs_Packing" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Rtf List" Tag="sml_RtfList" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Invoice List" Tag="sml_InvoiceList" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Rtf Stats" Tag="sml_RtfStats" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Invoice Stats" Tag="sml_InvoiceStats" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Product" Tag ="stk_Product" Create="False" Read="True" Update="False" Delete="False" /> <item Caption="Stock InOut" Tag ="stk_StockInOut" Create="False" Read="True" Update="False" Delete="False" /> <item Caption="Workflow" Tag="adm_Workflow" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="WorkflowForms" Tag="adm_WorkflowForms" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="OrderType" Tag="adm_OrderType" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="User" Tag="adm_User" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="Customer" Tag="adm_Customer" Create="False" Read="False" Update="False" Delete="False" /> <item Caption="System Parameters" Tag="stg_SysParam" Create="False" Read="False" Update="False" Delete="False" /> </guest> <admin> <item Caption="Order List" Tag ="jbs_OrderList" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Job List" Tag ="jbs_JobList" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Pending" Tag="jbs_Pending" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Schedule" Tag="jbs_Schedule" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Completed List" Tag="jbs_Completedlist" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Packing" Tag="jbs_Packing" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Rtf List" Tag="sml_RtfList" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Invoice List" Tag="sml_InvoiceList" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Rtf Stats" Tag="sml_RtfStats" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Invoice Stats" Tag="sml_InvoiceStats" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Product" Tag ="stk_Product" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Stock InOut" Tag ="stk_StockInOut" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Workflow" Tag="adm_Workflow" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="WorkflowForms" Tag="adm_WorkflowForms" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="OrderType" Tag="adm_OrderType" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="User" Tag="adm_User" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="Customer" Tag="adm_Customer" Create="True" Read="True" Update="True" Delete="True" /> <item Caption="System Parameters" Tag="stg_SysParam" Create="False" Read="True" Update="True" Delete="False" /> </admin> </root>
於是乎我就係要寫一個功能可以直接更改隻 XML 入面每隻 item 嘅 attributes。方法可以有好多種,不過我好鍾意哩隻嘢,Xonomy:
直接 click 你想改嘅 attribute 然後會 popup 俾你改。Xonomy 係基於 jquery,用一小段 HTLM 就得,不過如果要 edit 就要提供一隻 document specification 的東西,告訴 Xonomy 那些可以改?改的時候有咩嘢規範?改好嘅 result 可以用 build-in 嘅 javascript function:Xonomy.harvest() 嚟提取。簡單嚟講,通過以下嘅步驟就得,
Read Only:
- 準備隻 HTML
- 放隻 HTML path 入 HtmlBox.Url
- 搞掂
Read/ Write 就麻煩啲,因為要 harvest:
- 準備隻 HTML
- 準備隻 document specification
- 放入 HtmlBox.Url
- client side 修改
- client side 改完,click Save button,啟動 Xonomy.harvest(),通知 server
- server 接收修改後嘅 XML
係咪好似幾易吖?Read Only 太易,唔使分開嚟講,齋講 Read/ Write,1/2/3 係 server side,4/5 係 client side,而 6 又變番 server side,即係一個由 server > client > server 一個 round trip。
首先去攞有關嘅檔案:
- Xonomy v2.3 A ZIP file containing xonomy.js, xonomy.css and some supporting files.
- Xonomy v2.3 User manual
- jquery-1.10.2.min.js
- 我搞嘅 project files
我用 VS2010,Visual WebGUI 10.0.4,其實 projects files 都已經包括咗啲 Xonomy & jquery 檔案,不過,自己去攞一次會知道多啲哩堆係咩東西!
OK,講番正題,打開個 project,solution tree 如下:
sub-folder Xonomy 內,除咗三隻 htm 檔案,其他都係喺 Xonomy & jquery 下載嘅,
- Xonomy.htm
Read only,即係淨係有 Html 同 XML data,冇加 document specification - Xonomy_Editable.htm
Read/ Write,即係有 Html,XML data,同 documents specification - Xonomy_Wired.htm
Read/ Wrtie 同埋有哂 round-trip 要用嘅 javascript function
Xonomy.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <script type="text/javascript" src="jquery-1.10.2.min.js"></script> <script type="text/javascript" src="/xonomy/xonomy.js"></script> <link type="text/css" rel="stylesheet" href="/xonomy/xonomy.css"/> <script type="text/javascript"> function start() { var xml = "<list><item label='one'/><item label='two'/></list>"; var editor = document.getElementById("editor"); Xonomy.render(xml, editor, null); } </script> </head> <body onload="start()"> <div id="editor"></div> </body> </html>
Line 9 的 xml 就是 XML data,這裡是直接 hard coded,line 11 的 null 就是沒有 document specification 既意思,也就是說 Read Only,Html load 完就執行 start(),最後用 Xonomy.render() 交俾 Xonomy 全權負責。
Xonomy_Editable.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <script type="text/javascript" src="jquery-1.10.2.min.js"></script> <script type="text/javascript" src="/xonomy/xonomy.js"></script> <link type="text/css" rel="stylesheet" href="/xonomy/xonomy.css"/> <script type="text/javascript"> function start() { var docSpec = { onchange: function () { console.log("I been changed now!") }, validate: function (obj) { console.log("I be validatin' now!") }, elements: { "list": { menu: [{ caption: "Append an <item>", action: Xonomy.newElementChild, actionParameter: "<item/>" }] }, "item": { menu: [{ caption: "Add @label="something"", action: Xonomy.newAttribute, actionParameter: { name: "label", value: "something" }, hideIf: function (jsElement) { return jsElement.hasAttribute("label"); } }, { caption: "Delete this <item>", action: Xonomy.deleteElement }, { caption: "New <item> before this", action: Xonomy.newElementBefore, actionParameter: "<item/>" }, { caption: "New <item> after this", action: Xonomy.newElementAfter, actionParameter: "<item/>" }], canDropTo: ["list"], attributes: { "label": { asker: Xonomy.askString, menu: [{ caption: "Delete this @label", action: Xonomy.deleteAttribute }] } } } } }; var xml = "<list><item label='one'/><item label='two'/></list>"; var editor = document.getElementById("editor"); Xonomy.render(xml, editor, docSpec); } </script> </head> <body onload="start()"> <div id="editor"></div> </body> </html>
多咗 line 7 至 line 56(呻一呻,免費 WordPress 冇法加 plugins,搞唔掂啲 indent,而且又唔識控制個長度,好討厭!)就係個 document specification 資料,想知掂解就要 study 個 user manual,其他部份跟 Read Only 一樣。
Xonomy_Wired.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <script type="text/javascript" src="jquery-1.10.2.min.js"></script> <script type="text/javascript" src="/xonomy/xonomy.js"></script> <link type="text/css" rel="stylesheet" href="/xonomy/xonomy.css"/> <!-- The following line is vital! Without it your code will not work in obfuscated mode!!! --> <script src='Resources.Gizmox.WebGUI.Forms.Skins.CommonSkin.Interfaces.js.wgx' type='text/javascript'></script> <script type="text/javascript"> function start() { var docSpec = { onchange: function () { console.log("I been changed now!") }, validate: function (obj) { console.log("I be validatin' now!") }, elements: { "list": { menu: [{ caption: "Append an <item>", action: Xonomy.newElementChild, actionParameter: "<item/>" }] }, "item": { menu: [{ caption: "Add @label="something"", action: Xonomy.newAttribute, actionParameter: { name: "label", value: "something" }, hideIf: function (jsElement) { return jsElement.hasAttribute("label"); } }, { caption: "Delete this <item>", action: Xonomy.deleteElement }, { caption: "New <item> before this", action: Xonomy.newElementBefore, actionParameter: "<item/>" }, { caption: "New <item> after this", action: Xonomy.newElementAfter, actionParameter: "<item/>" }], canDropTo: ["list"], attributes: { "label": { asker: Xonomy.askString, menu: [{ caption: "Delete this @label", action: Xonomy.deleteAttribute }] } } } } }; var xml = "<list><item label='one'/><item label='two'/></list>"; var editor = document.getElementById("editor"); Xonomy.render(xml, editor, docSpec); } </script> <script type="text/javascript"> function submit(formId) { if (typeof (VWG) == 'undefined' || VWG == null || VWG.Events == null) return; var xml = Xonomy.harvest(); var eventType = "XonomyHarvest"; var objEvent = VWG.Events.CreateEvent(formId, eventType); VWG.Events.SetEventAttribute(objEvent, "Value", xml); VWG.Events.RaiseEvents(); } </script> </head> <body onload="start()"> <div id="editor"></div> </body> </html>
比 Xonomy_Editable.htm 多咗 line 8 同埋 line 65 至 line 75,如果要用到 VWG client side 嘅 javascript functions 就一定要有 line 8,否則 line 71/ 72/ 73 就會冇反應,用 js debugger 就會見到 VWG undefined。Line 66 哩個 function submit(formId) 係用嚟 call Xonomy.harvest(),然後將個 XML data 送返去 server。formId 好重要,就係哩個 VWG Form 嘅 runtime Tag ID,唔錯得,否則 Visual WebGUI 就唔知你想 post back 邊個 window,而且,由於 Visual WebGUI 將啲 DOM Tags 改寫過,好難喺 client side 認出邊個打邊個,所以要由 server side 提供就十拿九穩,於是當 server side call submit(strId) 時必須提供正確的 Form.ID(後面會講到)。
Form1.cs
左邊 No content 係 HtmlBox,右邊有 5 個 button,Clear Html 係用嚟清空隻 HtmlBox,Load Html (Read) 就係 load Xonomy.htm 入 HtmlBox,Load Html R/W 係 load Xonomy_Editable.htm,而 Load Wired Html 就係 load Xonomy_Wired.htm,好易明。Harvest 就係喺 server side 叫 client 做嘢:
private void cmdHarvest_Click(object sender, EventArgs e) { String script = String.Format("document.getElementById("TRG_{0}").contentWindow.submit("VWG_{1}");", this.htmlBox1.ID.ToString(), _FormId); VWGClientContext.Current.Invoke(this, "eval", script); }
Line 3 嘅 _FormId 喺邊度嚟?當 Form.Load 嘅時候記低:
void Form1_Load(object sender, EventArgs e) { // 搵出 client side 哩個 window 嘅 DOM id _FormId = ((RegisteredComponent)sender).ID.ToString(); }
TRG_ 同 VWG_ 就係 Visual WebGUI 改寫啲 html Tags 用既,一定要照跟,line 4 VWGClientContext.Current.Invoke 就係由 server side 叫 client side 做嘢,client side 收到指示就執行 function submit(strId) ,一切順利就會 RaiseEvent call back server,server side 就用下面嘅 FireEvent 接收:
protected override void FireEvent(Gizmox.WebGUI.Common.Interfaces.IEvent objEvent) { switch (objEvent.Type) { case "XonomyHarvest": String xml = objEvent["Value"]; MessageBox.Show(xml); break; default: base.FireEvent(objEvent); break; } }
冇 indents 唔係咁易睇?咁,我諗你應該係一路睇住個 project files,冇咩問題?收工!