Friday, August 17, 2007

Silverlight的Data Bindings:Silverlight 與 ASP.NET Ajax

Silverlight的Data Bindings:Silverlight 與 ASP.NET Ajax



不可否認,對於網頁美工人員或是動畫設計師而言,Silverlight提供了Flash以外的一個畫布,令她們可盡情揮灑創意!但對於設計師而言,Silverlight如何結合資料庫來呈現資料則是更具吸引力的課題,很明顯的!Silverlight 1.0 RC與Silverlight 1.1並不支援Data Binding機制,而且目前對於中文顯示仍存在著問題(下載字型不算是個解法,因為有法律問題),不過如果這個問題獲得解決,Silverlight結合資料庫後所呈現的效果,相信會給客戶完全不同的網頁操作感受,當然!前提得要Silverlight支援Data Bindings,否則一切都是紙上談兵罷了。要令Silverlight支援Data Bindings說來也不難,只要處理幾個關鍵問題即可。一、資料提供者為何?ASP.NET Ajax的PageMethods/Web Service可以勝任此工作,如下列程式片段所示。

private
static
DataTable BuildDataCache()

{


if (HttpRuntime.Cache["DataCache_Employees"] != null)


return
HttpRuntime.Cache["DataCache_Employees"] as
DataTable;


else

{


using (SqlConnection conn = new
SqlConnection(ConfigurationManager.ConnectionStrings[

"NorthwindConnectionString"].ConnectionString))

{


SqlDataAdapter adapter = new
SqlDataAdapter(

"SELECT * FROM Employees ORDER BY EmployeeID", conn);


DataTable table = new
DataTable("Employees");

adapter.Fill(table);


HttpRuntime.Cache["DataCache_Employees"] = table;


return table;

}

}

}


[WebMethod]


public
static
Employee GetData(int index)

{


DataTable table = BuildDataCache();


return
new
Employee(table.DefaultView[index]);

}


[WebMethod]


public
static
int GetCount()

{


DataTable table = BuildDataCache();


return table.DefaultView.Count;

}



protected
void Button1_Click(object sender, EventArgs e)

{

}

}


[Serializable]

public
class
Employee

{

[NonSerialized]


private
DataRowView _rv = null;



public
string EmployeeID

{


get

{


return (_rv.Row.IsNull("EmployeeID") ? string.Empty : _rv["EmployeeID"].ToString());

}

}



public
string LastName

{


get

{


return (_rv.Row.IsNull("LastName") ? string.Empty : (string)_rv["LastName"]);

}

}



public
string FirstName

{


get

{


return (_rv.Row.IsNull("FirstName") ? string.Empty : (string)_rv["FirstName"]);

}

}



public
string Title

{


get

{


return (_rv.Row.IsNull("Title") ? string.Empty : (string)_rv["Title"]);

}

}



public
string HireDate

{


get

{


return (_rv.Row.IsNull("HireDate") ?

string.Empty : ((DateTime)_rv["HireDate"]).ToShortDateString());

}

}



public Employee(DataRowView rv)

{

_rv = rv;

}

}

運用ASP.NET Ajax及JSON,我們可以輕易的用JavaScript來取得資料庫的資料,接著只要將資料設定給指定Silverlight控制項即可,這個工作可以簡單化也可以複雜化,簡單的作法是直接在JavaScript以findName來取得控制項,然後一一設定其屬性值。

handleLoad: function(plugIn, userContext, rootElement)

{

this.plugIn = plugIn;

this.dataContext = rootElement.children.getItem(0);

PageMethods.GetCount(this.OnSucceeded,this.OnFailed,this);

this.receiveData(this.currentDataIndex);

},

OnSucceeded: function(result, userContext, methodName)

{

if (methodName == "GetData")

{

userContext.displayData(result);

}

else if(methodName == "GetCount")

userContext.recordCount = result;

},

OnFailed:function(error, userContext, methodName)

{

if(error !== null)

{

alert(error.get_message());

}

},

displayData: function(data)

{

var idCtrl = this.dataContext.findName('txtEmployeeID');

var firstNameCtrl = this.dataContext.findName('txtLastName');

var lastNameCtrl = this.dataContext.findName('txtFirstName');

var titleCtrl = this.dataContext.findName('txtTitle');

var hireDateCtrl = this.dataContext.findName('txtHireDate');

var imgCtrl = this.dataContext.findName("imgPhoto");

idCtrl.text = data.EmployeeID;

firstNameCtrl.text = data.FirstName;

lastNameCtrl.text = data.LastName;

titleCtrl.text = data.Title;

hireDateCtrl.text = data.HireDate;

imgCtrl.source = "Default.aspx?ID="+idCtrl.text;

this.dataContext.findName("imgAnimation").begin();

}

