November 2008 - 文章

37"的奮發

新買了37" LCD電視,但有為中年人每天擠出來可以在家看電視的時間有限,不過為了體驗FullHD的最高解析度,還是架設了臨時的電腦資訊源,用VGA連線先嚐一下1920x1080的滋味。(最高解析度下畫面線條界限有些模糊,感覺上一點有時會被切分成LCD上的兩點,很像1280x1024 LCD模擬1024x768時的效果,看來應該還是要花錢買條DVI傳HDMI的線,有專家要提供意見嗎?)

昨天,發現了37" LCD TV的新用途!!

37"又大又寬的螢幕,用來看PDC 2008的寬螢幕課程錄影剛剛好,在視野雄偉、氣勢磅薄的優渥裡,中年人再次踏上刻苦奮發之路...

PS: 回答上次nowhereman問的問題,依據箱子上的標籤,我的BenQ SD3742是正港本土,2008年8月在台灣出生的。

WCF For Client Script

話說上回介紹了ASP.NET AJAX Client Templates,資料的來源需為Javascript物件陣列的形式,為了簡化起見,我是以Javascript [ { ... }, { ... } ]的方式Hard-Coding產生物件陣列。而在實務上,我們多半會另外撰寫一個Web Page、Web Service或WCF作為資料來源。

在微軟構築的藍圖中,WCF已內建Client Script支援功能,整合的緊密度讓人驚豔,算是建構AJAX後端的王道。更進一步,若資料來源更直接地映對到資料庫DataTable時,則ADO.NET Data Service還能提供更密切的整合,甚至還有前端的MicrosoftAjaxAdoNet.js提供一缸子的Javascript Function協助,寫起Code來更加得心應手。關於ADO.NET Data Service的部分留到之後再談,今天我們先將焦點集中在WCF上。

我們延續先前的範例,目標是要產生一個有Id, Name, Score, RecTime的人員資料陣列,但這回我們要用WCF來實作。

首先,用VS2008在ASP.NET專案中新增一個WCF Service,命名為PeopleSource.svc,VS2008會幫你產生PeopleSource.svc, App_Code\IPeopleSource.cs, App_Code\PeopleSource.cs。

接著,我們把它修改成我們要的樣子:

IPeopleSource.cs:

[ServiceContract(Namespace="Darkthread")]
public interface IPeopleSource
{
    [OperationContract]
    List<Person> GetPeople(bool hideScore);
}
 
[DataContract]
public class Person
{
    [DataMember]
    public string Id { get; set; }
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public double Score { get; set; }
    [DataMember]
    public DateTime RecTime { get; set; }
}

PeopleSource.cs: (記得要using System.ServiceModel.Activation)

