Wednesday, August 29, 2007
Friday, August 17, 2007
另類的Silverlight中文解法
另類的Silverlight中文解法
文/黃忠成
如你所知,由於字型的關係,Silverlight目前對於雙位元文字顯示可說是困難重重,大概可歸類出兩種解法,一種是下載字型到客戶端,一種是利用Blend2將文字變成圖形,下載字型目前有法律問題的隱憂,而且一個中文字型檔大小約5MB,仍嫌過大。在上次嘗試實作DataBinding功能後,心中就有一種想法,假如於Server端將文字轉成圖形後下載到客戶端,那麼因為不是直接下載字型,所以應無法律問題(這我不確定!),也不會因下載整個字型檔而導致網頁開啟速度過慢,這樣是否就能讓前次的Data Bindigns例子支援中文的顯示呢?可惜前幾天因陪老婆考試,一直沒時間來實現心中的構想,這幾天終於有時間來實現這個架構了。基本上,實現這個架構有兩個問題必須先行解決,第一個問題是Server端如何將文字變成圖形?這不難,下列的程式便可辦到。
protected {
Request.QueryString["ID"].Length > 0) { .................. }
{
ResolveParams(Request.QueryString["Transform"], out index, out column);
{
{ Response.Clear(); Response.BufferOutput = true; Response.ContentType = "image/bmp"; Response.OutputStream.Write(ms.GetBuffer(), 0, (int)ms.Length); ms.Dispose(); Response.Flush(); Response.End(); }
Response.End(); } } }
{
index = -1; column = string.Empty;
{ index = int.Parse(p[0]); column = p[1]; } }
{
new
}
Color background, int width, int height) {
g.Clear(Color.Transparent); g.DrawString(str, font, bFore, 2, 2); bFore.Dispose(); bBack.Dispose();
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); bmp.Dispose(); ms.Position = 0;
} |
如果你仔細看上面的程式,會發覺其中有字體預設的問題,若要以此觀念實作一個完整的架構,關於字型的資訊應於XAML中指定才好,不過目前我只是展示這個想法的可行性,就先放著這部份不理了。第二個要解決的問題是如何於XAML中指定Data Bindings資訊,這點不難,依據前版以TAG來指定Binding Expression的概念,只需加油添醋一番,即可套用。
<Canvas ................... <Image </Canvas> |
請注意,由於採圖形方式的緣故,這裡已不再使用TextBlock,而是使用Image控制項來顯示,SLDH.js也需稍做修改。
///////////////////////////////////////////////////////////////////////// // Silverlight Data Binding Helper 0.1 ///////////////////////////////////////////////////////////////////////// if (!window.SilverlightBinding) window.SilverlightBinding = {}; SilverlightBinding.BindingData = function(ctrl,bindingExpression,context) {
{
{
}
}
} SilverlightBinding.BindingData.prototype = { updateValue : function(dataItem) {
{
{
str = str.replace("{INDEX}",this.context.currentDataIndex);
str = str.replace("{BindingField}",this.bindingField); eval('this.ctrl.'+this.bindingProperty+" = str;"); }
eval('this.ctrl.'+this.bindingProperty+' = dataItem.'+this.bindingField+';'); } } } SilverlightBinding.BindingContext = function(bindingContainer) {
{
{
{
} } }
} SilverlightBinding.BindingContext.prototype = { _childWorker : function(parent,parseParent) {
{
{
{
}
}
{
}
{
}
} }, initialize:function() {
}, OnSucceeded: function(result, userContext, methodName) {
{
userContext.bindingControls[i].updateValue(result); }
userContext.recordCount = result; }, OnFailed:function(error, userContext, methodName) {
{ 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() {
}, prev:function() {
} } |
下圖為執行例。
後記
這種方式僅是一個應急的解法,畢竟傳輸圖形也是需要時間的,自然不比直接使用位於客戶端的字型來的有效率。
示例下載:
http://www.dreams.idv.tw/~code6421/files/SLDataDemo2.zip
Silverlight DataBindings for 1.1 (Managed code)
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; using System.Windows.Browser.Net; using System.Windows.Browser.Serialization; namespace SilverlightDataHelper {
{
{
{
} }
{
{
{
}
} }
{ _columns = new _values = new } }
{
{
{
} }
{
{
} }
{
{
} }
{
{
} }
{
{
}
{ _format = value; } }
{
{
{ _cachedProp = _control.GetType().GetProperty(_bindingProperty);
_bindingComplete = false; }
{
{
dataItem[BindingField]), UriKind.Relative); _cachedProp.SetValue(_control, uri, null); }
_cachedProp.SetValue(_control, string.Format(Format, dataItem[BindingField]), null); }
_cachedProp.SetValue(_control, dataItem[BindingField], null); } }
{
_bindingComplete = false; _control = ctrl;
{
{ _bindingComplete = false;
}
_bindingField = temp[1];
_bindingProperty = temp[1];
_format = temp[1]; }
_bindingProperty != string.Empty) _bindingComplete = true; } }
{
{
{
}
{
{ _position = value; UpdateBinding(); } } }
{
{
} }
{
{
} }
{
{
_bindingControls = new
} }
{
BindingControls.Add(data);
{ Panel pnl = (Panel)elem;
{
ChildWorker((FrameworkElement)pnl.Children[i]); } } }
{ BrowserHttpWebRequest request =
_bindingCountMethod, UriKind.Relative)); request.ContentType = "application/json; charset=utf-8"; request.Method = "POST"; request.ContentLength = 0; request.Referer = System.Windows.Browser.HtmlPage.DocumentUri.AbsolutePath; request.Accept = "/*/";
JavaScriptSerializer serializer = new JavaScriptSerializer();
_count = serializer.Deserialize<int>(data); sr.Close(); response.Close(); request.Close(); }
{ BrowserHttpWebRequest request =
_bindingMethod, UriKind.Relative)); JavaScriptSerializer serializer = new JavaScriptSerializer(); request.ContentType = "application/json; charset=utf-8"; request.Method = "POST"; request.Referer = System.Windows.Browser.HtmlPage.DocumentUri.AbsolutePath; request.Accept = "/*/";
reqStream.Write(buff, 0, buff.Length); request.ContentLength = buff.Length;
sr.Close(); response.Close(); request.Close();
}
{
item.UpdateValue(row); }
{ ChildWorker(_container); FetchCount(); UpdateBinding(); }
{ _container = container;
{ _bindingComplete = false;
}
_bindingComplete = false;
{
{ _serviceUrl = bindingMethods[0]; _bindingMethod = bindingMethods[1]; _bindingCountMethod = bindingMethods[2]; _bindingComplete = true; } }
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.Data.SqlClient; using System.Configuration; using System.Linq; using System.Web; using System.Web.Security; 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 {
{
{
ConnectionString"].ConnectionString)) {
"SELECT * FROM Employees ORDER BY EmployeeID", conn);
adapter.Fill(table);
} } }
{
{ columns.Add(col.ColumnName); values.Add(row.IsNull(col) ? string.Empty : row[col].ToString()); } result.Add(columns); result.Add(values);
} [WebMethod]
{
} [WebMethod]
{
}
{
Request.QueryString["ID"].Length > 0) {
"ConnectionString"].ConnectionString)) { conn.Open();
"SELECT Photo FROM Employees WHERE EmployeeID = @ID", conn); cmd.Parameters.AddWithValue("@ID", Request.QueryString["ID"]);
{ Response.Clear(); Response.BufferOutput = true; Response.ContentType = "image/jpeg";
ms.Write(((byte[])data), 78, ((byte[])data).Length - 78);
System.Drawing.Image.FromStream(ms).Save(jpegms, System.Drawing.Imaging.ImageFormat.Jpeg); jpegms.Position = 0; Response.OutputStream.Write(jpegms.GetBuffer(), 0, (int)jpegms.Length); ms.Dispose(); jpegms.Dispose(); Response.Flush(); Response.End(); } } } } } |
當需要做DataBindings時,只需要在.xaml.cs的Page_Loaded事件處理函式中建立此物件即可,見下面程式碼。
Page.xaml
<Canvas
> <Canvas
<Canvas.Background> <LinearGradientBrush> <GradientStop <GradientStop <GradientStop </LinearGradientBrush> </Canvas.Background> <TextBlock Name="txtEmployeeID" Canvas.Top="23" <TextBlock Name="txtLastName" Canvas.Top="23" <TextBlock Width="320" Text="Jeffray" <TextBlock Name="txtTitle" Canvas.Top="122" <TextBlock Width="576" Text="2005/3/4" <Image Tag="BindingField:EmployeeID;BindingProperty:Source;Format:Default.aspx?ID={0}" Width="357" <Image.Triggers> <EventTrigger <BeginStoryboard> <Storyboard <DoubleAnimation Storyboard.TargetName="imgPhoto" Storyboard.TargetProperty="Opacity" From="0.0" </Storyboard> </BeginStoryboard> </EventTrigger> </Image.Triggers> </Image> <TextBlock Canvas.Top="23" <TextBlock Canvas.Top="23" <TextBlock Canvas.Top="72" <TextBlock Canvas.Top="122" <TextBlock Canvas.Top="171" </Canvas> <TextBlock Text="Prev" <TextBlock Text="Next" </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 {
{
{
InitializeComponent(); _context = new SilverlightDataHelper.BindingContext(FindName("DataDemo") as Panel); _context.Initialize(); }
{
{ _context.Position--; ((Storyboard)FindName("imgAnimation")).Begin(); } }
{
{ _context.Position++; ((Storyboard)FindName("imgAnimation")).Begin(); } } } } |
下圖是執行畫面。
http://www.dreams.idv.tw/~code6421/files/SLDataDemo_11.zip
(你需要將Northwind.mdf、Northwind_log.ldf複製到App_Data目錄下,或是修改web.config中的ConnectionString來連結到北風資料庫)