.NET Naming Conventions

0 comments
遵循良好的命名慣例可以讓程式更容易被理解和閱讀。尤其,在團隊工作中採用一致的命名方針將利於程式碼的整合與維護。

匈牙利命名法(Hungarian Notation)是常見的命名方式,如果你在 .NET 程式設計中一眛的沿用匈牙利命名法,不僅無助於提升程式碼的可維護性,甚至會因為命名冗雜造成反效果。果真如此,你就更應該考慮使用更好的命名方式。在這裡推薦由 Pete Brown 依據微軟的開發類別庫的設計方針及業界公認標準(Industry-Accepted Standards)所整理的 .NET 命名準則,非常具有參考價值。

值得一提的是,這份文件還特別針對 Windows 表單和 Web 表單中的 UI 控制項識別名稱,提出獨道的命名原則。雖然作者特別強調不贊成使用匈牙利命名法的原因,然而在不使用匈牙利命名法的原則下,UI 控制項的識別名稱是唯一不在此限的例外。作者建議對所有 UI 控制項的識別名稱,統一採用 "ux"(意即 User eXperience 的縮寫)做為前綴詞(Prefix),其所持的理由如下:
  1. 相較於使用型別相依的匈牙利命名法更為簡潔,且不用顧慮變更控制項型別所帶來的名稱衝突問題。
  2. 有助於在 Intellisense 中將你的控制項型別變數名稱群組在一起。

如此一來,將可擺脫以往使用繁雜的控制項前綴詞(如 txt、btn 等)的命名方式。如果你也像我一樣常為控制項識別名稱命名感到苦惱,那麼相信採用新命名法將會為你帶來更佳的表單開發經驗。

資料來源:
Common .NET Naming Conventions by Pete Brown

繼續閱讀...

Mooquee - Marquee with Mootools

2 comments
Mooquee 是一個以 MooTools 為基礎所設計的跑馬燈的 JavaScript 類別,它提供建構選項可以表現出多種不同於 Marquee 標籤的跑馬燈動畫效果。

實作時,除了要引用 Mooquee 程式庫外,你還需要搭配 MooTools 1.2。在建構 Mooquee 物件時,你可以設定以下建構選項:
  • element:指定做為跑馬燈區塊的 HTML 元素識別碼。
  • cssitem:指定播放項目的樣式類別名稱。
  • firstitem:指定第一個播放項目的索引。
  • direction:以 up、down、left 或 right 指定跑馬燈移動的方向。
  • pause:以秒為單位指定每次停止時間,且必須大於或等於 duration 的值,其預設值為 1 秒。
  • duration:以秒為單位指定每次移動所需時間,其預設值為 1 秒。
  • overflow:設定當內容超出跑馬燈區塊範圍時的處理方式,預設值為 hidden。
  • startOnLoad:指定是否自動播放,預設值為 true。
如以下範例會建立向上垂直捲動的跑馬燈:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Mooquee Example</title>
<style type="text/css">
#mooquee_container {
width: 200px;
height: 20px;
border: 1px solid;
}
.mooquee_item {
background-color: #ffffff;
}
</style>
<script type="text/javascript" src="mootools-1.2-core.js"></script>
<script type="text/javascript" src="Mooquee.js"></script>
<script type="text/javascript">
window.addEvent('domready', function() {
var mooquee = new Mooquee({
element:'mooquee_container',
cssitem:'mooquee_item',
direction:'up',
duration:1,
pause:2,
firstitem:0
});
});
</script>
</head>
<body>
<div id="mooquee_container">
<div class="mooquee_item">跑馬燈訊息第 1 則</div>
<div class="mooquee_item">跑馬燈訊息第 2 則</div>
<div class="mooquee_item">跑馬燈訊息第 3 則</div>
</div>
</body>
</html>

繼續閱讀...

Internet Explorer 8 Web Slices

0 comments
Web Slices 是 Internet Explorer 8 的新功能之一,網頁開發者可以在單一網頁中指定多個區塊(Slices)供使用者訂閱到 IE8 新的最愛列(Favorites Bar)。

現在很多網站都會提供 RSS(Really Simple Syndication)的網站內容訂閱服務。使用者只要利用 RSS 閱讀器訂閱內容,就可以方便獲得網站的更新資訊,而不需造訪網站檢閱是否有變更或新資訊。對於提供 RSS 服務的網站來說,它必須提供特定格式的 XML 檔案(亦即 RSS feed)的內容,才能供閱讀器下載及檢查更新。相較之下,Web Slices 並不需要個別的 Feed 檔案,而是直接在網頁中定義 Feed 項目,如以下範例:
<div id="Div1" class="hslice">
<h4 class="entry-title">Web Slice Title</h4>
<div class="entry-content">
This will appear in the preview window.
</div>
</div>
如你所見,你只要在 HTML 標籤中透過 class 屬性,套用如上例粗體標示的樣式類別名稱,即可在網頁啟用 多個 Web Slice。一個最簡單的 Web Slice 至少要具備以下三個要素:
  • 一個套用 hslice 樣式類別的父元素。
  • 父元素的必須具備唯一的 ID 識別名稱。
  • 一個套用 entry-title 樣式類別的子元素。