[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(
    RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class PeopleSource : IPeopleSource
{
    public List<Person> GetPeople(bool hideScore)
    {
        List<Person> people = new List<Person>();
        people.Add(new Person { Id="A01", Name="Jeffrey", 
            Score=(hideScore) ? -1 : 32767, RecTime = DateTime.Now });
        people.Add(new Person { Id="A02", Name="Fox Mulder",  
            Score=(hideScore)?-1:6242.25, 
            RecTime = DateTime.ParseExact("2008-11-05 12:00:50", 
            "yyyy-MM-dd HH:mm:ss", null) });
        people.Add(new Person { Id="A03", Name="Dana Scully", 
            Score=(hideScore)?-1:8391.66, RecTime= new DateTime(2008, 6, 15) });
        return people;
 
    }
}

另外,web.config裡要做一些設定,好開啟內建WCF支援Client Script的功能,MSDN上有詳細的介紹,這裡只列出修改後的樣子:

  <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="default"></binding>
      </webHttpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="webScriptEnablingBehavior">
          <enableWebScript/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="PeopleSourceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    <services>
      <service behaviorConfiguration="PeopleSourceBehavior" name="PeopleSource">
        <endpoint address="" binding="webHttpBinding" 
                  contract="IPeopleSource"
                  bindingConfiguration="default"
                  behaviorConfiguration="webScriptEnablingBehavior">
        </endpoint>
      </service>
    </services>
  </system.serviceModel>

[Updated 2008-12-02] 以上的調整動作,在VS2008裡改建立AJAX-enabled WCF Service就可省略,請多利用。

接著,我們修改先前的ASPX,在ScriptManager中加入
<Services>
  <asp:ServiceReference Path="~/PeopleSource.svc" />
</Services>

神奇的事發生了,打入Darkthread(在IPeopleSource.cs裡宣告的ServiceContract Namespace),VS2008自動提示可用的Interface、Method名稱、呼叫參數,酷斃了!

我們將上回的ASPX內容稍作修改,主要是加入ServiceReference及呼叫Darkthread.IPeopleSource.GetPeople。WCF自動產生的Javascript端函數,第二參數onSuccess要宣告一個Function接收WCF傳回的結果。函數只有一個呼叫參數data,即為WCF端所return的物件。由於GetPeople宣告的傳回物件是List<Person>,在Javascript端,會透過JSON自動轉成有物件陣列,而物件有哪些屬性,則以先前在IPeopleSource.cs裡Person有宣告[DataMemeber]的屬性為準,也就是Id, Name, Score, RecTime。

如此一來,data跟上回Hard-Coding建出的物件陣列完全相同,我們直接dv.set_data(data)即可完成Client Side Binding,輕鬆愉快。

<asp:ScriptManager ID="ScriptManager1" runat="server">
  <Scripts>
    <asp:ScriptReference Path="~/js/jquery-1.2.6.js" />
    <asp:ScriptReference Path="~/js/MicrosoftAjaxTemplates.debug.js" />    
  </Scripts>
  <Services>
    <asp:ServiceReference Path="~/PeopleSource.svc" />
  </Services>
</asp:ScriptManager>
<script type="text/javascript">
    $(function() {
        var dv = $create(Sys.UI.DataView, {}, {}, {}, $get("tbTemplate"));
        Darkthread.IPeopleSource.GetPeople(false, function(data) {
            dv.set_data(data); 
            $("#tbTemplate").show();
        });
        
    });        
</script>
<table border="1" cellspacing="0" cellpadding="0"  style="font-size: 9pt; width: 300px;">
  <thead>
  <tr><th>Id</th><th>Name</th><th>Score</th><th>Record Time</th></tr>
  </thead>
  <tbody id="tbTemplate" style="display:none; padding: 3px;">
  <tr>
    <td style="text-align: center;">{{Id}}</td>
    <td>{{Name}}</td>
    <td style="text-align: right;">{{Score.format("N2")}}</td>
    <td style="text-align: center;">
    {{RecTime.format("yyyy/MM/dd HH:mm:ss")}}
    </td>    
  </tr>
  </tbody>            
</table>

執行網頁,我們得到與上回一模一樣的結果,不同的是,資料來源已改由WCF提供。

WCF函數傳回的List<T>可以當場轉成立即可用的Javascript Object Array,直接做為ASP.NET AJAX Client Templates的資料來源,非常簡便(尤其是VS2008還提供Javascript呼叫WCF時的Intellisense,讓人感動)。

講到這裡,有沒有聯想到什麼?

Yes! LINQ查詢的結果不就可以轉成List<T>? 大家知道可以怎麼玩了吧!

50萬人次紀念

小站點閱數突破50萬人次囉!!

原本昨天訂閱人數一度攻上506人,正計劃今天可以一併慶祝點閱數破50萬次K+訂閱數破500人--"雙破",沒想到虛胖只維持了一天,今天又跌回494人,殘念之餘,還是抓了FeedBurner的統計頁面留念一下,哈!

【成長歷程】

UpdatePanel招誰惹誰?

在前一篇文章裡,為ASP.NET AJAX Client Templates做了個開場,某些人心中或許已浮現幾絲驚恐,不斷吶喊--為什麼? 為什麼有人要對UpdatePanel趕盡殺絕? UpdatePanel用得好好的,為什麼忽然又要改朝換代學新東西? 沒了UpdatePanel,叫我們不會寫Javascript又被老闆要求搞什麼鬼AJAX的人去死嗎?

其實,不用過度驚慌,沒有一無是處的技術,只有用錯場合的白目。UpdatePanel並非萬惡不赦,只是在應用它之餘,也要通曉它的優點與罩門,儘可能找到可以展現優點的時機,避免會曝露罩門的場合。

UpdatePanel最大的賣點,應該是讓ASP.NET Developer可以在不懂Javascript及HTML端細節的前題下,靠著拖拉控件,用已熟悉的Server-Side Event就把原本死板的PostBack式網頁,瞬間升級成又酷又炫的AJAX風格網頁。瞧! 網頁上內容不斷翻新,螢幕都不會閃耶! 而且我兩分鐘就寫好了,你們這些用Javascript搞AJAX的,連更新資料的Web Service還沒動工哩~~

的確,UpdatePanel的架構簡單、邏輯集中(不需另外寫Web Page/Service供前端呼叫)、開發簡便快速,加上完全用Server-Side事件解決,只要會寫ASP.NET的開發者就能上手,又不易出錯,門檻頗低(依個人經驗,不會寫Javascript的ASP.NET開發者比比皆是!),優點多多。

這樣看來,UpdatePanel不是很完美嗎? 罩門在哪裡? 效能!!

UpdatePanel背後的原理,是用HttpRequest的方式執行PostBack,肥大的ViewState要照送,完整的WebPage處理流程也要全程跑完,差別是網頁只是會產生UpdatePanel裡面的HTML Code傳回。(換句話說,你要是整頁包在一個超大的UpdatePanel裡,傳的資料量就跟PostBack整個網頁相去不遠,因此要把握"UpdatePanel像比基尼,愈小愈好"的原則。)

讓我來示範一下。

我寫了一個網頁,放了一個鈕Button1用來更新Label1顯示時間,利用SqlDataSource將AdventureWorks資料庫的產品資料傳給GridView1並支援分頁顯示,當選取某項產品時,利用DetailsView1顯示產品明細,並顯示產品照片。

大部分的東西都用宣告做掉了,要寫的Code很少!

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
            Label1.Text = DateTime.Now.ToString("HH:mm:ss");
 
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToString("HH:mm:ss");
    }
 
    protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
    {
        DetailsView1.PageIndex = GridView1.SelectedRow.DataItemIndex;
    }

實際跑一下,分別按下Button1、GridView1翻到第2頁,並選取其中一個物項目。用HttpWatch Pro觀察,可以看到四次傳輸,下載資料量都是7KB近8KB,第二次後為PostBack,上傳資料傳輸量近3KB,主要是要傳送ViewState的緣故。

好,讓我們加上UpdatePanel,用一個UpdatePanel將Button1, Label1, GridView1, DetailsView1通通包起來,Server-Side的Code一行都不用改,馬上升級成AJAX動態更新,網頁閃動Bye-Bye,改寫過程之簡單,簡直像魔法。

再觀察一下資料傳輸量,我們一樣看載入、按Button1、換頁、看產品詳情四次傳輸。

首次載入時,網頁HTML約9KB,並額外下了三個js約90KB,但js部分未來可由Cache取得,不會每次重傳。但接下來的按Button1、換頁及檢視產品過程,雖然網頁不再閃動,傳輸的資料量為上傳2.4KB,下載7KB,跟PostBack十分相近。也就是說,雖然換上了AJAX的皮,骨子裡跟PostBack造成的網路及伺服器負擔是差不多的。

等等,剛才不是有某個色胚說什麼"UpdatePanel要像比基尼,愈小愈好"? 我們來把這件連身衣改成三點式好了! 分別用一個UpdatePanel(紅框)包住Button1及Label1,一個UpdatePanel(綠框)包住GridView1,一個UpdatePanel(藍框)包住DetailsView1,並將三個UpdatePanel.UpdateMode改為Conditional,分別由Button1.Click、GridView1.PageIndexChanged、GridView1.SelectedIndexChanged事件觸發:

拆成獨立UpdatePanel,讓我們可以每次更新一小塊,但由測試結果來看,每個動作上傳的2.5KB省不了,按Button1、換頁、檢視產品還是分別產生了2.5KB、5.8KB、3.4KB的資料下載量。最過分的是單單更新Label1.Text幾個字元也要傳回2.5KB,其中超過95%來自ViewState。

由以上的實驗來看,拆解出小而獨立的多個UpdatePanel,雖可以減少部分資料下載量,但每次更新,上傳跟下傳的ViewState終究還是省不了的。在上面的例子裡,即便UpdatePanel內空空如也,去2.5KB、回來2.5KB也得行禮如儀。同時,這種架構採行了全頁重新執行的概念,因此即便拆成多個UpdatePanel,同一時間內只允許一個UpdatePanel進行更新。

可以想見,若你的網頁會放在Internet上給成千上萬的使用者點閱,每次更新幾個字元就要來回5KB的資料量,頻率一高,對於頻寬就是沈重的負擔,何況處理這個大量的資料來回,對Client與Server都要耗費無謂的CPU、Memory、I/O資源,從機車一點的角度來看,亦不符合"節能減碳"的環保理念。在這種情境下,為求開發過程簡便快速、擔心開發者多學Javascript太過勞累就不再是重要的考量因素,如何用最少的資料傳輸量達成同樣的效果,才是王道,於是,像ASP.NET AJAX Client Templates這種主打輕巧的設計概念就應運而生了。

我們並不需完全抺煞UpdatePanel的價值。不可否認地,UpdatePanel的簡單易學、低技術門檻,仍是不可取代的,在對效能要求不高(尤其是LAN裡)、用量不大的情境下,拖拖拉拉寫個兩行就做出漂亮的AJAX網頁,有何不可? 只是,身為ASP.NET開發者,如果永遠停留在不學Javascript、只會用UpdatePanel的層次,就沒有能力滿足高承載網站的AJAX設計需求,注定無緣參與大型網站的建置,勢必不利前途(跟錢途),值得警惕!

Wow! ASP.NET AJAX Client Templates!

前陣子CodePlex釋出了ASP.NET AJAX 4.0 Preview 3,本來我是渾然不覺的,直到在PDC 2008的課程影片裡看到神奇的示範...

這個新鮮玩意叫做ASP.NET AJAX Client Templates,簡單來說,就是把大家已經很熟悉的WebControl Template概念移至Client端以Javascript端實作。以下的語法對寫過一陣子程式的ASP.NET開發者應該不陌生:

   1:      <form id="form1" runat="server">
   2:      <div>
   3:      <asp:GridView ID="GridView1" runat="server" 
   4:          AutoGenerateColumns="False">
   5:          <Columns>
   6:              <asp:TemplateField HeaderText="NameEval">
   7:                  <ItemTemplate>
   8:                      <asp:Label ID="Label1" runat="server" 
   9:                      Text='<%# Eval("Name_Eval") %>'></asp:Label>
  10:                  </ItemTemplate>
  11:              </asp:TemplateField>
  12:              <asp:TemplateField HeaderText="NameBind">
  13:                  <ItemTemplate>
  14:                      <asp:Label ID="Label2" runat="server" 
  15:                      Text='<%# Bind("Name_Bind") %>'></asp:Label>
  16:                  </ItemTemplate>
  17:              </asp:TemplateField>
  18:              <asp:TemplateField HeaderText="NameFuncEval">
  19:                  <ItemTemplate>
  20:                      <asp:Label ID="Label3" runat="server" 
  21:       Text='<%# CvrtStr(Eval("Name_Func_Eval")) %>'></asp:Label>
  22:                  </ItemTemplate>
  23:              </asp:TemplateField>

在ASPX中穿插<%# ... %>就可以將動態內容注入到要顯示的結果中,十分直覺易寫。不過,將產生UI元素的邏輯儘可能由Server端抽離,移至Client端完成,看來應是未來的主流哲學,ASP.NET AJAX也漸漸會向"Pure AJAX"的目標挺進。換句話說,過去使用UpdatePanel,來回傳送大量ViewState、在Server端組合HTML送至Client更新局部內容的做法,將會慢慢被淘汰。(還為自己不懂Javascript也寫得出AJAX而沾沾自喜的朋友,要小心將來一夕變天)

ASP.NET AJAX Template的概念相同,先不要扯到太複雜的前後端結合,我用一個最簡單的例子示範一下:

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <Scripts>
    <asp:ScriptReference Path="~/Inc/jQuery/jquery.js" />
    <asp:ScriptReference Path="~/MicrosoftAjaxTemplates.debug.js" />
    </Scripts>
</asp:ScriptManager>
<script type="text/javascript">
    $(function() {
        var dv = $create(Sys.UI.DataView, {}, {}, {}, $get("tbTemplate"));
        var people = [
            { Id: "A01", Name: "Jeffrey Lee", 
              Score: 32767, RecTime: new Date() },
            { Id: "A02", Name: "Fox Mulder",
              Score: 6242.25,
              RecTime: Date.parseInvariant("2008-11-05 12:00:50", 
              "yyyy-MM-dd HH:mm:ss") },
            { Id: "A03", Name: "Dana Scully",
                Score: 8391.66, RecTime: new Date(2008, 6, 15) }
            ];
        dv.set_data(people);
        $("#tbTemplate").show();
    });        
</script>
<table border="1" cellspacing="0" cellpadding="0" 
 style="font-size: 9pt; width: 300px;">
<thead>
    <tr><th>Id</th><th>Name</th><th>Score</th><th>Record Time</th></tr>
</thead>
<tbody id="tbTemplate" style="display:none; padding: 3px;">
    <tr>
        <td style="text-align: center;">{{Id}}</td>
        <td>{{Name}}</td>
        <td style="text-align: right;">{{Score.format("N2")}}</td>
        <td style="text-align: center;">
        {{RecTime.format("yyyy/MM/dd HH:mm:ss")}}</td>
    </tr>
</tbody>            
</table>

補充幾個重點:

  1. MicrosoftAjaxTemplates.debug.js可由Asp.Net AjaxPreview 3取得,debug.js是易讀的原始碼版本,方便學習及偵錯,js則是可用於正式環境的壓縮版,能大幅減少網路傳輸。
  2. 注意到了嗎? jQuery可以跟ASP.NET AJAX相輔相成,在jQuery的網頁初始函數$(function())中$create(Sys.UI.DataView)建立將Table的TBody部分設定成DataView。
  3. var people = [ { }, { } ]的簡要寫法才剛介紹過,目的要建出一個由Object組成的Array當成Data Source。在實際運用上,當然不會是用Javascript Hardcoding資料源,資料源多半會由WCF、Web Service或ADO.NET Data Service以JSON方式傳過來。
  4. 一開始先將tbody設為隱藏,等set_data(相當於Server-Side的DataBind())後再顯示出來。
  5. Bind資料的語法是{{ object_property_name }}。
  6. 有沒有看到Score.format("N2"), RecTime.format("yyyy/MM/dd HH:mm:ss"), Date.parseInvariant("2008-11-05 12:00:50","yyyy-MM-dd HH:mm:ss") ? 這些方便的格式化功能是ASP.NET AJAX Data Type Extension做的功德,大家可別放著不用自己DIY呀~~

是不是很簡潔有力呢? 至少對酷愛用Javascript處理前端邏輯的我來說,它十分迷人。關於ASP.NET AJAX Client Templates,微軟有份完整的Tutorial,但略為複雜,這陣子我會再試著用淺顯的例子展示一些神妙之處,敬請期待。

世界級的程式問題發問平台-StackOverflow.com

"到Internet上找答案"相信是很多人最核心的Internet應用。

Google提供了很無敵的搜索寶庫,但下不對關鍵字,解答可能這輩子都深藏茫茫網海,至死不得。主動發問是另一種操之在我的做法,因此,許多媒合提問者與解答者的平台應運而生。

其中,Yahoo奇摩知識+在台灣有很高的知名度(跟Yahoo花了大錢行銷很有關係),可以問的領域則從晶圓製程到為什麼會有頭皮屑,無所不包。iT邦幫忙則把問題的領域再縮小到限與IT相關,能更專注於電腦技術相關問題。MSDN論壇TechNet論壇則是範疇集中於微軟相關的專門平台。

但以上平台的主要資源多來自台灣各界的高手達人,問題一旦冷門深僻,極可能全台灣找不到第二個跟你有相同處境的可憐蟲,就不太容易從中獲得解答。此時就該向世界挺進,全球數十億的電腦使用人口,找到同病相憐之友的機率總會高多了吧?

上週五,有位網友參考我之前寫的Excel CSV花式技巧一文,發現了又要加逗號又要保留前導零的大絕招,讓Excel CSV當場倒地,找不出堪用的解決之道。我推測這已超出Excel CSV設計時的考量,看來無解。不過,要證明有解很容易,只要一個範例即可;要確認無解卻很困難,試過100種方法都行不通,又怎麼知道會不會有第101種方法讓你的無解推論無地自容,這跟要證明宇宙裡沒有外星人一樣困難。那麼,讓全球成千上萬的電腦玩家來評評理,大家都說不行,就算我搞錯,總情有可原吧?

Stack Overflow是一個以Programming相關問題與解答為主的知識交換平台,它非常自由,毋需註冊就可以使用(註冊則可以保留個人活動歷程並累積聲望,聲望高可以獲得更高的參與權,例如: 以Wiki的角度修改他人發言),問題分類採用Tag哲學,所以不用傷腦筋如何挑對分類,問題不限語言、平台,如其名所提示,唯一要求是必須與程式設計相關。換句話說,這是一個世界級、只討論程式問題的"Yahoo奇摩知識+",好奇它的時效性及威力,於是將前述的Excel CSV問題丟上Stack Overflow。

非常驚人地,不到10分鐘,我就募集到三則回答,看來Stack Overflow社群非常活躍! 而由回答中,也讓我有信心維持無解推論。(2008-11-24更新,無解推論已被網友推翻,台灣果然是卧虎藏龍之地)

下回有程式問題要問,當遍尋國內無所獲,磨好你的英文,試試在Stack Overflow發問吧!!

[Update 2008-11-24] 試過100種方法都行不通,又怎麼知道會不會有第101種方法讓你的無解推論無地自容 --> 這句講得太好了,還馬上應驗 orz。網友邪教露了一手,用一行="001" & CHAR(44) & "002"就輕鬆把問題給搞定了!! 本著回饋的心情,已順手把這個解決方案丟回Stack Overflow。

Microsoft Guava 3.0!

對微軟技術狂熱的最高境界是什麼?

是的! 食衣住行都要徹底融入才行,而這款"微軟牌芭樂"應該可以在微軟尚未跨足農產業界的當下,稍稍填補微軟基本教義派信眾腸胃的空虛~~~

據可靠消息指出,水果店老闆芭樂伯已報名下屆Microsoft MVP選拔,並很有可能當選,成為史上第一位沒用過Windows的MVP。

照片由Lucas提供,特此致謝

XML的jQuery處理補遺

保哥開啟了一個有趣的議題: 解決在 IE 下無法將 HTML/XML 轉成 jQuery 物件的問題 。當傳回的內容是XML,若未將HttpRequest的dataType設為"xml",最後在處理XML時,會發生在Firefox上可行但在IE不成功的結果。

因為好奇,追了一下jQuery的Source Code,發現關鍵出在這裡:

    httpData: function( xhr, type, filter ) {
        var ct = xhr.getResponseHeader("content-type"),
            xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
            data = xml ? xhr.responseXML : xhr.responseText;

當dataType=="xml"時,data會傳回responseXML(一個XML DOM物件),否則傳回responseText(字串)。換句話說,當dataType沒設定成"xml"時,Firefox正確地將$("<root>...</root>")轉成了XML DOM,但IE卻沒有。

先前有一篇小文談過用jQuery解析XML,裡面提到直接字串轉XML時,前後要加上<xml></xml>才行(我想當時的測試環境就是以IE為準),試了將字串改寫成<xml><root></root></xml>,果然就可以在IE上成功執行。

這回我對於沒加<xml></xml>在IE裡會發生什麼事有點興趣,這部分在上次的文章中沒有深入探討,只知會失敗。於是這次我做了以下的實驗:

alert($("<root><item>Text</item></root>").size());

嘿! 有趣的地方來了,上面這段Code在IE裡會得到5(Firefox裡則是1)。然後再進一步剖析:

        var o = $("<root><item>Text</item></root>");
        o.each(function() { alert(this.outerHTML); });

在IE下我得到<ROOT>, <ITEM>, undefined, </ITEM>, </ROOT>五項內容,再仔細深入,Text被解析成Text Node,因此沒有outerHTML,但o[2].data就可以取得字串內容。

頗奇怪的解析結果,似乎沒什麼大用,僅滿足好奇心而已。

Chiwis CS Gallery Manager on x64

一直以來,我都用Chiwis CS Gallery Manager上傳圖片到Community Server。但自從家中與公司分別換了Vista x64及Windows 2008 x64後,Chiwis CS Gallery Managery的安裝程式就無法成功執行,使用手工複製檔案,程式可以跑,卻因為少了安裝過程的3rd Party元件註冊程序,在選取檔案上傳時,出現以下錯誤導致作業失敗:

ERROR ¦ Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)) ¦    at System.Windows.Forms.UnsafeNativeMethods.CoCreateInstance(Guid& clsid, Object punkOuter, Int32 context, Guid& iid)
   at System.Windows.Forms.AxHost.CreateWithoutLicense(Guid clsid)
   at System.Windows.Forms.AxHost.CreateWithLicense(String license, Guid clsid)
   at System.Windows.Forms.AxHost.CreateInstanceCore(Guid clsid)
   at System.Windows.Forms.AxHost.CreateInstance()
   at System.Windows.Forms.AxHost.GetOcxCreate()
   at System.Windows.Forms.AxHost.TransitionUpTo(Int32 state)
   at System.Windows.Forms.AxHost.CreateHandle()
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.AxHost.EndInit()
   at CSGalleryManager.PhotoDetails.InitializeComponent()
   at CSGalleryManager.PhotoDetails..ctor()
   at CSGalleryManager.MainForm.addFileToQueue(SelectedListViewItemCollection items)

