先前示範過將JS端的物件傳入Silverlight,現在來逆向操作,把Managed Type傳回JS世界!

宣告一個ColumnSetting類別,其中特別設一個列舉(ColumnTypeEnum)觀察它的傳換結果。另外,宣告兩個函數: SL2JS()傳回ColumnSetting,JS2SL(ColumnSetting)傳入ColumnSetting當參數。

        //欄位型別列舉
        public enum ColumnTypeEnum
        {
            TextBox, NumericTextBox,
            ComboBox, Checkbox, DatePicker
        }
        //記得要加註ScritableType, 要用Property不能用Field
        [ScriptableType]
        public class ColumnSetting
        {
            public string Name { get; set; }
            public ColumnTypeEnum ColumnType { get; set; }
            public int Width { get; set; }
            public ScriptObject OnCellEdit { get; set; }
        }
 
        //傳ColumnSetting到JS端,注意ColumnType傳的是其整數值
        [ScriptableMember]
        public ColumnSetting SL2JS()
        {
            ColumnSetting cs = new ColumnSetting()
            {
                Name = "Amount",
                ColumnType = ColumnTypeEnum.NumericTextBox,
                Width = 100
            };
            return cs;
        }
        [ScriptableMember]
        public void JS2SL(ColumnSetting cs)
        {
            if (IsFunction(cs.OnCellEdit))
                cs.OnCellEdit.InvokeSelf("Callback");
            MessageBox.Show(cs.Width.ToString());
        }

在Javascript端,我們做幾個測試:

  1. 用SL2JS()以cs變數接回ColumnSetting物件
  2. 用cs.Name, cs["Width"]嘗試讀取其值,而列舉cs.ColumnType傳回其整數值1
  3. 用for (var p in cs)測試,可發現它不像一般純Javascript Object可以列舉出屬性,骨子裡它還算是SL中的物件,只是外面再包覆(Marshaling)一層包裝,盡量模擬JS Object的行為而已。
  4. 用先前介紹過的InvokeSelf技巧,可以傳入函數供Silverlight端呼叫。
  5. 修改cs.Width後,再將同一元件當成參數傳入JS2SL(),Silverlight端可以再取得Managed Type讀取修改過的值。
function onSilverlightLoad(sender, args) {
 
    var slCtl = sender.getHost();
    var jssk = slCtl.Content.JavascriptSidekick;
    
    //由SL端取得Managed物件
    var cs = jssk.SL2JS();
    alert(cs.Name); //正常
    alert(cs.ColumnType); //enum,會傳回其值=1
    alert(cs["Width"]); //用[colName]也成
    
    //得到的東西並非純Javascript物件
    try {
        var sb = [];
        for (var p in cs) 
        {
            sb.push(p + "->" + cs[p]);
        }
        //故無法用for (var p in csRefTest)列舉, 
        //IE:sb.length=0 FF:Error
        alert(sb.length);     
    } catch(err) {
        alert("Error:" + err);
    }
    
    //修改內容再傳回去
    //設定回Call事件
    cs.OnCellEdit = function(s) {
        alert("Hello, " + s);
    };
    //修改數值
    cs.Width = 200;
    jssk.JS2SL(cs);
}

那我們可不可以從{ Name:"...", ColumnType:.. }自已捏一個Javascritp物件轉型成ColumnSetting呢? 答案是不行! 即使所有屬性取的名字一模一樣,Javascript物件無法直接轉型成Managed Type。

若要在JS端生成可轉型成Managed Type的物件,可透過createObject()或createManagedObject(),createManagedObject因不需事先RegisterScritableType,使用起來較方便,請參考以下的例子。

    //改用JS端從無到有建立物件
    var jscs = { 
        Name:"UnitPrice", ColumnType:1, Width:300 };
    jssk.JSD2SL(jscs);
    
    //以上的寫法是行不通的,會發生轉型錯誤
    //This object cannot be converted to the specified type 
    //'JsInterop.JavascriptSidekick+ColumnSetting'.        
 
    //要建立可當參數傳入的Managed Object,改用createManagedObject()
    jscs = jssk.createManagedObject("ColumnSetting");
    jscs.Name="UnitPrice";
    jscs.ColumnType=1;
    jscs.Width=365;
    jssk.JS2SL(jscs);

下回再來看另一個有趣的問題,By Value or By Reference?


Comments

Be the first to post a comment

Post a comment