當 IE 8 偵測到網頁有可用的 Web Slice 時,其工具列上的就會出現綠色的 圖示來提示使用者。同時,當使用者的滑鼠游標移至網頁中的 Web Slice 時也會出現相同的圖示。當使用者按下圖示就可訂閱 Web Slices,並將其新增至 [我的最愛] 列。只要使用者訂閱 Web Slices,便可以直接在 [我的最愛] 列開啟快顯視窗預覽到相關資訊。如果有興趣,你可以先體驗 eBay 實作 Web Slices 的專屬網站:htt://ie8.ebay.com/。

相關連結:
New RSS Features for Internet Explorer 8
Internet Explorer 8 Web Slice Style Guide
Internet Explorer 8 Beta 1 Whitepapers - Release WebSlices

繼續閱讀...

AJAX DateTimePicker Extender

15 comments
在 Windows Forms 中,有 DateTimePicker 控制項可以用來選擇日期或編輯時間。然而,在 ASP.NET 中只有內建可選取日期的 Calendar Web 伺服器控制項,以及 AJAX Control Toolkit 提供的 Calendar 擴充項外,並沒有兼具選擇日期或編輯時間的 Web 控制項。所幸,jscalendar 正好可以滿足這樣的需求。它是一款功能強大且免費的 DHTML 日期選擇器,不僅內建多種的面板和色彩主題,還支援多國語言顯示介面。

為了能方便引用到 ASP.NET 網頁中,於是自己便著手將 jscalendar 1.0 封裝到自訂的 AJAX DateTimePicker 擴充項控制項。


DateTimePicker 擴充項的屬性
屬性說明
Align指定快顯日曆的對齊方式,預設值為 "Br"。詳見對齊方式格式說明。
Format指示要套用到文字方塊中的日期顯示格式,期預設值為 "%Y-%m-%d"。詳見自訂日期時間格式說明。
FirstDayOfWeek以 0 到 6 指定每週的起始日,0 表示星期日,1 為星期一,依此類推。預設為星期日。
PopupButtonID指定用來觸發顯示快顯日曆的控制項識別碼。
TargetControlID指定顯示選取日期的 TextBox 控制項識別碼。
Theme預設的日曆面板主題為 Aqua,你可以依照偏好變更為其他的面版樣式:Blue、Blue2、Brown、Green、System、Tas、Win2k1、Win2k2、Win2kCold1、Win2kCold2。
TimeMode以 12 或 24 定義時間格式,預設為 12 小時制。
SingleClick指定日期選擇是 Single-Click 或 Double-Click 模式。若指定為 true(預設值)則表示為 Single-Click 模式。
ShowsTime指定是否顯示時間,若指定為 false(預設值)則表示不顯示。
WeekNumbers指定是否顯示星期名稱,若指定為 true(預設值)則表示要顯示。

對齊方式格式
使用 Align 屬性可以指定快顯日曆的垂直對齊(Vertical Alignment)及水平對齊(Horizontal Alignment)方式。在 Align 屬性中的第一個字元表示垂直對齊方式,其格式規範如下:
格式說明
T下邊緣與目標元素的上邊緣相鄰對齊。
t下邊緣與目標元素的下邊緣相鄰對齊。
c垂直置中於目標元素。
b上邊緣與目標元素的上邊緣相鄰對齊。
B上邊緣與目標元素的下邊緣相鄰對齊。

第二個字元表示水平對齊方式,其格式規範如下:
格式說明
L右邊緣與目標元素的左邊緣相鄰對齊。
l左邊緣與目標元素的左邊緣相鄰對齊。
c水平置中於目標元素。
r右邊緣與目標元素的右邊緣相鄰對齊。
R左邊緣與目標元素的右邊緣相鄰對齊。

自訂日期時間格式
以下是使用 Format 屬性指定自訂日期時間格式的規範:
格式說明
%a表示星期名稱的縮寫。
%A表示星期名稱的全名。
%b表示月份名稱的縮寫
%B表示月份名稱的全名。
%C表示世紀的數字。
%d以 00 到 31 表示月份的日期。
%e以 0 到 31 表示月份的日期。
%H以 00 到 23 的數字來表示小時。
%I以 01 到 12 的數字來表示小時。
%j本年第幾天。
%k以 0 到 23 的數字來表示小時。
%l以 1 到 12 的數字來表示小時。
%m以 01 到 12 的數字代表月份。
%M以 00 到 59 的數字來表示分鐘。
%n表示換行字元。
%p表示 PM 或 AM。
%P表示 pm 或 am。
%S以 00 到 59 的數字來表示秒數。
%s自 Epoch 系統時間開始所經過的秒數。
%t表示 Tab 字元。
%U, %W, %V本年的第幾周。
%u以星期一為起始序數,從 1 到 7 的數字代表星期的日期。
%w以星期日為起始序數,從 0 到 6 的數字代表星期的日期。
%y以兩個位數的數字來表示年份。
%Y以四個位數的數字來表示年份。
%%將 % 字元顯示為常值(Literal)。

範例
下列範例會建立能讓使用者選擇日期及時間的日曆:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<ace:DateTimePickerExtender ID="DateTimePickerExtender1" runat="server"
TargetControlID="TextBox1"
Format="%Y-%m-%d %H:%M"
ShowsTime="true"
SingleClick="false" />

你也可以加入按鈕來開啟快顯日曆:
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:Button runat="server" ID="Button2" Text="..." OnClientClick="return false;" />
<ace:DateTimePickerExtender ID="DateTimePickerExtender2" runat="server"
TargetControlID="TextBox2"
PopupButtonID="Button2"
Format="%Y-%m-%d %H:%M"
ShowsTime="true" />


 Download source code - for ASP.NET 3.5