一開始我很鴕鳥地另外裝了VM跑Windows 2003,每每要上傳圖檔,就進VM裡作業,但久了久之也開始對每次傳圖就要切換環境有些懶,所以就開始著手找解決方案。(偷懶真是迫使人類進步的原動力呀)

由訊息來看,錯誤出在建立ActiveX OCX物件時,程式目錄下就只有一個XStandrad.ocx,嘗試過手動regsvr32 XStandard.ocx,可以成功註冊,但錯誤依舊;CS Gallery Upload Manager.exe.config裡有個參數UseXstandardEditor,設成false一樣出錯(在Form初始化階段就會載入該OCX,停用也緩不濟急)。

不得已只好把腦筋動到改Source Code上,Chiwis CS Gallery Manager是個Open Source專案(Open Source萬歲),可以透過SVN方式下載原始碼。打開專案,沒兩下就找到使用XStandard.ocx的地方---PhotoDetail.cs,原來它用XStandard提供RichText的照片描述編輯,我Blog上的圖片一向只作文章插圖,編輯照片描述的功能幾乎沒用過,因此就大大方方地把OCX元件刪除,並修改了跟元件關聯的程式碼,Rebuild後,嘿~~~ Chiwis CS Gallery Manager就在x64上跑將起來,成功!

37吋的幸福

