摘要:另類的Silverlight中文解法
另類的Silverlight中文解法
 文/黃忠成
   如你所知,由於字型的關係,Silverlight目前對於雙位元文字顯示可說是困難重重,大概可歸類出兩種解法,一種是下載字型到客戶端,一種是利用Blend2將文字變成圖形,下載字型目前有法律問題的隱憂,而且一個中文字型檔大小約5MB,仍嫌過大。在上次嘗試實作DataBinding功能後,心中就有一種想法,假如於Server端將文字轉成圖形後下載到客戶端,那麼因為不是直接下載字型,所以應無法律問題(這我不確定!),也不會因下載整個字型檔而導致網頁開啟速度過慢,這樣是否就能讓前次的Data Bindigns例子支援中文的顯示呢?可惜前幾天因陪老婆考試,一直沒時間來實現心中的構想,這幾天終於有時間來實現這個架構了。基本上,實現這個架構有兩個問題必須先行解決,第一個問題是Server端如何將文字變成圖形?這不難,下列的程式便可辦到。
 protected void Page_Load(object sender, EventArgs e)              {                      if (Request.QueryString["ID"] != null &&                         Request.QueryString["ID"].Length > 0)                      {                        ..................                      }                      else if (Request.QueryString["Transform"] != null)                      {                          int index;                          string column;                          ResolveParams(Request.QueryString["Transform"],               out index, out column);                          if (index != -1)                          {                              Employee data = GetData(index);                              MemoryStream ms = ResolveTransform(data, column);                              if (ms != null)                              {                                  Response.Clear();                                  Response.BufferOutput = true;                                  Response.ContentType = "image/bmp";                                  Response.OutputStream.Write(ms.GetBuffer(), 0, (int)ms.Length);                                  ms.Dispose();                                  Response.Flush();                                  Response.End();                              }                              else                                  Response.End();                          }                      }                  }                  private void ResolveParams(string transformParams, out int index, out string column)                  {                      string[] p = Request.QueryString["Transform"].Split('*');                      index = -1;                      column = string.Empty;                      if (p.Length == 2)                      {                          index = int.Parse(p[0]);                          column = p[1];                      }                          }                  private MemoryStream ResolveTransform(object data, string column)                  {                              PropertyInfo pi = data.GetType().GetProperty(column);                      if (pi != null)                          return GetDBCSJPEStream(pi.GetValue(data,null).ToString(),               new Font("PMingLiU", 11,FontStyle.Bold), Color.Black, Color.White, 280,24);                      return null;                  }                  private MemoryStream GetDBCSJPEStream(string str, Font font, Color foreColor,               Color background, int width, int height)                  {                      Bitmap bmp = new Bitmap(width, height);                              Graphics g = Graphics.FromImage(bmp);                      Brush bFore = new SolidBrush(foreColor);                      Brush bBack = new SolidBrush(background);                      g.Clear(Color.Transparent);                      g.DrawString(str, font, bFore, 2, 2);                      bFore.Dispose();                      bBack.Dispose();                      MemoryStream ms = new MemoryStream();                      bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);                      bmp.Dispose();                      ms.Position = 0;                      return ms;                  }  |          
如果你仔細看上面的程式,會發覺其中有字體預設的問題,若要以此觀念實作一個完整的架構,關於字型的資訊應於XAML中指定才好,不過目前我只是展示這個想法的可行性,就先放著這部份不理了。第二個要解決的問題是如何於XAML中指定Data Bindings資訊,這點不難,依據前版以TAG來指定Binding Expression的概念,只需加油添醋一番,即可套用。
 <Canvasxmlns="http://schemas.microsoft.com/client/2007"              ...................              <ImageName="imgLastName"Tag="BindingField:LastName;BindingProperty:Source;Format:Default.aspx?Transform={INDEX}*{BindingField}"Width="300"Height="24"Canvas.Left="500"Canvas.Top="23" />              </Canvas>  |          
請注意,由於採圖形方式的緣故,這裡已不再使用TextBlock,而是使用Image控制項來顯示,SLDH.js也需稍做修改。
 /////////////////////////////////////////////////////////////////////////              // Silverlight Data Binding Helper 0.1              /////////////////////////////////////////////////////////////////////////              if (!window.SilverlightBinding)                         window.SilverlightBinding = {};              SilverlightBinding.BindingData = function(ctrl,bindingExpression,context)              {                 var bindings = bindingExpression.split(';');                 this.bindingComplete = false;                 this.ctrl = ctrl;                 this.context = context;                 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)                      {                         var str = eval("this.format.replace('{0}',dataItem."+this.bindingField+');');                                                          if(this.format.indexOf("INDEX") != -1)                            str = str.replace("{INDEX}",this.context.currentDataIndex);                         if(this.format.indexOf("BindingField") != -1)                            str = str.replace("{BindingField}",this.bindingField);                         eval('this.ctrl.'+this.bindingProperty+" = str;");                      }                      else                         eval('this.ctrl.'+this.bindingProperty+' = dataItem.'+this.bindingField+';');                    }                 }              }              SilverlightBinding.BindingContext = function(bindingContainer)              {                 var parseBindings = bindingContainer.tag.split(';');                 this.bindingComplete = false;                 this.bindingContainer = bindingContainer;                 this.bindingControls = new Array();                 this.currentDataIndex = 0;                 this.recordCount = 0;                 for(var i = 0; i < parseBindings.length; i++)                 {                   var parseBinding = parseBindings[i].split(':');                   if(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,this);                        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);                  }              }  |          
下圖為執行例。
 
後記
  這種方式僅是一個應急的解法,畢竟傳輸圖形也是需要時間的,自然不比直接使用位於客戶端的字型來的有效率。
 示例下載:
 http://www.dreams.idv.tw/~code6421/files/SLDataDemo2.zip