資料來源:
http://sourceforge.net/projects/jscalendar/

繼續閱讀...

Ajax Loading Gif Generator

2 comments
建置 AJAX-Enabled 網頁,可以藉由網頁局部更新(Partial-page Update)改進使用者經驗。然而,一個良好的 AJAX 網頁,在一個或多個網頁局部呈現的過程中,應該要能提供更新狀態的視覺化回應,用圖形或文字提示使用者。通常,你可以加入動畫圖檔來回饋使用者的處理要求,讓使用者介面更為活潑。然而,在這裡並非要介紹如何在網頁局部更新中提供視覺化回應,而是要推薦一個免費提供 AJAX Loading 動畫圖示的產生器:Ajaxload。

它有多達 37 種圖示樣式(Indicator type)可供選擇,並且可以隨心所欲調整前景及背景顏色:


按下 [Generate it!] 按鈕後,你就可以在預覽區下載你的動畫圖示:


資料來源:
http://www.ajaxload.info/

繼續閱讀...

Slideshow 2!

0 comments
Slideshow 2! 是一個以 Mootools 為基礎所設計的 JavaScript 類別,它可以協助你輕易建立多種有如 Flash 動畫效果般的 DHTML 投影片播放器。除了完整的功能之外,其動感的視覺效果表現更是 Slideshow 2! 的重點特色。你可以從這裡找到最新版本的原始碼及說明文件。

在這裡,我就列舉兩種最常用的播放器型態。如以下範例會以淡入淡出(Fading)的特效,循環播放含有標題的相片:
<html>
<head>
<title>Slideshow 2! Example</title>
<link rel="stylesheet" type="text/css" href="css/slideshow.css" media="screen" />
<script type="text/javascript" src="js/mootools.js"></script>
<script type="text/javascript" src="js/slideshow.js"></script>
<script type="text/javascript">
window.addEvent('domready', function() {
var data = {
'1.jpg': { caption: '晨曦乍現' },
'2.jpg': { caption: '藍天浮雲' },
'3.jpg': { caption: '夕陽餘暉' }
};
var myShow = new Slideshow('show', data, {
captions: true, controller: true, hu: 'images/', width: 400, height: 300
});
});
</script>
</head>
<body>
<div id="show" class="slideshow">
<img src="images/1.jpg" width="400" height="300" />
</div>
</body>
</html>



你也可以加入有縮圖的移動捲軸:
<script type="text/javascript">
window.addEvent('domready', function(){
var data = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg', '6.jpg', '7.jpg'];
var myShow = new Slideshow('show', data, {
thumbnails: true, controller: true, hu: 'images/', width: 400, height: 300
});
});
</script>


除此之外,它還能表現出遠近縮放(Zoom)、推進(Push)等特效的動態相片。你可以從這裡看到更多令人驚艷的範例作品展示。如果你覺得這個程式真是受用,不妨也可以到作者的網站,用贊助的行動鼓勵作者喔!

資料來源:
Slideshow 2! A javascript class for Mootools 1.2 to stream and animate the presentation of images on your website by Aeron Glemann

P.S. 本文所使用的範例圖片係取材自 http://www.freeimages.co.uk/。

繼續閱讀...

AJAX ModalPopup Extender in GridView Control

5 comments
在 ASP.NET 3.5 中,你可以使用 UpdatePanel 及 ScriptManager 伺服器控制項來更新局部的網頁,而不需重新載入整個網頁。當你使用 GridView 控制項搭配 UpdatePanel 控制項,對資料來源進行編輯、刪除等功能,及重新繫結資料更新頁面的記錄,就可以透過觸發 UpdatePanel 控制項來完成。

你可以啟用 GridView 控制項內建的編輯功能,讓使用者能夠編輯個別資料列的內容。但如果需要編輯的資料欄位或是內容不足以在有限的方格(Grid)中呈現時,就必須考慮其他編輯模式。這時,你可以考慮搭配 DetailsView 控制項,或是採用更進階的巢狀式應用等。不過,在這裡要介紹另一種呈現方式,就是選用 ModalPopupExtender 控制項,以強制回應的快顯對話方塊來編輯資料列的內容。本文接下來的主題將帶你瞭解如何把 ModalPopupExtender 控制項整合應用到 GridView 控制項。ModalPopupExtender 控制項是 AJAX Control Toolkit 所提供的擴充性控制項。你可以造訪這裡下載 AJAX Control Toolkit。

本範例會從 AdventureWorks 範例資料庫,使用 GridView 控制項搭配使用 FormView 控制項,建立員工資訊的主要及詳細頁面。你可以從這裡下載範例資料庫。

定義 GridView 的內容
首先,你需定義一個包含 GridView 控制項的更新面板:
<asp:UpdatePanel ID="EmployeesUpdatePanel" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:GridView ID="EmployeesGridView" runat="server" DataKeyNames="EmployeeID"
DataSourceID="EmployeesSqlDataSource" AutoGenerateColumns="False" AllowPaging="True">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name"
SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last Name"
SortExpression="LastName" />
<asp:BoundField DataField="NationalIDNumber" HeaderText="ID Number"
SortExpression="NationalIDNumber" />
<asp:BoundField DataField="Gender" HeaderText="Gender"
SortExpression="Gender" ItemStyle-HorizontalAlign="Center" />
<asp:BoundField DataField="BirthDate" HeaderText="Birth Date"
SortExpression="BirthDate" DataFormatString="{0:yyyy/MM/dd}" />
<asp:BoundField DataField="MaritalStatus" HeaderText="Marital Status"
SortExpression="MaritalStatus" ItemStyle-HorizontalAlign="Center" />
<asp:TemplateField>
<ItemTemplate>
<a href="javascript:showModalPopup(<%# Eval("EmployeeID")%>);">Edit</a>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>