結婚時買的29" Sony老電視在兩個多月前壞掉,開機有聲無影,並會三秒後自動關機。請了維修師傅來看,推測是高壓線路故障,映像管有無損壞則不知,維修費2,500起跳,且需帶回工廠修理。

用了八年多的映像管電視值得花2,500修理嗎? 結果當然決定棄修重購,讓摳到不行的鐵公雞有機會淺嚐敗家滋味,於是新電視採購小組隨即成立。

讓人跌破眼鏡地,採購小組的第一步卻先買了一台DecaView 22"內建電視盒的LCD(因為同一時間,家中另一台兼負電視盒顯示器的ViewSonic 17" LCD剛好也掛點,趁著送修XPC SB81P Power Supply時到光華抱了一台回家。看到沒,一次壞三樣,真是機瘟),暫放客廳先頂著,排除迫切的需求壓力,才能敗家敗得從容、敗得精明、敗得划算。在Mobile01及PTT爬了一陣子文章,發現不少人對BenQ的SH3742/SD3742(FullHD+友達面板+三年保固+愛用國貨+NTD 29,888)頗有好評,這兩款分別在燦坤與家樂福舖貨,決定機型後,剩下的便看出手時機。由於有小22吋頂著,才能不急不徐,慢慢等待最甜的擊球點出現。

