Silverlight DataBindings for 1.1 (Managed code)
文
/
黃忠成
RC1方興未艾,
RC2已在路上了,看來1.0 Release之日不遠了!前面一篇文章利用了PageMethods與JavaScript為Silverlight 1.0RC加上DataBindings的功能,此次舞台換到了Silverlight 1.1 Alpha Refresh及Visual Studio 2008 Beta 2上,與1.1時不同,這次已無法用單一的Web Site模式實作,基於ASP.NET Ajax與Silverlight所使用的CLR Runtime不同,我們必須將Silverlight與ASP.NET Ajax拆開,別誤會!這並非意味你無法將Silverlight與ASP.NET Ajax放在同一個虛擬目錄下,Silverlight 1.1使用的Binary目錄是ClientBin,ASP.NET是Bin,兩者並無衝突,限制只在於你必須將Silverlight與ASP.NET Ajax分成兩個Project來編譯,在Silverlight編譯完成後將.xaml、.js複製到ASP.NET Ajax的專案目錄下,再將.dll複製到ASP.NET Ajax的ClientBin目錄下即可。回到主題,在Silverlight 1.1中,實現Data Bindings除了可以用前一篇文章的JavaScript技巧外,還多了一個選擇,那就是使用C#等Managed的語言,SLDH.js的C#版本如下。
SLDH.cs
/////////////////////////////////////////////////////////////////////////
// Silverlight Data Binding Helper 0.1 for Silverlight 1.1 Alpha Refresh
/////////////////////////////////////////////////////////////////////////
using
System;
using
System.Text;
using
System.IO;
using
System.Net;
using
System.Reflection;
using
System.Linq;
using
System.Collections.Generic;
using
System.Windows;
using
System.Windows.Controls;
using
System.Windows.Documents;
using
System.Windows.Ink;
using
System.Windows.Input;
using
System.Windows.Media;
using
System.Windows.Media.Animation;
using
System.Windows.Shapes;
namespace
SilverlightDataHelper
{
public class JSONDaTarow
{
private List<object> _columns;
private List<object> _values;
public object this[int index]
{
get
{
return _values[index];
}
}
public object this[string name]
{
get
{
for (int i = 0; i < _columns.Count; i++)
{
if (((string)_columns[i]).Equals(name))
return _values[i];
}
return null;
}
}
internal JSONDaTarow(object[] columns,object[] values)
{
_columns = new List<object>(columns);
_values = new List<object>(values);
}
}
public class BindingData
{
private bool _bindingComplete = false;
private string _bindingProperty = string.Empty;
private string _bindingField = string.Empty;
private FrameworkElement _control;
private string _format = string.Empty;
private PropertyInfo _cachedProp = null;
public bool BindingComplete
{
get
{
return this._bindingComplete;
}
}
public string BindingProperty
{
get
{
return _bindingProperty;
}
}
public string BindingField
{
get
{
return _bindingField;
}
}
public FrameworkElement Control
{
get
{
return _control;
}
}
public string Format
{
get
{
return _format;
}
set
{
_format = value;
}
}
public void UpdateValue(JSONDaTarow dataItem)
{
if (_bindingComplete)
{
if (_cachedProp == null)
{
_cachedProp = _control.GetType().GetProperty(_bindingProperty);
if (_cachedProp == null)
_bindingComplete = false;
}
if (_cachedProp != null && Format != string.Empty)
{
if (_cachedProp.PropertyType == typeof(Uri))
{
Uri uri = new Uri(string.Format(Format,
dataItem[BindingField]),UriKind.Relative);
_cachedProp.SetValue(_control,uri,null);
}
else
_cachedProp.SetValue(_control,string.Format(Format,
dataItem[BindingField]),null);
}
else if (_cachedProp != null)
_cachedProp.SetValue(_control,dataItem[BindingField],null);
}
}
public BindingData(FrameworkElement ctrl,string bindingExpression)
{
string[] bindings = bindingExpression.Split(';');
_bindingComplete = false;
_control = ctrl;
for (int i = 0; i < bindings.Length; i++)
{
string[] temp = bindings[i].Split(':');
if (temp.Length != 2)
{
_bindingComplete = false;
return;
}
if (temp[0].ToLower() == "bindingfield")
_bindingField = temp[1];
else if (temp[0].ToLower() == "bindingproperty")
_bindingProperty = temp[1];
else if (temp[0].ToLower() == "format")
_format = temp[1];
}
if (_bindingField != string.Empty &&
_bindingProperty != string.Empty)
_bindingComplete = true;
}
}
public class BindingContext
{
private Panel _container;
private List<BindingData> _bindingControls = null;
private bool _bindingComplete = false;
private int _position = 0;
private int _count = -1;
private string _serviceUrl = string.Empty;
private string _bindingMethod = string.Empty;
private string _bindingCountMethod = string.Empty;
private string _countMethod = string.Empty;
public int Position
{
get
{
return _position;
}
set
{
if (_position != value && value < Count && value >= 0)
{
_position = value;
UpdateBinding();
}
}
}
public int Count
{
get
{
return _count;
}
}
public bool BindingComplete
{
get
{
return _bindingComplete;
}
}
public List<BindingData> BindingControls
{
get
{
if (_bindingControls == null)
_bindingControls = new List<BindingData>();
return _bindingControls;
}
}
private void ChildWorker(FrameworkElement elem)
{
string expRSSsion = elem.Tag == null ? string.Empty : elem.Tag;
BindingData data = new BindingData(elem,expRSSsion);
if (data.BindingComplete)
BindingControls.Add(data);
if (elem is Panel)
{
Panel pnl = (Panel)elem;
for (int i = 0; i < pnl.Children.Count; i++)
{
if (pnl.Children[i] is FrameworkElement)
ChildWorker((FrameworkElement)pnl.Children[i]);
}
}
}
private void FetchCount()
{
browserHttpWebRequest request =
new browserHttpWebRequest(new Uri(_serviceUrl + "/" +
_bindingCountMethod,UriKind.Relative));
request.ContentType = "application/json; charset=utf-8";
request.Method = "POST";
request.ContentLength = 0;
request.Accept = "/*/";
HttpWebResponse response = request.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream());
JavaScriptSerializer serializer = new JavaScriptSerializer();
string data = sr.ReadToEnd();
_count = serializer.Deserialize<int>(data);
sr.Close();
response.Close();
request.Close();
}
private JSONDaTarow FetchData(int index)
{
browserHttpWebRequest request =
new browserHttpWebRequest(new Uri(_serviceUrl + "/" +
_bindingMethod,UriKind.Relative));
JavaScriptSerializer serializer = new JavaScriptSerializer();
request.ContentType = "application/json; charset=utf-8";
request.Method = "POST";
request.Accept = "/*/";
Stream reqStream = request.GetRequestStream();
byte[] buff = Encoding.UTF8.GetBytes("{index:" + index.ToString() + "}");
reqStream.Write(buff,buff.Length);
request.ContentLength = buff.Length;
HttpWebResponse response = request.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream());
string data = sr.ReadToEnd();
sr.Close();
response.Close();
request.Close();
object[] parsedData = serializer.Deserialize<object[]>(data);
return new JSONDaTarow((object[])parsedData[0],(object[])parsedData[1]);
}
private void UpdateBinding()
{
JSONDaTarow row = FetchData(Position);
foreach (BindingData item in BindingControls)
item.UpdateValue(row);
}
public void Initialize()
{
ChildWorker(_container);
FetchCount();
UpdateBinding();
}
public BindingContext(Panel container)
{
_container = container;
if (_container.Tag == null)
{
_bindingComplete = false;
return;
}
string[] parseBinding = container.Tag.Split(':');
_bindingComplete = false;
if (parseBinding.Length == 2 && parseBinding[0].ToLower() == "bindingcontext")
{
string[] bindingMethods = parseBinding[1].Split(',');
if (bindingMethods.Length == 3)
{
_serviceUrl = bindingMethods[0];
_bindingMethod = bindingMethods[1];
_bindingCountMethod = bindingMethods[2];
_bindingComplete = true;
}
}
if (_bindingComplete)
ChildWorker(container);
}
}
}
|
ㄟ...程式碼變長了哦~~~ >"<,在某些情況下,Managed Code不見得比JavaScript簡單吧!只是別忘了,這些程式碼是預先編譯後再下載到客戶端,由Silverlight CLR執行的,就理論上來說,執行效率應該比JavaScript好才對。由於Managed SLDH使用了另一種JSON格式來交換資料,所以.aspx.cs也要做一些調整。
Default.aspx.cs
using
System;
using
System.IO;
using
System.Data;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
System.Web.UI.WebControls.WebParts;
using
System.Web.UI.HtmlControls;
using
System.Xml.Linq;
using
System.Web.Services;
public
partial class _Default : System.Web.UI.Page
{
private static DataTable BuildDataCache()
{
if (HttpRuntime.Cache["DataCache_Employees"] != null)
return HttpRuntime.Cache["DataCache_Employees"] as DataTable;
else
{
ConnectionString"
].ConnectionString))
{
"SELECT * FROM Employees ORDER BY EmployeeID"
,conn);
DataTable table = new DataTable("Employees");
adapter.Fill(table);
HttpRuntime.Cache["DataCache_Employees"] = table;
return table;
}
}
}
private static List<object> BuildJSONRow(DaTarow row)
{
List<object> result = new List<object>();
List<string> columns = new List<string>();
List<object> values = new List<object>();
foreach (DataColumn col in row.Table.Columns)
{
columns.Add(col.ColumnName);
values.Add(row.IsNull(col) ? string.Empty : row[col].ToString());
}
result.Add(columns);
result.Add(values);
return result;
}
[WebMethod]
public static List<object> GetData(int index)
{
DataTable table = BuildDataCache();
return BuildJSONRow(table.defaultview[index].Row);
}
[WebMethod]
public static int GetCount()
{
DataTable table = BuildDataCache();
return table.defaultview.Count;
}
protected void Page_Load(object sender,EventArgs e)
{
if (Request.QueryString["ID"] != null &&
Request.QueryString["ID"].Length > 0)
{
"ConnectionString"
].ConnectionString))
{
conn.open();
"SELECT Photo FROM Employees WHERE EmployeeID = @ID"
,conn);
cmd.Parameters.AddWithValue("@ID",Request.QueryString["ID"]);
object data = cmd.ExecuteScalar();
if (data != null && ((byte[])data).Length > 0)
{
Response.Clear();
Response.BufferOutput = true;
Response.ContentType = "image/jpeg";
MemoryStream ms = new MemoryStream();
ms.Write(((byte[])data),78,((byte[])data).Length - 78);
MemoryStream jpegms = new MemoryStream();
System.Drawing.Image.FromStream(ms).Save(jpegms,
System.Drawing.Imaging.ImageFormat.Jpeg);
jpegms.Position = 0;
Response.OutputStream.Write(jpegms.GetBuffer(),(int)jpegms.Length);
ms.dispose();
jpegms.dispose();
Response.Flush();
Response.End();
}
}
}
}
}
|
當需要做DataBindings時,只需要在.xaml.cs的Page_Loaded事件處理函式中建立此物件即可,見下面程式碼。
Page.xaml
<
Canvas
x:Name
=
"parentCanvas"
xmlns
=
"http://schemas.microsoft.com/client/2007"
xmlns:x
=
"http://schemas.microsoft.com/winfx/2006/xaml"
Loaded
=
"Page_Loaded"
x:Class
=
"SilverlightProject1.Page;assembly=ClientBin/SilverlightProject1.dll"
Width
=
"640"
Height
=
"480"
Background
=
"White"
>
<
Canvas
Name
=
"DataDemo"Height="600"Width="800"
Tag
=
"BindingContext:Default.aspx,GetData,GetCount">
<
Canvas.Background
>
<
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"
<
TextBlock
Tag
=
"BindingField:LastName;BindingProperty:Text"
Name
=
"txtLastName"Width="320"Height="24"Canvas.Left="500"
Width
=
"320"Height="24"Canvas.Left="166"Canvas.Top="72"
<
TextBlock
Tag
=
"BindingField:Title;BindingProperty:Text"
Name
=
"txtTitle"Width="576"Height="24"Canvas.Left="166"
<
TextBlock
Tag
=
"BindingField:HireDate;BindingProperty:Text"Name="txtHireDate"
Width
=
"576"Height="24"Canvas.Left="166"Canvas.Top="171"
<
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>
<
TextBlock
Canvas.Left
=
"100"Canvas.Top="200"
Text
=
"Prev"MouseLeftButtonDown="OnPrevClick"/>
<
TextBlock
Canvas.Left
=
"150"Canvas.Top="200"
Text
=
"Next"MouseLeftButtonDown="OnNextClick"/>
Canvas>
|
Page.xaml.cs
using
System;
using
System.Linq;
using
System.Collections.Generic;
using
System.Windows;
using
System.Windows.Controls;
using
System.Windows.Documents;
using
System.Windows.Ink;
using
System.Windows.Input;
using
System.Windows.Media;
using
System.Windows.Media.Animation;
using
System.Windows.Shapes;
namespace
SilverlightProject1
{
public partial class Page : Canvas
{
private SilverlightDataHelper.BindingContext _context = null;
public void Page_Loaded(object o,EventArgs e)
{
// required to initialize variables
InitializeComponent();
_context = new SilverlightDataHelper.BindingContext(FindName("DataDemo") as Panel);
_context.Initialize();
}
void OnPrevClick(object sender,EventArgs args)
{
if (_context.Position > 0)
{
_context.Position--;
((Storyboard)FindName("imgAnimation")).Begin();
}
}
void OnNextClick(object sender,EventArgs args)
{
if (_context.Position < _context.Count)
{
_context.Position++;
((Storyboard)FindName("imgAnimation")).Begin();
}
}
}
}
|
下圖是執行畫面。