上例中,在 GridView 控制項的資料行中加入樣板欄位,並在其 ItemTemplate 標籤中,加入一個編輯連結用來呼叫 JavaScript 函式,以顯示快顯對話方塊。


定義快顯對話方塊的內容
接下來,就是定義包含強制回應快顯對話方塊內容的面板:
<asp:Panel id="ModalPopupPanel" runat="server" Style="display: none" CssClass="modalPopup">
<asp:UpdatePanel ID="ModalPopupUpdatePanel" runat="server" UpdateMode="Conditional"
ChildrenAsTriggers="False">
<ContentTemplate>
<asp:UpdateProgress ID="ModalPopupUpdateProgress" runat="server"
AssociatedUpdatePanelID="ModalPopupUpdatePanel">
<ProgressTemplate>
<div>Loading ...</div>
</ProgressTemplate>
</asp:UpdateProgress>
<asp:FormView ID="EmployeeFormView" runat="server" DefaultMode="Edit"
DataKeyNames="EmployeeID" DataSourceID="EmployeeDetailsSqlDataSource"
OnItemUpdated="EmployeeFormView_ItemUpdated">
<EditItemTemplate>
<table>
<tr>
<td>First Name:</td>
<td>
<asp:Label ID="FirstNameLabel" runat="server"
Text='<%# Eval("FirstName") %>' />
</td>
</tr>
<tr>
<td>Last Name:</td>
<td>
<asp:Label ID="LastNameLabel" runat="server"
Text='<%# Eval("LastName") %>' />
</td>
</tr>
<tr>
<td>ID Number:</td>
<td>
<asp:TextBox ID="NationalIDNumberTextBox" runat="server"
Text='<%# Bind("NationalIDNumber") %>' />
</td>
</tr>
<tr>
<td>Gender:</td>
<td>
<asp:DropDownList ID="GenderDropDownList" runat="server"
SelectedValue='<%# Bind("Gender") %>'>
<asp:ListItem Value="M">Male</asp:ListItem>
<asp:ListItem Value="F">Female</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>Birth Date:</td>
<td>
<asp:TextBox ID="BirthDateTextBox" runat="server"
Text='<%# Bind("BirthDate","{0:yyyy/MM/dd}") %>' />
</td>
</tr>
<tr>
<td>Marital Status:</td>
<td>
<asp:DropDownList ID="MaritalStatusDropDownList" runat="server"
SelectedValue='<%# Bind("MaritalStatus") %>'>
<asp:ListItem Value="M">Married</asp:ListItem>
<asp:ListItem Value="S">Single</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
</table>
<asp:Button ID="HiddenUpdateButton" runat="server" CommandName="Update"
style="display: none" />
</EditItemTemplate>
</asp:FormView>
<asp:HiddenField ID="EmployeeIDHiddenField" runat="server"
OnValueChanged="EmployeeIDHiddenField_ValueChanged" />
<asp:Label ID="UpdatePanelMessage" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
<div style="text-align: center">
<asp:Button id="OkButton" runat="server" Text="確 定" CommandName="Update"></asp:Button>
<asp:Button id="CancelButton" runat="server" Text="取 消"></asp:Button>
</div>
</asp:Panel>

在面板中,我們加入了包含 FormView 控制項的 UpdatePanel 控制項,並在 FormView 控制項的編輯樣板中,加入隱藏的更新按鈕,這個按鈕將會透過 JavaScript 觸發 UpdatePanel 控制項的更新動作,並透過 FormView 控制項所搭配的 SqlDataSource 控制項來執行員工資料的更新。因為在 ModalPopupUpdatePanel 更新面板中的 ChildrenAsTriggers 屬性被設為 False,所以必須明確呼叫 Update 方法,才能更新面板的內容:
protected void EmployeeFormView_ItemUpdated(object sender, EventArgs e)
{
EmployeesGridView.DataBind();
EmployeesUpdatePanel.Update();
}

而 OkButton 及 CancelButton 按鈕則分別用來定義快顯對話盒中,「確定」及「取消」動作的目標控制項。另外,在 UpdatePanel 控制項中的還加入了隱藏欄位 EmployeeIDHiddenField,用來記錄在 GridView 控制項中被選取員工的主索引鍵值,並加入其 ValueChanged 事件的處理常式:
protected void EmployeeIDHiddenField_ValueChanged(object sender, EventArgs e)
{
ModalPopupUpdatePanel.Update();
}

這個隱藏欄位將透過 JavaScript 來觸發 UpdatePanel 控制項,以載入 FormView 控制項的繫結資料。

加入 ModalPopupExtender 控制項
<asp:Button runat="server" ID="HiddenTargetControlForModalPopup" style="display:none" />
<ajaxToolkit:ModalPopupExtender ID="ModalPopupExtender1" runat="server"
TargetControlID="HiddenTargetControlForModalPopup"
PopupControlID="ModalPopupPanel"
BackgroundCssClass="modalBackground"
DropShadow="true"
OnOkScript="onOk()"
OkControlID="OkButton"
CancelControlID="CancelButton"
/>