心目中最理想的時點是搶在家樂福買2,000送200優惠活動時下手,等同再打九折。無奈這段時間家樂福搞過兩三次送200促銷,要不隔天才知,要不記錯日子,幾次良機就這麼擦身而過。上週五下班,一眼瞄見聯合報頭版的家樂福廣告,衝!!!

出門前再爬了一下文,發現SD3742正在狂打促銷,一些網站購物紛紛打出26,888的特價,我打打算盤,跟家樂福29,888九折後相近,但現場看機下單較為簡便,加上有大賣場的商譽背書,最後還是決定出手。(其實真正的原因,應該是心裡雄雄的敗家之火悶燒兩個多月,火勢凶猛,早已銳不可擋)

到了現場,買2,000送200真是魅力無法擋,九點多仍是人山人海,家電區更是萬頭鑽動,結帳櫃台排了老長。在液晶電視區逛了一圈,就是沒看到SD3742,找來現場電視售貨區的先生一問,原來現場沒貨,當天下單,預估可能週三、四才會到貨,我露出"這樣子哦... 我知道了"的神情,其實心盤算"反正今天買有九折,兩個月都等了,等三天那有差K"。沒想到臉上的猶豫之色激起了電視先生的危機意識,馬上說他立刻打電話去查,兩分鐘後便告知說隔天晚上可以送到... 嘿,那還遲疑什麼,快讓口袋裡的小朋友在大賣場中盡情地嘻戲奔跑吧!!

