Silverlight Data FAQ: วิธี bind data source แบบ flexible (untyped)
-
16 กุมภาพันธ์ 2555 8:19ผู้ดูแลSilverlight Data FAQ: วิธี bind data source แบบ flexible (untyped)
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.
ตอบทั้งหมด
-
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_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