加入用戶端程式碼
<script type="text/javascript" language="javascript">
<!--
function onOk() {
__doPostBack('<%= string.Format("{0}$HiddenUpdateButton", EmployeeFormView.UniqueID) %>','');
}

function showModalPopup(v)
{
var modalPopupBehavior = $find('<%= ModalPopupExtender1.ClientID %>');
modalPopupBehavior.show();
var hiddenField = $get('<%= EmployeeIDHiddenField.ClientID %>');
if (hiddenField) {
hiddenField.value = v;
__doPostBack('<%= EmployeeIDHiddenField.UniqueID %>','');
}
}
//-->
</script>

上例中,你會發現在程式碼是使用 $find 函式來取得 ModalPopup 行為類別(Behavior)執行個體的參照。這與 $get 函式又有什麼不同呢?在 Microsoft Ajax Library 中,$get 函式是 Sys.UI.DomElement.getElementById 方法的別名,用來取得 DOM 元素的參照;而 $find 函式則是 Sys.Application.findComponent 方法的別名,它會傳回 Ajax Client Component 執行個體的參照。因為 ModalPopup 亦是 Sys.Component,所以你必須使用 $find 函式才能取得 ModalPopup Behavior 執行個體的參照。但為何又稱 ModalPopup 是行為類別呢?那是因為 ModalPopup 是繼承自 Sys.UI.Behavior 類別的 Component。想瞭解更多有關於 ASP.NET AJAX Client 的相關資訊,可以參考這裡。


 Download source code - 430 KB

繼續閱讀...

認識 JavaScript 的物件導向技術

1 comments
如果要說 JavaScript 是全世界被誤解最深的程式語言,其實一點也不為過。長久以來,它的名稱讓許多人誤以為是 Java 的子集合。當初 JavaScript 是以作為給非程式人員的腳本語言為訴求,所以學習門檻低,很容易就能上手。正因為如此,它也讓許多人停留在粗糙、簡單的既定印象,即便到現在已經進化成一個物件導向程式語言(Object-Oriented Programming Language, OOPL)。

現在,物件導向程式設計(OOP)是已普遍被應用到許多 JavaScript 程式庫(如 AJAX Library)。如果,你要使用這些程式庫,你就有必要深入理解 JavaScript 語言的概念,才能靈活運用它來應付更複雜的 Web 應用程式。

然而 JavaScript 對 OOP 的支援方式,卻與其他以類別為基礎(Class-Based)的物件導向語言大相逕庭。本文接下來,將帶你初探 JavaScript 語言對 OOP 的支援能力。

物件
JavaScript 物件是索引鍵和值配對(Name-Value Pair)的集合。「名稱」的部分必須是字串,而「數值」可以是任何資料型別。它的資料結構跟 C# 中的 Dictionary 和 Hashtable 非常相近。

以下是建立物件的基本方法:
var rectangle = new Object();
rectangle.width = 5;
rectangle.height = 3;
rectangle.getArea = function() {
return width * height;
}
alert(rectangle.getArea()); // Displays "15"

以上範例程式碼同時呼叫 new 和建構函式,會建立新的 Object 物件。然後,才指派屬性及方法給物件變數,而所謂的方法,其實也只是參考到 Function 物件的屬性而已。這個範例顯示,JavaScript 物件的屬性不一定要事先宣告,你可以在任何時候加入額外的屬性。如果你把 JavaScript 物件它當成是在 Dictionary 物件,將指派屬性視同是加入索引鍵和值,應該就不難理解了。

你也可以使用 JavaScript 1.2 版本支援的物件實體語法(Object Literal),來宣告及初始化物件:
var rectangle = {
"width" : 5,
"height" : 3,
"getArea" : function() {
return width * height;
}
};
alert(rectangle.getArea()); // Displays "15"

每個屬性初始設定以逗號分隔的定義方式,與 C# 3.0 的物件初始設定式非常類似,唯一的差別在於這裡只接受字串索引鍵。

當要存取物件的屬性時,你可以使用熟悉的 "." (點)運算子來存取。
rectangle.width = 5;
var width = rectangle.width;

或者,使用 "[]" 運算子來取得及設定物件的屬性。
rectangle["width"] = 5;
var width = rectangle["width"];

當然,你也可以在任何時候移除物件的屬性:
delete rectangle["width"];
delete rectangle.height;

函數也是物件
JavaScript 函式其實就是包含可執行程式碼的 Function 物件。它被視為第一級物件(First-Class Object),這意味它可以被動態建立,儲存在變數、陣列和物件中,也能做為函式的「傳入參數」或「傳回結果」。
function add(x, y) {
return x + y;
}
alert(add(1, 2)); // Displays "3"

當你使用 function 關鍵字宣告函式,其實就是在定義一個 Function 物件。事實上,在執行時期會配置與函數同名的 Function 物件。以上面的範例來說,會建立一個名為 add 的函數物件。所以,你才可以在函數物件變數使用 "()" 運算子呼叫函式程式碼。

除了具名函數外,你也可以宣告匿名(Anonymous)函數,並傳回它的參照:
var add = function(x, y) {
return x + y;
};
alert(add(1, 2)); // Displays "3"

當然,你也可以在宣告的同時,直接呼叫匿名函數,並取得傳回值:
var result = (function(x, y) {
return x + y;
})(1, 2);
alert(result); // Displays "3"

另外,你也可以使用 Function 建構函式建立函數物件:
var add = new Function("x", "y", "return x + y;");
alert(add(1, 2)); // Displays "3"

Function 函數比較特殊,即使沒有使用 new 關鍵字也會產生相同的結果。

在匿名函式中,如果你要在函式內部遞迴呼叫自己,可以使用 arguments.callee 的屬性。當函式被呼叫時,會自動建立包含所有傳遞參數值的陣列物件,並指派給名為 arguments 的區域變數。該物件提供了一個叫做 arguments.callee 的屬性,這個屬性會指向的目前的函式,因此可以用來做遞迴呼叫:
var fso = new ActiveXObject("Scripting.FileSystemObject");
(function(folder) {
var files = new Enumerator(folder.Files);
while(!files.atEnd()) {
document.write(fso.BuildPath(folder.path, files.item().name));
files.moveNext();
}
var subfolders = new Enumerator(folder.SubFolders);
while(!subfolders.atEnd()) {
arguments.callee(subfolders.item());
subfolders.moveNext();
}
})(fso.GetFolder("d:\\"));

以上範例,使用遞迴方式列舉根目錄下的所有子目錄及檔案。

自訂物件
通常,我們所說的物件是指是類別或結構的實體。然而,JavaScript 只有建構函式,卻沒有類別。所謂的建構函式,其實就是一般的函式。最接近類別的方式,是定義如下的建構函式:
function Shape(x, y) {
this.x = x;
this.y = y;
this.getCoordinates = function(){
return "(" + this.x + " ," + this.y + ")";
}
}
var objShape = new Shape(5, 10);
alert(objShape.getCoordinates()); // Displays "(5, 10)"

上例中的 x 、y 是屬性成員,getCoordinates() 是方法成員。在這裡第一次出現尚未提過的 "this" 關鍵字,稍後會有詳細說明。請先看這行程式碼:
var objShape = new Shape(5, 10);

當你使用 "new" 運算子呼叫建構函式時,JavaScript Engine 會執行以下動作:
  • 建立新的 Object 物件。
  • 在 Shape 函式的執行環境中,將所建立的物件參考指派給 this 值。
  • 接著,將此物件的 constructor 屬性指向 Shape 函數,以及將其內部隱含的 __proto__ 屬性指向 Shape 函數物件的 prototype 屬性。
  • 然後,執行 Shape 函式內的程式碼。
  • 最後,傳回物件參考。

請記得,除了 Function 函式外,在一般情況下,只要使用 new 運算子呼叫函式,就會傳回完全初始化的 Object 物件。

JavaScript 的程式碼都是在執行環境(Execution Context)中執行,執行環境包含變數範圍鏈(Scope Chain)的資訊以及呼叫此方法的物件參照(也就是 this 值)。而可執行程式碼又分為 Global Code 、 Function Code 及 Eval Code 三種。不同種類的程式碼,會建立不同的執行環境,而 this 值需視執行環境或呼叫者而定:
  • Global Code
    任何在函式以外(不屬於函式的一部分)的程式碼。只會產生一個 Global 執行環境。這時的 this 值就會是 Global 物件(在瀏覽器中即為 window 物件)。
  • Function Code
    宣告於函式本體的程式碼。每次的函式呼叫都會建立個別的執行環境。如果函式是透過物件參考來呼叫,那麼 this 值就會被設定為該物件參考。若是使用 new 運算子呼叫函式,則 this 值會參考到新產生的 Object 物件。如果是透過函式物件的 call() 或是 apply() 方法來呼叫,則需視傳入的第一個參數而定。
  • Eval Code
    透過 eval() 函式執行的程式碼。其 this 值與所在的執行環境的 this 值相同。

原型繼承
原型物件(Prototype)是 JavaScript 用來模擬類別階層(Class Hierarchy)的中心概念物件。每個函式物件(fucntion 型別的物件)都有一個 prototype 屬性(該物件所擁有的原型物件),它可以用來加入自訂屬性及方法。所謂的原型物件,其實也是使用 Object 建構函式所建立的物件。另外,所有物件實體內部還隱含一個名為 __proto__ 的屬性,它會指向其建構函式的 prototype 屬性。當然,建構函式的 prototype 屬性參照的原型物件也隱含有 __proto__ 屬性,指向它的建構函式的原型物件,以此類推,最後追朔到最終基底原型 Object.prototype 為止。每一個物件都會繼承一整鏈的原型,這樣的鏈結關係稱之為原型鍊(Prototype Chain)。

我們都知道 Object 的原型物件擁有以下的屬性成員:
  • constructor
  • toString()
  • toLocaleString()
  • valueOf()
  • hasOwnProperty(propertyName)
  • isPrototypeOf(objectRef)
  • isPropertyEnumerable(propertyName)

當你建立自訂物件時,將繼承 Object 物件原型的所有屬性和方法。
function MyClass() {

}
var myObject = new MyClass();
alert(myObject.toString()); // Displays "[object Object]"

上例中,我們透過自訂的物件參照呼叫 toString() 方法。事實上,這個方法是來自 Object 的原型物件。那麼 JavaScrpt 是如何解析呢?當你試圖要存取物件的屬性/方法時,沒有定義在物件中,那麼 JavaScript 就會檢查該物件的原型。如果還是沒有,就會循我們前面所提到的原型鏈往上尋找,直到 Object.prototype 為止。

現在,你已暸解 JavaScript 是如何以原型鏈來模擬類別階層關係。接下來,我們就可以利用原型物件來實做衍生類別:
function Shape(x, y) {
this.x = x;
this.y = y;
}
Shape.prototype = {
getCoordinates : function() {
return "(" + this.x + " ," + this.y + ")";
}
};
function Rectangle(x, y, width, height) {
Shape.call(this, x, y);
this.width = width;
this.height = height;
}
Rectangle.prototype = new Shape();
Rectangle.prototype.getArea = function() {
return this.width * this.height;
};
var objRect = new Rectangle(100, 200, 5, 10);
alert(objRect.getCoordinates()); // Displays "(100, 200)"
alert(objRect.getArea()); // Displays "50"

在上例中,Shape 可視為基底類別(Base Class),而 Rectangle 是衍生類別(Derived Class)。其中,繼承的關鍵在於設定 Rectangle.prototype 屬性,將 Shape 物件加入 Rectangle 的原型鏈。另外,在 Rectangle 建構函式中,透過函數物件的 call() 方法,呼叫 Shape 建構式來進行初始化。

結語
為什麼要使用 OOP ?說穿了,就是為了能夠讓程式設計更接近人類的自然思維,讓程式碼易於擴充與維護。 當你使用 OOPL 撰寫程式碼時,你就已經是在 "OOP" 了。正所謂「只在此山中,雲深不知處」,差別只在於你對物件導向是否有所體認罷了。誠如 Kenming 在「不要從程式語言學習「物件導向」!」一文中提到:

因為,物件導向是一種思維,是一種哲理,是一種典範,甚至是一種生活觀,你需要綜合相當多的知識,蘊化為 "智慧",來協助你如何應付與應對軟體的 "善變",並能提供具體的解決方案。

雖然 JavaScript 對 OOP 可提供很好的支援,但對習慣類別型程式設計的人來說,或許會有很大的落差。所幸,未來 JavaScript 2.0 版本將朝向類別型語言發展。不過,在此之前如果你希望使用 JavaScript 撰寫更具彈性的物件導向程式,可以參考 MooTools 所提供的程式庫,它提供了絕佳的類別函式,讓你可以更容易撰寫類別式的 JavaScript 程式碼。

參考資料:
使用物件導向技術來建立進階 Web 應用程式
Javascript Closures

繼續閱讀...

使用 AJAX 動態載入縣市和地區

2 comments
在 Web 應用程式中,常常都需要提供可以讓使用者登錄個人資料(如姓名、聯絡電話、通訊地址)的互動式網頁。以地址輸入欄位來說,通常還會加入縣市及鄉鎮區的連動式下拉選單(Cascading Drop-Down)供使用者選擇。在 ASP.NET 中,傳統作法是使用兩個 DropDownList 控制項,並透過 AutoPostBack 的方式擷取選取項目,再動態將資料填入 DropDownList 控制項,來達到多層連動下拉選單的效果。不過,這樣的缺點是每次變更清單選取項目時,都會造成自動向伺服器發出網頁請求,增加網路傳輸量。所幸,你可以選擇 ASP.NET AJAX Control Toolkit 的 CascadingDropDown 控制項,來做為替代方案。

ASP.NET AJAX 已內建於 ASP.NET 3.5,所以只要下載 AJAX Control Toolkit 元件即可。如果是 ASP.NET 2.0 則必須先安裝 ASP.NET 2.0 AJAX Extensions 1.0,才可以使用 ASP.NET AJAX Control Toolkit。你可以造訪 ASP.NET AJAX 官方網站取得相關下載。

ASP.NET AJAX Control Toolkit 同時也提供了完整的範例程式碼,除了可以讓你快速學習外,也可以立即引用到你的 ASP.NET 開發專案。本文程式碼亦是以 CascadingDropDown 範例為基礎修改而來。接下來,將使用 ASP.NET 3.5 來說明如何使用 CascadingDropDown 控制項,並搭配 Web 服務來動態載入縣市及鄉鎮區資料項。

建立資料來源
在這裡我們使用 XML 文件來儲存縣市及鄉鎮區的資料,如以下所示:
<?xml version="1.0" encoding="utf-8" ?>
<PostCodeService>
<county name="台北市">
<town name="中正區" value="100"/>
<town name="大同區" value="103"/>
<town name="中山區" value="104"/>
<town name="松山區" value="105"/>
<town name="大安區" value="106"/>
<town name="萬華區" value="108"/>
<town name="信義區" value="110"/>
<town name="士林區" value="111"/>
<town name="北投區" value="112"/>
<town name="內湖區" value="114"/>
<town name="南港區" value="115"/>
<town name="文山區" value="116"/>
</county>
</PostCodeService>

建立 Web Service
為了讓 AJAX 用戶端指令碼能存取 Web 服務,我們必須建立 .asmx Web 服務,並且以 ScriptServiceAttribute 屬性限定 Web 服務類別。
using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.Services;
using System.Xml;
using AjaxControlToolkit;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class PostCodeService : System.Web.Services.WebService
{
private static XmlDocument _document;
private static object _lock = new object();

public static XmlDocument Document
{
get
{
lock (_lock)
{
if (_document == null)
{
_document = new XmlDocument();
_document.Load(
HttpContext.Current.Server.MapPath("~/App_Data/PostCodeService.xml"));
}
}
return _document;
}
}

public static string[] Hierarchy
{
get { return new string[] { "county", "town" }; }
}

[WebMethod]
public CascadingDropDownNameValue[] GetDropDownContents(string knownCategoryValues,
string category)
{
StringDictionary knownCategoryValuesDictionary =
CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
return CascadingDropDown.QuerySimpleCascadingDropDownDocument(
Document, Hierarchy, knownCategoryValuesDictionary, category);
}
}

以上程式碼使用 Singleton Pattern 建立 XmlDocument 物件來載入 XML 文件,並加入 lock 陳述式以確保只會產生唯一個實例(Instance)。

AJAX 用戶端網頁
<table>
<tr>
<td>縣市:</td>
<td><asp:DropDownList ID="CountyDropDownList" Width="100" runat="server" /></td>
</tr>
<tr>
<td>鄉鎮區:</td>
<td><asp:DropDownList ID="TownDropDownList" Width="100" runat="server" AutoPostBack="true"
OnSelectedIndexChanged="TownDropDownList_SelectedIndexChanged" /></td>
</tr>
<tr>
<td>郵遞區號:</td>
<td>
<asp:UpdatePanel ID="PostCodeUpdatePanel" runat="server" UpdateMode="Conditional"
RenderMode="inline">
<ContentTemplate>
<asp:TextBox ID="PostCodeTextBox" Width="100" runat="server"></asp:TextBox>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="TownDropDownList"
EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
</td>
</tr>
</table>
<ajaxToolkit:CascadingDropDown ID="CountyCascadingDropDown" runat="server" TargetControlID="CountyDropDownList"
Category="county" PromptText="請選擇縣市" LoadingText="載入中..."
ServicePath="PostCodeService.asmx" ServiceMethod="GetDropDownContents" />
<ajaxToolkit:CascadingDropDown ID="TownCascadingDropDown" runat="server" TargetControlID="TownDropDownList"
Category="town" PromptText="請選擇鄉鎮區" LoadingText="載入中..."
ServicePath="PostCodeService.asmx" ServiceMethod="GetDropDownContents" ParentControlID="CountyDropDownList" />


 Download source code - 430 KB for ASP.NET 3.5

繼續閱讀...

Regular Expression Examples

0 comments
對於要做複雜的文字處理的應用程式,規則運算式(Regular Expression)提供有效率、功能強大的解決方法,是不可或缺的工具。話雖如此,要熟記規則運算式的所有技巧並不容易。為方便隨查即用,在此把自己常用的規則運算式範例作個總整理。

浮點數
^([-+]?[0-9]*\.?[0-9]+)$

下列程式碼範例會驗證輸入字串是否為有效的浮點數。
bool IsValidFloat(string inputString)
{
return Regex.IsMatch(inputString, @"^([-+]?[0-9]*\.?[0-9]+)$");
}

日期格式
^((?:19|20)\d\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$

下列程式碼範例會驗證輸入字串是否為有效的日期。
bool IsValidDate(string inputString)
{
Match m = Regex.Match(inputString,
@"^((?:19|20)\d\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$");
if (m.Success)
{
int year = int.Parse(m.Groups[1].Value);
int month = int.Parse(m.Groups[2].Value);
int day = int.Parse(m.Groups[3].Value);
if (day == 31 && (month == 4 || month == 6 || month == 9 || month == 11))
{
return false;
}
else if (day >= 30 && month == 2)
{
return false;
}
else if (month == 2 && day == 29
&& !(year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)))
{
return false;
}
else
{
return true;
}
}
return false;
}

電子郵件格式
^([0-9a-z]+[-._+&])*[0-9a-z]+@([0-9a-z]+[.])+[a-z]{2,3}$

下列程式碼範例會驗證輸入字串是否為有效的電子郵件格式。
bool IsValidEmail(string inputString)
{
return Regex.IsMatch(inputString, @"^([0-9a-z]+[-._+&])*[0-9a-z]+@([0-9a-z]+[.])+[a-z]{2,3}$", RegexOptions.IgnoreCase );
}

中文字
[\u4e00-\u9fa5]

擷取 HTML 元素
<([a-z]+)\b[^>]*>(.*?)</\1>

下列程式碼範例會從輸入字串中去除 HTML 標籤。
string StripHtml(string inputString)
{
return Regex.Replace(inputString,@"</?[a-z]+\b[^>]*/?>", string.Empty, RegexOptions.IgnoreCase);
}

擷取 HTML 標籤的屬性
ATTR\s*=\s*(?:"(?<1>[^"]*)"|(?<1>\S+))

下列範例會從 HTML 格式的輸入字串中比對 img 標籤,並擷取出 src 屬性,透過替代的方式將屬性值套用到有加入 onload 屬性及指令碼的 img 標籤,以限制圖片最大寬高。
string FormatPost(string message)
{
message = Regex.Replace(message, @"<img\b*[^>]*?src\s*=\s*(?:""(?<1>[^""]*)""|'(?<1>[^\']*)')\s*/?\s*>", @"<img onload=""javascript:rmwa_img_loaded(this, 600, 200)"" src=""$1"" />", RegexOptions.IgnoreCase);
return message;
}


相關參考資源:
MSDN 規則運算式語言項目
Regular Expression Library
Regular-Expressions.info

繼續閱讀...