乖乖到結帳櫃枱排隊,電視先生還打趣地說 "說什麼現在有多不景氣? 這裡看起來一點都不像 orz"

終於排到我,一刷商品條碼,噹噹噹,收銀機傳來好消息,電腦系統顯示,SD3742的售價已調為26,888(不過為了處理舊電視,我多付了600加送貨及搬走廢品的服務),跟網路上找到的最低價相同;換句話說,無意間等同拿到市面低價又打了九折,嘿! 爽~~~

本以為隔天晚上到貨是業務人員慣用的樂觀預估法(記得以前在估專案交付時程,也都對客戶採行相同的預估哲學),沒想到週六晚上八點多就接到電話說貨要送到,為家樂福的物流精準度拍個手。

送貨先生手腳俐落,幾分鐘後,BenQ SD3742就在小小客廳裡賣力展現37"大螢幕的魔力,一家大小一起窩在沙發看卡通電影,沈醉在科技與銀子聯手打造的幸福裡~~~

沒圖沒真相?? "開箱"照在這裡,大伙就別敲碗了。

後記: 不知是否是類比視訊盒的功勞,新電視看第四台的訊號清晰度比DecaView 22"及老Sony平面電視清楚許多,整體來說,這台的品質及C/P值令我滿意。

雲間二格

連下了一週的雨,這個週六老天爺很罕見地沒有遵守【莫非定律】,給了一個晴天。

天氣漸寒,要早起難免一番掙扎,所幸意志力最後戰勝,仍把握難得的好天氣,一鼔作氣直奔二格山頂。

水氣飽滿的多雲晴天,山頂總有許多景色可看(當然也伴隨著一路溼滑,在山林間穿梭要格外小心),翡翠水庫提供的豐沛水氣總能在環著水庫的山頭間搞出一片迷你雲海,雖然氣勢難比百岳雲海的霸氣,但只花不到10塊錢的油錢,能讓蟄居台北的好爸爸阿宅一睹大自然的壯麗,足矣!

一週前,也是個晴天,但雲更多些。

在拾級而上的當下,一朵白雲低空掠過山頭,置身其間,夢幻般的景象令人震撼,就像... 算了,說了你也不會明白,有機會自己去親身體驗吧!

 

(在網頁上點照片可以觀看大圖)

jQuery課後輔導: 你可能沒用過的Javascript小技巧

使用Javascript撰寫Client-Side程式也好些年了,卻從來沒對Javascript這個語言下過苦工,一直抱持用到哪學到哪的精神。在接觸jQuery之後,見識到不少以前沒用過的神奇寫法,一開始還真花了點時間才搞懂,在此做個簡單的分享:

陣列表示法

宣告已知元素的陣列,可以不必動用到new Array(),寫成var ary = ["1", "2", "3"];就好了,一口氣把宣告跟內容指定都搞定。

這跟C# 3.0裡的Implicit Typed Array是不是有異曲同工之妙?

抄襲? 我倒不會這麼解讀,程式語言在發展過程中,本來就該融入其他語言犀利之處、偷習他派武功的精華,從Developer的使用者角度,誰在乎意識形態搞什麼漢賊不兩立? 貫徹"Developer之所欲,常在我心",才是王道!

匿名物件

大家對LINQ中用var宣告Anonymous Type,應該感到挺新奇有趣的,而Javascript裡也可以看到類似的東西。Javascript雖然不是Strong-Typed語言,但卻也很容易實踐Object、Property、Method等物件操作,例如:

var person = new Object();
person.Name = "Jeffrey";
person.Age = 18;
person.Say = function(msg) {
    alert(this.Name + "(" + this.Age + ") said: " + msg);
};
person.Say("Hello");

而以上的程式,可以省去宣告物件及逐一指定屬性,直接簡化寫成:

var person2 = 
{     
    Name:"Jeffrey", Age:18, 
    Say:function(msg) { 
    alert(this.Name + "(" + this.Age + ") said: " + msg) 
    } 
};
person2.Say("Hello Again!");

再來一個範例,快速宣告並設定一個物件陣列!

var people = [ 
    { Name:"George", Age:18 },
    { Name:"Mary", Age:16 }
    ];
for (var i=0; i<people.length; i++)
{
    var person = people[i];
    alert(person.Name + "(" + person.Age + ")");
}

這種寫法很常被用來函數的參數傳遞上,一口氣要傳多個且彈性組合的屬性內容時很方便(頗有Dictionary/Hashtable的味道),例如:

$("#btnOK").css({ color: "yellow", backgroundColor: "green" });

匿名函數

其實剛才已經不小心示範過了,就是Say = function(msg) { ... }的寫法。

傳統上當我們要撰寫事件時,都會另外宣告一個function funName() { ... },再將函數名稱指定到特定的物件上。而在jQuery裡,常常都會直接在要傳入函數參數的地方,如Click事件、HTTP Request呼叫結果處理等,直接用function() { ... }寫Code,例如:

    <script type="text/javascript" src="AfaClient/jquery-1.2.6.js"></script>
    <script type="text/javascript">
    $(function() {
        $("#btnOK").click(function() {
            alert($(this).attr("title"));
        });
    });
    </script>
    <input type="button" id="btnOK" value="OK" title="Button's Title"/>

在以上範例中,$(function() { ... });用來宣告網頁載入後onload事件要執行的邏輯,這裡寫的是找到btnOK並宣告它的onclick事件,程式裡還一併示範在事件中可以用this取得觸發事件的物件。

OracleCommand.Parameters.AddWithValue

不知怎麼了,每次寫ORACLE存取程式都會在咒罵中度過"美好"時光,剛才花了半小時處理一個錯誤:

ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'MYSTOREDPROC'

程式碼差不多像以下這個樣子,要傳入十五個名稱又臭又長的參數給一個Procedure:

OracleCommand cmd = new OracleCommand("MyStoredProc");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("p_complex_para_name", p1);
cmd.Parameters.AddWithValue("p_more_complex_para_name", p2);
//...
cmd.ExecuteNonQuery();

錯誤指出與參數數目或型別有關,p1, p2...清一色是string,而所有Procedure參數都是VARCHAR2,檢查過所有OracleParameter.DbType都是AnsiString,因此我一口咬定是數目或名稱出了問題。於是名稱比了又比,數目數了又數,就是找不出問題出在哪裡。(還無意中學到一個Tip,ORACLE的參數名詞如果超過31個字母,也會導致以上錯誤)

最後把仔細檢查了p1-p15,發現其中有一個的值是null!!

換句話說,Parameters.AddWithValue("..",  null)時,即使OracleParameter.DbType == DbType.AnsiString也會導致傳送到ORACLE的參數型別不正確。如果擔心這類問題,避用AddWithValue,改用Add明確指定型別,也是不錯的寫法。

特貼文一篇紀念本次鬼打牆!!

TIPS - WCF on IIS 7.0

在Vista IIS 7上寫了一個WCF服務,正想用http: //localhost/MyWebApp/MyWCF.svc看一下有無成功執行,結果傳回以下錯誤:

HTTP Error 404.3 - Not Found
The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.

查了一下,在IIS7上跑WCF要動點手腳,得執行ServiceModelReg.exe註冊WCF: (注意要用Admin身份跑,最簡單的方法是 開始->執行->cmd,然後ctrl-shift-Enter,就會以Admin身份開始命令視窗)

C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation>servicemodelreg -i

藥到病除!

TIPS-為VS2008加入完整jQuery Intellisense

微軟不是說jQuery已正式納入為ASP.NET產品的一部分? Visual Studio 2008應該對jQuery提供更多支援才對呀? 有嗎?

日前,MS Visual Studio Web Tools的Program Manager, Jeff King, 在他的Blog提供了讓VS2008加入完整jQuery Intellisense的做法

首先,jQuery的官方下載區(http://docs.jquery.com/Downloading_jQuery#Download_jQuery),在原本的Minified, Packed, Uncompressed版檔案連結下方,多了一個Documentation項目,裡面有Visual Studio的專用文件說明檔: jquery-1.2.x-vsdoc.js。

這個檔案怎麼用呢? 依照Jeff King的說法,要寫成以下的格式: (js中則是要加入/// <reference path="js/jquery-1.2.6-vsdoc.js" />宣告)

<script type="text/javascript" src="js/jquery-1.2.6.js"></script>
<% if (false) { %>
    <script type="text/javascript" src="js/jquery-1.2.6-vsdoc.js">
    </script>
<% } %>

if (false)的寫法有點Tricky,它可以讓Visual Studio讀入vsdoc.js的內容,在Intellisense時顯示完整的函數、參數說明,卻又不干擾實際的執行。加入後,在Visual Studio裡要動用jQuery相關函數時,就可以看到完整的文字敘述,感覺還不錯。

不過,要另外手動加上vsdoc.js的Script Include顯得有些笨,更不用說那個if (false)的怪招,感覺是硬拼出來的解決方案。所幸,這只是暫時的因應之計,未來將會有Hotfix讓Visual Studio能自動尋找vsdoc.js,屆時就可以稱得上是內建支援了。

【2008-11-08更新】Wow! 自動搜尋-vsdoc.js的Hotfix已經出來囉! 可以下載玩玩!

更多文章 下一頁 »

搜尋

Go

<November 2008>
SunMonTueWedThuFriSat
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication