Silverlight Data FAQ: วิธี bind data source แบบ flexible (untyped)

Answered Silverlight Data FAQ: วิธี bind data source แบบ flexible (untyped)

ตอบทั้งหมด

  • 16 กุมภาพันธ์ 2555 8:28
    ผู้ดูแล
     
     คำตอบ



    data binding ใน Silverlight2 ถูกออกแบบมาเป็นอย่างดี โดยที่เราจะต้องมีการกำหนด object model ไว้ล่วงหน้า ซึ่งเป็นลักษณะส่วนใหญ่ แต่ก็ยังมีหลายอย่างที่ยังจำเป็นต้องใช้ data source ที่มีความยืดหยุ่นกว่า เพราะว่าเราไม่สามารถทราบโครงสร้างของ data source ได้ว่าเป็นแบบไหน ซึ่งจะเป็นประโยชน์อย่างมากโดยเฉพาะเมื่อใช้งานร่วมกับ DataGrid เพราะDataGrid รองรับauto generate column



    ยกตัวอย่างเช่น ใน SharePoint ผู้ใช้สามารถจะกำหนด data source ได้โดยการเพิ่ม column ที่กำหนดเองใน GridView และ SharePoint เองก็จะเก็บการตั้งค่าที่ผู้ใช้กำหนดไว้ใน metadata มากกว่าที่จะสร้าง database table ขึ้นมาเป็น column ส่วนตอน runtime ก็จะ generate GridView column เป็นแบบ dynamic ตามที่ได้กำหนดการตั้งค่าไว้ก่อนหน้านี้



    ใน Silverlight เราสามารถใช้วิธีแก้ปัญหาที่คล้ายคลึงกันนี้ได้ โดยวิธีการในลักษณะนี้จะเกี่ยวกับ reflection และ emitting code ซึ่งทั่วไปการทำ emitting code จะเขียนโค้ดค่อนข้างยาก นอกจากว่าเราจะเข้าใจเรื่อง MSIL จริงๆ

    ตัวอย่างการเขียนโปรแกรม เช่น



    publicclass Class1



    {



    private int _Age;



    public int Age



    {



    get { return _Age;
    }



    set { _Age = value;
    }



    }

    }



    ใช้ Reflector ในการ view IL สำหรับ getter และ setter



    L_0000: nop



    L_0001:
    ldarg.0



    L_0002: ldfld int32SilverlightClassLibrary1.Class1::_Age



    L_0007: stloc.0



    L_0008: br.s
    L_000a



    L_000a:
    ldloc.0



    L_000b: ret


    โค้ด



    ldarg.0



    ldfld int32 SilverlightClassLibrary1.Class1::_Age



    ret


    วิธีการโหลด address ของ method เพื่อเรียกใช้ stack เป็น parameter ตัวแรก

    โดยให้โหลด
    field named _Age และคืนค่า



    ซึ่งคล้ายกับ IL สำหรับ setter ดังนี้



    ldarg.0



    ldarg.1



    stfld int32 SilverlightClassLibrary1.Class1::_Age



    ret



    วิธีการโหลด address ของ method จะเรียกใช้ stack เป็น parameter ตัวแรก โดยจะโหลด parameter จริงที่จะส่งต่อไปยัง setter เพื่อเป็น parameter ตัวที่สอง ที่จะเรียกใช้ stack ในการกำหนดค่า field _Age และคืนค่า



    ข้อมูลเพิ่มเติม กรุณาอ้างอิงเอกสาร CLR



    สำหรับความรู้เรื่อง emitting เราสามารถกำหนดค่าได้ที่ metadata ซึ่งเราจะต้องใช้พจนานุกรมในการจัดเก็บรายชื่อ column กับ type โดยในแต่ละ
    metadata จะจัดเก็บรายการในแต่ละ row และในแต่ละ row ก็จะมีพจนานุกรมเพื่อใช้ในการจับคู่กับค่าในแต่ละ column

    ซึ่งเราจะได้
    data structure ดังต่อไปนี้



    publicclass Metadata



    {



    public Dictionary<string, Type> Columns { get; set;
    }



    public List<MetadataRow> Rows { get;
    set; }



    }



    publicclass MetadataRow



    {



    public Dictionary<string, object>
    ColumnValues { get; set;
    }


    }



    สมมติว่าเรามี metadata อยู่ และต้องการสร้าง type dynamic ตามข้อมูลที่มีอยู่ใน metadata เราจะใช้ emitting ในการ generate type ซึ่งก่อนอื่นเราจะต้องสร้าง type ที่ชื่อว่า DataSource แล้วทำการเพิ่ม property ตาม column ที่กำหนดใน metadata

    ข้อมูลเพิ่มเติมเกี่ยวกับวิธีสร้าง
    type แบบ dynamic โดยใช้ emitting

    กรุณาเยี่ยมชม
    http://msdn.microsoft.com/en-us/library/4xxf1410(VS.95).aspx



    AppDomain myDomain =AppDomain.CurrentDomain;



    AssemblyName myAsmName =new AssemblyName("MyAssembly");



    AssemblyBuilder myAssembly =
    myDomain.DefineDynamicAssembly(myAsmName,



    AssemblyBuilderAccess.Run);



    ModuleBuilder myModule =
    myAssembly.DefineDynamicModule(myAsmName.Name);



    TypeBuilder myType =
    myModule.DefineType("DataSource",TypeAttributes.Public);



    foreach (string
    columnNamein metadata.Columns.Keys)



    {



    Type properyType = metadata.Columns[columnName];



    FieldBuilder exField =
    myType.DefineField("_" +
    columnName, properyType,FieldAttributes.Private);



    PropertyBuilder exProperty =
    myType.DefineProperty(columnName,PropertyAttributes.None,



    properyType,Type.EmptyTypes);



    //Get



    MethodBuilder exGetMethod
    = myType.DefineMethod("get_" +
    columnName,



    MethodAttributes.Public,
    properyType,Type.EmptyTypes);



    ILGenerator getIlgen =
    exGetMethod.GetILGenerator();



    //IL for a simple getter:



    //ldarg.0



    //ldfld int32 SilverlightClassLibrary1.Class1::_Age



    //ret



    getIlgen.Emit(OpCodes.Ldarg_0);



    getIlgen.Emit(OpCodes.Ldfld,
    exField);



    getIlgen.Emit(OpCodes.Ret);



    exProperty.SetGetMethod(exGetMethod);



    //Set



    MethodBuilder exSetMethod
    = myType.DefineMethod("set_" +
    columnName,



    MethodAttributes.Public,null, new Type[] { properyType });



    ILGenerator setIlgen =
    exSetMethod.GetILGenerator();



    //IL for a simple setter:



    //ldarg.0



    //ldarg.1



    //stfld int32 SilverlightClassLibrary1.Class1::_Age



    //ret



    setIlgen.Emit(OpCodes.Ldarg_0);



    setIlgen.Emit(OpCodes.Ldarg_1);



    setIlgen.Emit(OpCodes.Stfld,
    exField);



    setIlgen.Emit(OpCodes.Ret);



    exProperty.SetSetMethod(exSetMethod);



    }



    Type finished =
    myType.CreateType();



    หลังจากที่สร้าง type และ properties ขึ้นมาแล้ว เราจะต้องสร้าง instance ของ DataSource ในแต่ละ row ขึ้นมาด้วย และใส่ค่าเข้าไป โดยการกำหนด properties
    และกำหนด
    ItemsSource ของ DataGrid



    ObservableCollection<object> source = new
    ObservableCollection<object>();



    foreach (MetadataRow
    rowin metadata.Rows)



    {



    object item = Activator.CreateInstance(finished);



    foreach (string
    columnNamein metadata.Columns.Keys)



    {



    MethodInfo method =
    finished.GetMethod("set_" +
    columnName);



    method.Invoke(item, newobject[] { row.ColumnValues[columnName] });



    }



    source.Add(item);



    }


    this.dg.ItemsSource =
    source;



    ตามตัวอย่าง เราใช้ data สมมติ แต่ในความเป็นจริง เราจะให้ผู้ใช้กำหนด column, value, serial metadata ใน xml, การเรียกใช้ REST service และการบันทึกค่าบน server



    Metadata metadata =new Metadata();



    metadata.Columns =newDictionary<string,Type>();



    metadata.Columns.Add("Column1",typeof(string));



    metadata.Columns.Add("Column2",typeof(int));



    metadata.Columns.Add("Column3",typeof(bool));



    metadata.Rows =newList<MetadataRow>();



    Dictionary<string,object>
    row1Values = newDictionary<string,object>();



    row1Values.Add("Column1","abc");



    row1Values.Add("Column2",
    2);



    row1Values.Add("Column3",true);



    metadata.Rows.Add(newMetadataRow() { ColumnValues = row1Values });



    Dictionary<string,object>
    row2Values = newDictionary<string,object>();



    row2Values.Add("Column1","xyz");



    row2Values.Add("Column2",
    20);



    row2Values.Add("Column3",true);



    metadata.Rows.Add(newMetadataRow() { ColumnValues = row2Values });



    Dictionary<string,object>
    row3Values = newDictionary<string,object>();



    row3Values.Add("Column1",null);



    row3Values.Add("Column2",
    0);



    row3Values.Add("Column3",false);



    metadata.Rows.Add(newMetadataRow() {
    ColumnValues = row3Values });



    Supa Sethasiripong [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • ทำเครื่องหมายเป็นคำตอบโดย supa_sModerator 20 กุมภาพันธ์ 2555 5:25
    •