這種做法的缺點是設計師得用JavaScript寫下許多程式碼。另一種做法是利用Silverlight控制項的Tag屬性,指定Binding Expression,然後於JavaScript中巡覽她們來一一指定值,如下列片段所示。

<Canvas
xmlns="http://schemas.microsoft.com/client/2007"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Canvas
Name="DataDemo"
Height="600"
Width="800"
Tag="BindingContext:GetData,GetCount">

<Canvas.Background>

<LinearGradientBrush>

<GradientStop
Color="Yellow"
Offset="0.0" />

<GradientStop
Color="Orange"
Offset="0.5" />

<GradientStop
Color="Red"
Offset="1.0" />

</LinearGradientBrush>

</Canvas.Background>

<TextBlock
Tag="BindingField:EmployeeID;BindingProperty:Text"
Name="txtEmployeeID"
Width="144"
Height="24"
Canvas.Left="166"
Canvas.Top="23"
Text="A00001"
TextWrapping="Wrap"/>

<TextBlock
Tag="BindingField:LastName;BindingProperty:Text"
Name="txtLastName"
Width="320"
Height="24"
Canvas.Left="500"
Canvas.Top="23"
Text="Alean Company"
TextWrapping="Wrap"/>

<TextBlock
Tag="BindingField:FirstName;BindingProperty:Text"
Name="txtFirstName"
Width="320"
Height="24"
Canvas.Left="166"
Canvas.Top="72"
Text="Jeffray"
TextWrapping="Wrap"/>

<TextBlock
Tag="BindingField:Title;BindingProperty:Text"
Name="txtTitle"
Width="576"
Height="24"
Canvas.Left="166"
Canvas.Top="122"
Text="Taipen 101"
TextWrapping="Wrap"/>

<TextBlock
Tag="BindingField:HireDate;BindingProperty:Text"
Name="txtHireDate"
Width="576"
Height="24"
Canvas.Left="166"
Canvas.Top="171"
Text="2005/3/4"
TextWrapping="Wrap"/>

<Image
Name="imgPhoto"
Tag="BindingField:EmployeeID;BindingProperty:Source;Format:Default.aspx?ID={0}"
Width="357"
Height="206"
Canvas.Left="400"
Canvas.Top="301">

<Image.Triggers>

<EventTrigger
RoutedEvent="Image.Loaded">

<BeginStoryboard>

<Storyboard
Name="imgAnimation">

<DoubleAnimation

Storyboard.TargetName="imgPhoto"

Storyboard.TargetProperty="Opacity"

From="0.0"
To="1.0"
Duration="0:0:6"/>

</Storyboard>

</BeginStoryboard>

</EventTrigger>

</Image.Triggers>

</Image>

<TextBlock
Name="txtLabel1"
Width="114"
Height="24"
Canvas.Left="18"
Canvas.Top="23"
Text="Employee ID:"
TextWrapping="Wrap"/>

<TextBlock
Name="txtLabel1_Copy"
Width="120"
Height="24"
Canvas.Left="349"
Canvas.Top="23"
Text="Last Name:"
TextWrapping="Wrap"/>

<TextBlock
Name="txtLabel1_Copy1"
Width="130"
Height="24"
Canvas.Left="18"
Canvas.Top="72"
Text="First Name:"
TextWrapping="Wrap"/>

<TextBlock
Name="txtLabel1_Copy2"
Width="104"
Height="24"
Canvas.Left="18"
Canvas.Top="122"
Text="Title :"
TextWrapping="Wrap"/>

<TextBlock
Name="txtLabel1_Copy3"
Width="93"
Height="24"
Canvas.Left="18"
Canvas.Top="171"
Text="Hire Date:"
TextWrapping="Wrap"/>

</Canvas>

</Canvas>

那如何解析Tag的Binding Expression並指定值呢?答案是利用Silverlight的JavaScript支援,一一巡覽所有控制項,一一解析Tag,然後再指定值。

/////////////////////////////////////////////////////////////////////////

// Silverlight Data Binding Helper 0.1

/////////////////////////////////////////////////////////////////////////


if (!window.SilverlightBinding)

window.SilverlightBinding = {};


SilverlightBinding.BindingData = function(ctrl,bindingExpression)

{


var bindings = bindingExpression.split(';');


this.bindingComplete = false;


this.ctrl = ctrl;


for(var i = 0; i < bindings.length; i++)

{


var temp = bindings[i].split(':');


if(temp.length != 2)

{


this.bindingComplete = false;


return;

}


if(temp[0] == 'BindingField')


this.bindingField = temp[1];


else
if(temp[0] == 'BindingProperty')


this.bindingProperty = temp[1];


else
if(temp[0] == 'Format')


this.format = temp[1];

}


this.bindingComplete = true;

}


SilverlightBinding.BindingData.prototype =

{

updateValue : function(dataItem)

{


if(this.bindingComplete)

{


if(this.format)

eval('this.ctrl.'+this.bindingProperty+

" = this.format.replace('{0}',dataItem."+this.bindingField+');');


else

eval('this.ctrl.'+this.bindingProperty+' = dataItem.'+this.bindingField+';');

}

}

}


SilverlightBinding.BindingContext = function(bindingContainer)

{


var parseBinding = bindingContainer.tag.split(':');


this.bindingComplete = false;


this.bindingContainer = bindingContainer;


this.bindingControls = new Array();


this.currentDataIndex = 0;


this.recordCount = 0;


if(parseBinding.length == 2 && parseBinding[0] == "BindingContext")

{


var bindingMethods = parseBinding[1].split(',');


if(bindingMethods.length == 2)

{


this.bindingMethod = bindingMethods[0];


this.bindingCountMethod = bindingMethods[1];


this.bindingComplete = true;

}

}


if(!this.bindingComplete) alert('ERROR,Binding Failed.');

}


SilverlightBinding.BindingContext.prototype =

{

_childWorker : function(parent,parseParent)

{


if(parent.tag && parent.tag != '')

{


if(parseParent)

{


var bindingData = new SilverlightBinding.BindingData(parent,parent.tag);


if(bindingData.bindingComplete)

{


this.bindingControls.length++;


this.bindingControls[this.bindingControls.length-1] = bindingData;

}


else


delete bindingData;

}


try

{


var temp = parent.children;

}


catch(err)

{


return;

}


for(var i = 0; i < parent.children.count; i++)


this._childWorker(parent.children.getItem(i),true);

}

},

initialize:function()

{


this._childWorker(this.bindingContainer,false);


this._receiveCount();


this._receiveData(0);

},

OnSucceeded: function(result, userContext, methodName)

{


if (methodName == userContext.bindingMethod)

{


for(var i = 0; i < userContext.bindingControls.length; i++)

userContext.bindingControls[i].updateValue(result);

}


else
if(methodName == userContext.bindingCountMethod)

userContext.recordCount = result;

},

OnFailed:function(error, userContext, methodName)

{


if(error !== null)

{

alert(error.get_message());

}

},

_receiveData: function(index)

{

eval('PageMethods.'+this.bindingMethod+

'(index,this.OnSucceeded,this.OnFailed,this);');

},

_receiveCount: function()

{

eval('PageMethods.'+this.bindingCountMethod+

'(this.OnSucceeded,this.OnFailed,this);');

},

next:function()

{


if(this.currentDataIndex+1 >= this.recordCount)


return;


this._receiveData(++this.currentDataIndex);

},

prev:function()

{


if(this.currentDataIndex -1 < 0)


return;


this._receiveData(--this.currentDataIndex);

}

}

使用此JavaScript物件的方法很簡單,只要於handleLoad中建立此物件,然後將Silverlight的根控制項傳入即可。

handleLoad: function(plugIn, userContext, rootElement)

{

this.plugIn = plugIn;

this.dataContext = rootElement.children.getItem(0);

this.bindingContext =

new SilverlightBinding.BindingContext(rootElement.children.getItem(0));

this.bindingContext.initialize();

}

切換上下筆時也很簡單。

function Next()

{

window.Silverlight.current_DataContext.bindingContext.next();

window.Silverlight.current_DataContext.dataContext.findName("imgAnimation").begin();

}


function Prev()

{

window.Silverlight.current_DataContext.bindingContext.prev();

window.Silverlight.current_DataContext.dataContext.findName("imgAnimation").begin();

}

下圖為執行畫面。


看不到炫麗效果啊?哈~~~我承認,我的美工細胞粉差!


本例下載位址:

http://www.dreams.idv.tw/~code6421/files/SLDataDemo1.zip

No comments: