Visual WebGUI 利用 HtmlBox 和 Html Page 互動

個標題好似唔係咁易明 🙂

不過,如果有用過 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

htmlbox_00

直接 click 你想改嘅 attribute 然後會 popup 俾你改。Xonomy 係基於 jquery,用一小段 HTLM 就得,不過如果要 edit 就要提供一隻 document specification 的東西,告訴 Xonomy 那些可以改?改的時候有咩嘢規範?改好嘅 result 可以用 build-in 嘅 javascript function:Xonomy.harvest() 嚟提取。簡單嚟講,通過以下嘅步驟就得,

Read Only:

  1. 準備隻 HTML
  2. 放隻 HTML path 入 HtmlBox.Url
  3. 搞掂

Read/ Write 就麻煩啲,因為要 harvest:

  1. 準備隻 HTML
  2. 準備隻 document specification
  3. 放入 HtmlBox.Url
  4. client side 修改
  5. client side 改完,click Save button,啟動 Xonomy.harvest(),通知 server
  6. server 接收修改後嘅 XML

係咪好似幾易吖?Read Only 太易,唔使分開嚟講,齋講 Read/ Write,1/2/3 係 server side,4/5 係 client side,而 6 又變番 server side,即係一個由 server > client > server 一個 round trip。

首先去攞有關嘅檔案:

我用 VS2010,Visual WebGUI 10.0.4,其實 projects files 都已經包括咗啲 Xonomy & jquery 檔案,不過,自己去攞一次會知道多啲哩堆係咩東西!

OK,講番正題,打開個 project,solution tree 如下:

htmlbox_01

 

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

htmlbox_02

左邊 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,冇咩問題?收工!