asp.net

  • 716
  • 0
  • 2022-07-04

asp.net

ASP.NET 由淺入深 :: 2010 iT 邦幫忙鐵人賽 https://ithelp.ithome.com.tw/users/20010292/ironman zh-TW Mon, 06 Jun 2022 21:14:42 +0800 91之ASP.NET由淺入深 不負責講座 Day30 - ORM introduction https://ithelp.ithome.com.tw/articles/10057249?sc=rss.iron https://ithelp.ithome.com.tw/articles/10057249?sc=rss.iron 前面29天,大致上已經把.Net 2.0 Webform的基礎與一些概念都介紹完了。

接下來的模組其實都比較獨立出來,今天要講的是O/R mapping的簡介。
ORM...]]> 前面29天,大致上已經把.Net 2.0 Webform的基礎與一些概念都介紹完了。

接下來的模組其實都比較獨立出來,今天要講的是O/R mapping的簡介。
ORM可以寫成一整書也不嫌少,所以我這邊只先蜻蜓點水的點出一點概念。
1.定義
Object Relational Mapping,物件與關連式資料庫對應技術

2.簡單的說
(1)將原本資料庫中的資料表(可能是根據幾個資料表的關係,組成的某個資料集合)對應為一個物件集合,將資料列對應為一個物件,將欄位對應成物件的一個屬性。
(2)讀取關連式資料庫資料出來後,經過ORM,就可以透過物件的方式去使用該資料集合。
(3)也可透過物件的方式,定義物件的行為,而來對關連式資料庫進行CRUD。

3.目的
(1)developer面對的是物件,而不是一堆資料表、資料列。
物件、類別的定義,可以回頭想想,類別是抽象的集合。有屬性、有行為,有OO的特性,絕對比攤開看不出關係的dataset好懂,reuse也會比較高,也會比較穩定跟抽象。

(2)Domain model mapping
可以根據domain model設計出更穩定的系統

(3)透過ORM定義的語法,比起SQL來說,程式的可維護性更高

(4)可攜性
會透過ORM framework,可以把注意力focus在物件的操作與使用,而不用因為不同的DBMS就寫不同套的方法做一樣的事。
比較正式的ORM framework會將自己定義的語法,根據不同的DBMS轉成對應可執行的SQL statement。

4.注意
(1)performance問題
開發效率提升,但執行效率容易有隱憂。轉換SQL對應的問題,通常結果不會錯,但效率可能會很差。所以要去瞭解最後轉出來的SQL statement有沒問題。

5.迷思
學會ORM就不需要學SQL了?學jQuery也得懂JavaScript本身的特性、意義跟用法。
ORM只是關連性資料庫與物件中間的橋樑,而SQL才是資料庫的語言,ORM只是協助轉換。
所以SQL的觀念、基本語法還是都要具備,用起ORM才能得心應手,才能用的更正確、更精準。

6.比較常見的ORM framework
(1)NHibernate
(2)LINQ to SQL
(3)ADO.NET Entity framework: 通常使用LINQ to Entity Framework

]]> 就是91 2010-10-27 18:17:29
91之ASP.NET由淺入深 不負責講座 Day29 - 泛型 https://ithelp.ithome.com.tw/articles/10056974?sc=rss.iron https://ithelp.ithome.com.tw/articles/10056974?sc=rss.iron 經過Day25~Day28 Ajax增加user experience的洗禮後,
我們要再把注意力拉回.net framework的基礎: 泛型。

這其實有點難描述,因...]]> 經過Day25~Day28 Ajax增加user experience的洗禮後,
我們要再把注意力拉回.net framework的基礎: 泛型。

這其實有點難描述,因為會繞到更多的基礎,最後可能會從型別開始講。

這邊,太基礎的部分,我會附上reference來當作參考。
1.介紹
(1)CLR 2.0的新功能
(2)使用泛用型別以最佳化程式碼重複使用、型別安全性和效能。
(3)泛型的最常見用法是建立集合類別。
(4).NET Framework 類別庫包含 System.Collections.Generic 命名空間中的數種新泛用集合類別。在任何可能的情況下都應該使用這些類別取代類別 (例如在 System.Collections 命名空間中的 ArrayList)。
(5)您可以建立自己的泛用介面、類別、方法、事件和委派 (Delegate)。
(6)泛用類別可能會被限制為啟用對特定資料型別上的方法進行存取。
(7)在泛型資料型別中使用的型別資訊,可以在執行階段透過Reflection取得。

2.Boxing & Unboxing
(1)相關:
實值型別
參考型別
(2)簡單的說:實值型別與object型別的轉換
(3)還是很難懂嗎?我們拿一個實體的例子來做說明,大家都有聽過周杰倫的半島鐵盒嗎?
J:小姐,請問有賣半島鐵盒嗎?
店員:有啊,你從前面右轉的第二排架子上就有了。

半島鐵盒,是一個實體的東西,我們可以把他當作"實值型別"來看。
前面右轉第二排架子上,我們可以當作這是"參考型別的記憶體位址"

半島鐵盒這個東西,放到某個架子上(或裝在某個盒子裡),這就是boxing
要取值的時候得先知道架子的位置走過去架子那邊(或把箱子打開)才能取出值取出前要檢查是不是我們要的半島鐵盒,是的話再把它拿出來,這就是unboxing

想像一下,架子,就是我們的Array。
Array裡面可能放著半島鐵盒、全島鐵盒、中島鐵盒、長島鐵盒還有一支烏龜...
(烏龜是不能轉型成鐵盒的...)

(4)boxing & unboxing的問題
-型別轉換的錯誤
-效能的消耗
-開發時編譯無法得知錯誤,要到run time才知道
-參考型別Null的問題

3.泛型的優點
(1)解決大部分boxing與unboxing的問題
-安全型別,避免轉型失敗的風險
-避免boxing與unboxing效能消耗問題
-編譯時期就能知道是否有型別錯誤

(2)同樣的function,針對不同型別,可以只寫一份
例如factory pattern透過Reflection產生任意型別

    public static T CreateProduct<T>(string product) where T : class
    {
        var obj = (T)Assembly.Load(typeof(T).Assembly.FullName).CreateInstance(typeof(T).Assembly.GetName().Name + "." + product); 
        return obj;
    }

4.補充
(1)各Collection陣列個別特性列表
(2)泛型集合對應

]]> 就是91 2010-10-26 15:31:26
91之ASP.NET由淺入深 不負責講座 Day28 - jQuery with Ajax in ASP.NET https://ithelp.ithome.com.tw/articles/10056727?sc=rss.iron https://ithelp.ithome.com.tw/articles/10056727?sc=rss.iron 前面幾篇文章提到了AJAX的概念,以及過渡時期偽裝型AJAX的UpdatePanel,
那究竟真正的ajax該怎麼寫呢?XMLHttpRequest?CallBack?

前面幾篇文章提到了AJAX的概念,以及過渡時期偽裝型AJAX的UpdatePanel,
那究竟真正的ajax該怎麼寫呢?XMLHttpRequest?CallBack?

既然是從JavaScript為出發點去對Server進行Request,且將response的結果bind到DOM上,
那我們當然就從jQuery當作切入點囉。

jQuery幫我們把Ajax的功能包裝起來,讓我們可以很方便、直覺的使用Ajax的功能。

然而網路上與書籍的範例,大部分server端的處理都是PHP的,我們這篇文章就要介紹怎麼用jQuery的Ajax跟ASP.NET做結合。
jQuery官網,請參考:http://jquery.com/
我們這篇文章直接用實例來進行說明:

1.需求
範例的需求是,我們有兩個textbox,一個是ID,一個是Name, 當輸入ID改變時,要帶出Name的資料。(學會之後,就可以擺脫AutoPostBack=true,Text_Changed的方式啦...)

2.Server端處理Request方式
我把ID帶Name的程式碼,分別用三種方式來處理
(1).ashx(泛型處理常式)
(2).aspx(一般網頁頁面)
(3).asmx(Web Service)
為方便各位下載sample code後可以直接跑,
這邊的Name,就是取first name,加上js傳過來的第二個參數MiddleName:『middle』字樣,
last name的部分,則是測試Session值能否被Server端讀取到,所以再補上Session的值『91』來紀念代表從server端回傳的Name。

3.Server端處理Request作法,及Response方式
(1).ashx
-先新增一個泛型處理常式檔案,記得引用System.Web.SessionState與實作IReadOnlySessionState,我們才能在程式碼裡面讀到Session。
-接參數的部分,使用context.Request.Form["參數名稱]
-最後return的值,則是使用context.Response.Write(回傳值)
-圖示程式碼

(2).aspx
-先新增一個.aspx,接著只保留第一行<%@ Page %>那行
-在Page_Load的事件撰寫要處理的程式碼
-接參數的部分,使用的是Request.Form["參數名稱]
-最後return的值,則是直接Response.Write(回傳值)
-圖示程式碼

(3).asmx
-先新增一個.asmx,接著把[System.Web.Script.Services.ScriptService]取消註解
-新增一個public的method,叫做GetFullName,method上頭加上[WebMethod(EnableSession=true)],才能使用Session
-return的資料直接return即可,雖然return的型別是string,但是javascript實際接到的是xml格式的字串
-圖示程式碼

4. .aspx上的sample code
有了這三種處理ID回傳Name的程式後, 來看看我們的頁面要怎麼撰寫。網頁的部分,用三組來當試驗:

    <form id="form1" runat="server">
    <div>
        .ashx: first name<asp:TextBox ID="txtIDbyAshx" runat="server"></asp:TextBox>
        full name<asp:TextBox ID="txtNamebyAshx" runat="server"></asp:TextBox><br />
        
        .aspx: first name<asp:TextBox ID="txtIDbyAspx" runat="server"></asp:TextBox>
        full name<asp:TextBox ID="txtNamebyAspx" runat="server"></asp:TextBox><br />
        
        .ashx: first name<asp:TextBox ID="txtIDbyAsmx" runat="server"></asp:TextBox>
        full name<asp:TextBox ID="txtNamebyAsmx" runat="server"></asp:TextBox><br />
    </div>
    </form>

5.Client端使用jQuery的ajax功能,注意的地方:
(1)註冊在change事件
(2)Ajax option
-type:使用post
-url的部分,web service需加上method名稱
-傳入參數的部分,可用json,或一般字串使用『&』串接,如同一般的post或QueryString
-ajax成功的話,會進入success,失敗會進入error
-成功回傳的字串,在success的function(回傳值)
-web service回傳為XML格式,需自行parse使用

(3)圖示程式碼:

6.結論
ajax的能力未來一定得具備,因為不管是一般的html、畫面冗重的Web form、或是ASP.NET MVC,要等待Post的過程,對User來說都是種折磨。

既然沒有多難,就順手把它學起來吧!

In 91 it!! Sample Code: jQueryAjax.zip

]]>
就是91 2010-10-25 18:22:50
91之ASP.NET由淺入深 不負責講座 Day27 - AJAX.NET(3) UpdatePanel簡介 https://ithelp.ithome.com.tw/articles/10056464?sc=rss.iron https://ithelp.ithome.com.tw/articles/10056464?sc=rss.iron 在上一篇文章介紹到了什麼是AJAX,以及為什麼會需要AJAX這個技術後,
我們這一篇文章要來介紹『無痛,但算偽裝型AJAX的UpdatePanel』。

上一篇文章一直強...]]> 在上一篇文章介紹到了什麼是AJAX,以及為什麼會需要AJAX這個技術後,
我們這一篇文章要來介紹『無痛,但算偽裝型AJAX的UpdatePanel』。

上一篇文章一直強調一點,UpdatePanel != AJAX,其實是指不完全相等。
嚴格說起來應該是『UpdatePanel = Partial Render by AJAX』
這篇文章我們會介紹一下,什麼是Partial Render。
1.UpdatePanel簡介
(1)出發點:
在不影響傳統網頁架構底下,最快速且容易的方式,將傳統HttpRequest,透過Scriptmanager與UpdatePanel,轉用XMLHttpRequest要求server端資料。

(2)迷思:
並非只有送出AJAX需要的資料給server端,而是仍將全頁資料送出給server處理,只是以非同步方式部分呈現(Partial Render)。

2.使用UpdatePanel操作網頁流程,我們用圖來說明:

送出(submit)資料時,會被AJAX.NET library攔截,轉由AJAX.NET以XMLHttpRequest去對server進行request跟response。(但這個時候其實已經把form上所有資料submit,也會經過所有server端的生命週期)最後針對選取的控制項(欲更新的範圍)HTML,進行重新Render。因為通常只有部分範圍,所以被稱為『部分呈現』(Partial Render)。

3.使用UpdatePanel的網頁事件流程,如圖所示:

4.使用UpdatePanel與否的效能差異
因為使用UpdatePanel,網頁上的標記會變多,還要額外加上將Submit要求轉成XMLHttpRequest處理的程式碼,所以要求的大小會比一般的網頁大,但是由於採用「部分呈現」,僅需要修改UpdatePanel涵蓋範圍的HTML,所以回應的大小端看UpdatePanel裡的標記內容大小有多少。

這邊指的最佳化則是指將UpdatePanel涵蓋且需要部分更新的內容大小降到最低,可以不更新的部分就不要放在UpdatePanel裡。

我們用一張圖表來看一下效能差異:

5.AJAX架構-設計方法選擇
這邊以MSDN上的表格來說明,根據需要的情況可以採用不同AJAX程度的技術或架構來設計網頁。

6.補充『遠端方法呼叫』
PageMethod:
(1)ScriptManager的EnablePageMethods屬性設定為True
(2)後置程式碼method需加上[System.Web.Services.WebMethod] 修飾語,並宣告成靜態方法(Static or Share)。
(3)Javascript呼叫方式:PageMethods.MethodName(參數,CallbackFunction)。
(4)注意:無法使用在User Control與Custom Control裡。
(5)可參考:[ASP.NET AJAX]PageMethod:用javascript呼叫server端的method

Web Service
(1)asmx
(2)WebMethod修飾語
(3)ScriptManager的Services屬性(也就是ServiceReference),加入欲參考的asmx
(4)Javascript呼叫方式:Service的類別名稱.MethodName(參數, CallbackFunction)

]]> 就是91 2010-10-24 17:03:32
91之ASP.NET由淺入深 不負責講座 Day26 - AJAX.NET(2) https://ithelp.ithome.com.tw/articles/10056316?sc=rss.iron https://ithelp.ithome.com.tw/articles/10056316?sc=rss.iron 在講UpdatePanel是一種偽裝型的AJAX之前,我想先用一篇文章來說明什麼是AJAX,以及相關的一些概念。(原本想放在一起的,不過應該一定會超過size)

這邊的AJAX就不...]]> 在講UpdatePanel是一種偽裝型的AJAX之前,我想先用一篇文章來說明什麼是AJAX,以及相關的一些概念。(原本想放在一起的,不過應該一定會超過size)

這邊的AJAX就不侷限於AJAX.NET了,而是http的非同步Request機制。

不過一些圖檔還是會先以ASP.NET為server示意圖。
1.AJAX
AJAX的全名:Asynchronous JavaScript and XML(非同步 JavaScript 和 XML),將一些既有的技術包裝在一起,其中包括了
(1)XHTML and CSS
(2)The Document Object Model (DOM)
(3)JavaScript
(4)XML and XSLT
(5)The XMLHttpRequest object

新一代的瀏覽器(IE5以後)含有能從 JavaScript 使用的 XMLHttpRequest 物件,能回呼至伺服器。如此一來,網頁能對使用者輸入做出反應,執行 Out-of-Band 作業,而不需重整整個網頁。大致上說來,這個概念很簡單,但是當要撰寫用戶端 JavaScript 來與伺服器溝通,並處理從 Web 服務傳回的 XML 時,AJAX Libraries 能大幅簡化這些棘手的工作。

2.非同步的緣由
AJAX 所嘗試解決的問題,一般來說是 HTTP 通訊協定本身的問題。HTTP 是瀏覽器在與網頁伺服器通訊、要存取頁面和回傳資料時,所採用的標準。這個通訊協定是無狀態的,也就是說,網頁重整期間的使用者輸入保留的工作是由伺服器上的程式碼來掌管。使用者一般體驗到的是,網頁會整個重整,然後把ViewState帶回伺服器,之後,網頁上的使用者輸入會在傳送回瀏覽器的 HTML 裡面還原。

3.HTTP的運作模式與特性
(1)Browser與Server之間的傳輸協定
(2)無狀態 (請參考一開始介紹PostBack的文章)
(3)網頁重整期間由Server端程式保留
(a)輸入後網頁重整
(b)狀態資訊帶回伺服器
(c)新的資料與使用者輸入的資訊,傳送回Browser,透過隱藏的ViewState還原狀態
(d)Postback後,畫面閃一下變白,出現新的網頁

4.操作網頁過程
(1)User的操作只想更新一部份網頁
(2)HTTP卻仍傳輸整個網頁的HTML
(3)螢幕立刻變成空白(後端處理中)
(4)User與網頁互動中斷,需等待
(5)等Browser接收顯示新的ViewState後才能繼續動作

結果:網頁內容一長,頻寬太少、速度太慢時,小小的動作,Crush!

5.XMLHttpRequest
(1)IE5內建的ActiveX 物件,初次用於 Outlook Web Access
(2)提供client端同http服務器通訊的協議
(3)客戶端可以通過XmlHttp對象 (MSXML2.XMLHTTP.3.0)向http服務器發送請求並使用Document Object Model (DOM)處理回應
(4)雖然 XMLHttpRequest 以 XML 開頭,其實 Request / Receive 並不需要一定是 XML 的格式,只要是文字便可使用
(5)可以更新網頁的部分內容而不需要刷新整個頁面

AJAX 之所以能改善使用經驗,是因為它使用 XMLHttpRequest 物件來回呼至伺服器,叫用 Web 服務,而非重整整個網頁,而且更新的網頁部分後來會根據所接收的 XML/JSON,直接在 JavaScript 中修改。
使用者並不會查覺到網頁更新,而且當 Out-of-Band 作業在背景非同步進行之際,仍可繼續讀取網頁或與網頁進行互動。

6.同步與非同步
(1)同步:整個頁面必須等到 Server 傳回資料之後,才會繼續處理(才能繼續發送後續的Request)
(2)非同步:不需要等待 Server 端傳送資料完畢,使用者仍然可以在頁面上繼續動作(連續發送Request)。以 setTimeout (計時器)為例,當執行了 setTimeout,不會有任何的等待時間、而會繼續執行下面的程式碼,等待時間到後,才會執行指定的 function。

7.AJAX的精神就是非同步的XMLHttpRequest
AJAX運作:
(1)由 Web Page 觸發 Javascript
(2)Javascript 呼叫 XMLHttpRequest 物件
(3)XMLHttpRequest 物件發出一個非同步的 Request 給 Server(此時 Javascript 會繼續往下執行)
(4)一旦 Server 輸出內容、就將所輸出的字串傳回給 XMLHttpRequest 物件
(5)XMLHttpRequest 物件會去尋找預先定義好的 Callback Function(這個 Callback Function 是負責處理成功回應的後續動作)
(6)將處理成功的反應顯示在網頁(DOM)上(端看 Callback 如何處理)

把submit的post動作(也就是httpRequest),改由client script去呼叫xmlhttpRequest物件,發出非同步Request,透過JSON/XML的格式傳遞所需資訊給server。
此時Server正在處理所需資料,User仍可在Browser繼續操作網頁。
等Server資料處理完後,一樣回傳JSON/XML格式,給client端的XMLHttpRequest物件。
XMLHttpRequest物件接收到回傳的JSON/XML後,呼叫client script上定義的callback function,將server回傳的資料,透過操作DOM,顯示在網頁上。

8.傳統頁面與AJAX
AJAX的主從式互動,甩開整個網頁重整後的方式。在擷取初始 HTML 以後,對伺服器的後續呼叫會在 XML、JavaScript Object Notation (JSON) 或 HTML 程式碼片段中收到更新的資料,進而累進地更新網頁。
背景的非同步呼叫能夠叫用 Web 服務或擷取網頁變更,使用者不會感受到任何中斷。這些非同步呼叫會管理後繼伺服器回傳的更新檢視狀態資訊,當必須重整整個網頁時,正確的網頁狀態會帶到伺服器。

傳統Web與AJAX不同的運作方式如下圖所示:

9.非同步要求與回應
我們簡單用兩張圖來示意:
(1)Process:

(2)Data transfer:

10.結論
簡單的說,AJAX就是一種技術,可以透過JavaScript對Server進行Request,透過字串(包括XML、JSON、SOAP等等等...)的格式來進行傳接資料。而這個動作是非同步的,由user觸發頁面上client端的事件,對server端進行Request,所以不會影響到user在操作原本的頁面。當server端Response資料回來後,會呼叫JavaScript的某個CallBack function,將資料binding到相關的DOMs上,或是進行UI畫面上的處理。

]]> 就是91 2010-10-23 18:44:16
91之ASP.NET由淺入深 不負責講座 Day25 - AJAX.NET(1) https://ithelp.ithome.com.tw/articles/10056085?sc=rss.iron https://ithelp.ithome.com.tw/articles/10056085?sc=rss.iron 講完了設計web site常用的UserControl, MasterPage以及預先設計的BasePage。
接著就是要增加user experience的設計了。

...]]> 講完了設計web site常用的UserControl, MasterPage以及預先設計的BasePage。
接著就是要增加user experience的設計了。

今天我們要介紹的是AJAX.NET (這應該是最新的名字),也就是包含了ScriptManager, UpdatePanel以及Timer的簡介。

請注意,UpdatePanel != AJAX,這一點一定要知道。

我們會在下一篇文章,來介紹Why UpdatePanel跟想像中的AJAX不一樣。
1.ScriptManager
(1)兼具Server Control與Client Script framework的角色
(2)也可以使用在AjaxControlToolkit裡面的ToolkitScriptManager,可以參考: http://www.dotblogs.com.tw/hatelove/archive/2009/02/17/7211.aspx
(3)只能有一個,而且要最早引用(要在使用任何ScriptManager提供的方法之前引用)

2.UpdatePanel
(1)企圖達到AJAX功能的Server control
(2)namespace: System.Web.Extension
(3)UpdateMode
(a)UpdateMode="Conditional“
(b)UpdateMode=“Always“,Default value
(4)驅動方式
(a)Trigger
(b)UpdatePanelID.Update()
(5)可以為巢狀UpdatePanel
(6)要注意避免使用,完全包覆網頁的UpdatePanel (performance只有下降)

3.Timer
(1)功能:定時postback,觸發事件委派方法
(2)可透過UpdatePanel來達到非同步的效果,但仍是全頁submit

4.結論
AJAX.NET裡面,最重要,也是一定要知道的一點,UpdatePanel != AJAX,UpdatePanel只能稱的上"Partial Render",部分呈現,所以UpdatePanel並不會為performance帶來戲劇化的改善,包括user experience也是。

簡單的說,UpdatePanel的行為,仍是全頁(form)submit,但最後的Render透過轉發為Ajax的方式,僅修改欲影響的範圍內的原始碼。

詳細的原理,我會在下一篇文章進行說明,敬請期待。

]]> 就是91 2010-10-22 16:29:59
91之ASP.NET由淺入深 不負責講座 Day24 - BasePage https://ithelp.ithome.com.tw/articles/10055893?sc=rss.iron https://ithelp.ithome.com.tw/articles/10055893?sc=rss.iron 介紹了在開發系統很常會用到的UserControl與MasterPage後,接著來介紹我很常設計的一個類別: BasePage。

BasePage是我自己建的一個類別,繼承自Sys...]]> 介紹了在開發系統很常會用到的UserControl與MasterPage後,接著來介紹我很常設計的一個類別: BasePage。

BasePage是我自己建的一個類別,繼承自System.Web.UI.Page,接著有需要使用到BasePage共通性的頁面,就都繼承於BasePage。

可以降低重複開發,統一修改,多墊一層super class,雖非抽象介面,但Page是一個特別的class,所以super class並不會造成太多class與class之間的問題。只有繼承鏈的強耦合問題。
Issues
1.繼承自System.Web.UI.Page

2.頁面的super class

3.可自訂function , property跟event(搭配頁面生命週期做變化)

4.統一處理共同的資訊,如
(1)Global logger
(2)存取設定檔或Resource檔,例如Message多國語言
(3)註冊JavaScript的function
(4)每一個繼承BasePage子頁面都需要的畫面處理

5.與MasterPage的差異
(1)focus
MasterPage:處理的共同property與function,都是focus在跟MasterPage上UI有關的東西
BasePage:處理的通常是比較非UI,但每一支頁面幾乎會用到的共同function
(2)特性
MasterPage:繼承自UserControl
BasePage:通常繼承System.Web.UI.Page (也有可能繼承於另一個自訂的BasePageX.cs)

6.同樣要注意的特點
(1)每頁都套用MasterPage或繼承BasePage,代表上面有寫的都會跑,要小心過於肥大,影響performance。單一職責原則在這邊也同樣適用。
(2)繼承就是侵入性的強耦合,繼承有的問題,在這邊使用上也有可能會出現。介面隔離原則跟依賴反轉原則,用來解耦的方式,在這邊也同樣適用。

]]> 就是91 2010-10-21 19:43:21
91之ASP.NET由淺入深 不負責講座 Day23 - UserControl與MasterPage https://ithelp.ithome.com.tw/articles/10055599?sc=rss.iron https://ithelp.ithome.com.tw/articles/10055599?sc=rss.iron 經過了Day14~Day22的洗禮,相信大家已經腦袋裡面都是OO來OO去,抽象來抽象去了...
接下來我們又要介紹比較實作面的東西了。

今天要介紹的是UserContr...]]> 經過了Day14~Day22的洗禮,相信大家已經腦袋裡面都是OO來OO去,抽象來抽象去了...
接下來我們又要介紹比較實作面的東西了。

今天要介紹的是UserControl跟MasterPage,它們之間也有繼承與封裝的概念,
所以前面的OO概念,是不會白白浪費的。

至於SOLID原則,則是寫code的過程,應該要要求自己程式品質時的一個基準點。
UserControl
1.副檔名為.ascx,與網頁類似,但不能直接連接至user control

2.需要設計對外的property、function跟event,典型的封裝概念

3.也有control的事件生命週期

4.實作了INamingContainer:
識別在 System.Web.UI.Page 物件的控制項階層架構內建立新 ID 命名空間的容器控制項。
也就是放在UserControl裡面的控制項,ID會再加上container的ID當prefix。

5.可以在IDE就看到畫面

6.可重複使用(但較難跨專案使用),如電話控制項、地址控制項,跨專案使用,就是透過複製貼上。 也可以考慮設計成custom control,則只需要將dll拉至工具箱內即可。

7.User control之間的互動,請參考連結:http://www.dotblogs.com.tw/sin1980/archive/2009/09/27/asp1.aspx

8.動態新增user control注意事項
(1)[ASP.NET]動態新增User Control,並設定屬性值與取屬性值
(2)[ASP.NET]動態Load UserControl – part 2
(3)[Tips]UserControl動態新增時,轉型注意事項

MasterPage

1.繼承UserControl

2.各頁的aspx置放於ContentPlaceHolder中

<asp:ContentPlaceHolder ID="MainPlace" runat="server“ />

3.只是變形的UserControl,有套用MasterPage的頁面,只是把自己的內容放在ContentPlaceHolder中
(1)假設A頁與B頁有套MasterPage,由A頁導到B頁,這兩頁仍是不同的頁面
(2)與frame不同

4.MasterPage上也可以撰寫共用的function
如Tree、頁籤、共用按鈕、共用資訊等..就跟UserControl可以設定對外function一樣。

5.使用上小心效率問題
因為每個套用MasterPage,都會跑到相關程式

6.注意事項
(1)關於Content Page中加入CSS或JS檔的引用
參考:http://social.msdn.microsoft.com/Forums/zh-TW/236/thread/0deaae5c-46d9-40e4-83b8-e114c2db79f3

<script type="text/javascript" src='<%#ResolveUrl("~/JavaScript/jquery.cookie.js") %>'></script>



protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Page.Header.DataBind();
    }

(2)巢狀 master pages架構下,取得master中物件的問題
參考:http://social.msdn.microsoft.com/Forums/zh-TW/236/thread/2eb55775-252c-464c-87d4-fa2b7ef14f45

]]> 就是91 2010-10-20 19:08:36
91之ASP.NET由淺入深 不負責講座 Day22 - DIP 依賴反轉原則 https://ithelp.ithome.com.tw/articles/10055368?sc=rss.iron https://ithelp.ithome.com.tw/articles/10055368?sc=rss.iron 今天要介紹的是Dependency Inversion Principle, DIP,依賴反轉原則。
跟IoC(控制反轉)是一樣的意思,另外相關的就是Dependency Injec...]]>
今天要介紹的是Dependency Inversion Principle, DIP,依賴反轉原則。
跟IoC(控制反轉)是一樣的意思,另外相關的就是Dependency Injection,則是實作DIP的一種方式。

用到的觀念有 『多型』、『Liskov Substitution Principle』跟『Open/Close Principle』
1.定義
High level modules should not depend upon low level modules.
Both should depend upon abstractions.
Abstractions should not depend upon details.
Details should depend upon abstractions.

抽象等級高的模組,不應該依賴於抽象等級低的模組,
兩者都應該依賴abstraction。
abstract不應該依賴於細節。
細節應該依賴於abstract。

恩,難懂的要死,這些原則從字面上都不好懂,翻成中文就更不好懂了。

2.具體意思
模組間的依賴關係,是透過抽象介面而發生的。實現class之間不發生直接的依賴關係,其依賴關係是通過interface或abstract class產生的。
因為只要實作interface的,都要滿足interface的contract內容,這樣我才可以讓你注進來(dependency injection)

3.簡單的說
(1)也就是class之間的耦合關係,要多墊一層interface/abstract來隔開。
(2)高層要用低層的方法,就都透過interface來表述抽象的行為,而不用管實體的concrete class是什麼。(滿足多型與Liskov substitution principle)

4.系統抽象程度看interface, reuse程度看abstract
系統抽象程度,看interface就是因為這樣,抽象代表不管實作,代表系統可以穩定。因為通常改變的都是實作內容,當需要抽換邏輯時,只需要改變注入的concrete class即可。符合開放封閉原則。

5.結論
(1)每個class都應該盡量有abstract class或interface,避免與外界互動時,直接耦合
(2)變數類型,應該盡量是abstract或interface
(3)基本上,任何一個concrete class,都不應該是繼承另一個concrete class的。如果把繼承鏈想像成tree,代表所有leaf才可以是concrete class。
(4)避免覆寫abstract class已經決定的concrete function,會對系統造成不穩定性,因為不知道從繼承鏈的哪邊改起。
(5)請滿足Liskov SubStitution Principle。

6.例子
剛好這幾天在下大雨,我們就舉個例子好了。

場景:下雨,我們要拿東西遮雨。

1.現在的需求是我們拿雨傘來遮雨,所以我們的Code就長這樣:

        /// <summary>
        /// 當下雨天,我們就new一把雨傘來遮雨      
        /// </summary>
        public void RainnyDayWithUmbrella()
        {
            天氣 weather = new 天氣();
            if (天氣.IsRain)
            {
                雨傘 umbrella = new 雨傘();
                umbrella.遮雨();
            }
        }

在這個Case裡面可以看到Client Class(也就是場景類)的RainnyDayWithUmbrella(),裡面直接與雨傘這個claas耦合,因為RainnyDayWithUmbrella()需要用到雨傘來遮雨()。
這個動作就違反了DIP。

2.我們把需求改成用陽傘來遮雨

        /// <summary>
        /// 需求變更,我們現在要改成用陽傘來遮雨。
        /// </summary>
        public void RainnyDayWithSunUmbrella()
        {
            天氣 weather = new 天氣();
            if (天氣.IsRain)
            {
                陽傘 umbrella = new 陽傘();
                umbrella.遮雨();
            }
        }

可以看到,因為現在要用陽傘了,我們必須得『修改』我們的程式碼,把雨傘改成陽傘。
這違反了Open/Close Principle。
因為高層模組(也就是Client),希望使用的遮雨方法,依賴於低層模組。

3.我們來看,什麼是依賴反轉。依賴反轉在這個case的重點,就是讓原本場景依賴於細節class的方法,改成細節class的方法依賴於抽象介面。

        /// <summary>
        /// 我們care的是,下雨,要遮雨(interface/abstract)。
        /// 用什麼(concrete class)遮,則不會影響我們Client Class的抽象邏輯(下雨,遮雨)。
        /// </summary>
        public void RainnyDayWithDIP()
        {
            天氣 weather = new 天氣();
            if (天氣.IsRain)
            {
                I天氣應變措施 action = GetSomethingCoverBody(CoverStuff.雨傘);
                action.遮雨();
            }
        }

        private I天氣應變措施 GetSomethingCoverBody(CoverStuff coverThing)
        {
            switch (coverThing)
            {
                case CoverStuff.包包:
                    return new 包包();
                case CoverStuff.帽子:
                    return new 帽子();
                case CoverStuff.雨傘:
                    return new 雨傘();
                case CoverStuff.陽傘:
                    return new 陽傘();
                case CoverStuff.none:                    
                default:
                    return null;
            }
        }

        enum CoverStuff
        {
            none=0, 
            雨傘,
            陽傘,
            帽子,
            包包
        }

如同註解所說,我們的高層模組要的是抽象化的行為與邏輯,
我們care的是,下雨,要遮雨(interface/abstract)。
用什麼(concrete class)遮,則不會影響我們Client Class的抽象邏輯(下雨,遮雨)。

[註]GetSomethingCoverBody()這個方法,則可以套用factory pattern,new的部分則可以套用Dependency injection的framework來改寫,可以讓耦合的程度降到最低。

]]>
就是91 2010-10-19 20:13:35
91之ASP.NET由淺入深 不負責講座 Day21 - LSP 里氏替換原則 https://ithelp.ithome.com.tw/articles/10055044?sc=rss.iron https://ithelp.ithome.com.tw/articles/10055044?sc=rss.iron 今天要講的是Liskov Substitution Principle,LSP (里氏替換原則),
會跟前面介紹到的多型、繼承,以及開放封閉原則的概念有關。

還不熟悉的...]]> 今天要講的是Liskov Substitution Principle,LSP (里氏替換原則),
會跟前面介紹到的多型、繼承,以及開放封閉原則的概念有關。

還不熟悉的,可以參考一下前面的文章:
1.91之ASP.NET由淺入深 不負責講座 Day14 - Object Oriented (1)
2.91之ASP.NET由淺入深 不負責講座 Day18 - 開放封閉原則
1.定義
(1)If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,
the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
(2)Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

2.簡單的說
使用父類的地方,必須可以使用子類別代替,而不需要任何改變。

3.目的
用來規範繼承的標準。

4.原則
(1)子類別必須完全實現父類別的方法
(2)子類別可以擁有自己不同的屬性與方法
(3)要小心參數型別與return型別,型別涵蓋範圍大小造成的問題:override與overload的差異

5.針對違反LSP設計時可行的重構(Refactoring)方式
當Class A錯誤的繼承Class B時:
(1)可建構一個新的abstract class C,作為2個concrete Class A與B的父類別,也就是多抽象一層來當兩個的父類別,讓A與B在繼承鏈中深度一樣,來避免A繼承B的強耦合與錯誤使用
(2)也可以透過委派的方式來解決(分離)A與B的錯誤繼承關係。

6.參考
(1)http://www.engr.mun.ca/~theo/Courses/ssd/pub/sd-principles-3.pdf
(2)http://irw.ncut.edu.tw/peterju/se.html

PS:晚一點再補上比較好懂的sample code

]]> 就是91 2010-10-18 13:07:48
91之ASP.NET由淺入深 不負責講座 Day20 - ISP 介面隔離原則 https://ithelp.ithome.com.tw/articles/10054846?sc=rss.iron https://ithelp.ithome.com.tw/articles/10054846?sc=rss.iron 前言
前面介紹了單一職責原則、開放封閉原則、最少知識原則,今天要介紹的是Interface Segregation Principle, ISP (介面隔離原則)。

介...]]> 前言
前面介紹了單一職責原則、開放封閉原則、最少知識原則,今天要介紹的是Interface Segregation Principle, ISP (介面隔離原則)。

介面隔離原則,與單一職責原則和最少知識原則有一點關係,而SOLID原則的總綱是開放封閉原則,所以如果對這些觀念還不夠熟悉的朋友們,可以參考一下前面幾篇文章:
[ASP.NET]91之ASP.NET由淺入深 不負責講座 Day17 – 單一職責原則
[ASP.NET]91之ASP.NET由淺入深 不負責講座 Day18 – 開放封閉原則
[ASP.NET]91之ASP.NET由淺入深 不負責講座 Day19 – LoD/LKP 最少知識原則
1.定義
(1)Clients should not be forced to depend upon interfaces that they don't use.
也就是Client不應該依賴它不需要的介面。
(2)The dependency of one class to another one should depend on the smallest possible interface.
也就是Class之間的依賴關係應該建立在最小的interface上。

2.簡單的說
Class與外部的關係,應只依賴它需要的最小介面,所以介面不能太肥,應該要細化,不然會強迫中獎。每一個interface的方法數目應該盡可能地降到最低。

3.與單一職責原則的矛盾
(1)單一職責是指屬於同一個職責的方法應該放在一起,且一個class/interface只負責一個職責。
可能產生一種情況,實作interface的class,用不到該interface的所有方法。
(2)相對於介面隔離原則,則是每一個class實作了某個介面,就應該代表會用到該介面所有的方法,如果不會用到所有方法,代表該interface可以再繼續細化。

4.目的
對interface的scope進行約束。

5.基本原則
(1)interface盡量小
-粒度越小代表系統可以越靈活,但也代表結構越複雜,開發難度提升
-最小的情況就是一個interface只有一個method,但違反單一職責原則
-細分interface的前提,就是不能違反單一職責原則

(2)interface應高內聚
-粒度應該符合環境與未來需求中,細分到最細。
-interface就是contract的觀念,對外承諾的function越少,變動風險越低,就越穩定 。

(3)訂製服務
interface只提供訪問者需要的方法 。

(4)粒度大小的細分標準
因地因時制宜,取捨需求、開發與結構的平衡點 。

6.結論
殺雞焉用牛刀,但書:
(1)如果是為了殺雞,就不需要用到牛刀
(2)如果是為了用牛刀,那就還是得用牛刀

7.舉例
我們用下面這張class diagram(點圖可放大)來看,可以看到以工作上所需的技能來分,一個ExcellentWorker,應同時具備HardSkill與SoftSkill。
然而,不是每一樣工作,都需要excellent的worker才能做事。一個健康的團隊,應該有將有兵,分工合作,各司其職,互助互補。

如果今天工作需要的是協助測試,那可以找QA,也可以找Joey。最好是先找QA,因為如果找Joey,他的對外方法不只測試的介面,還有許多其他的功用,就很有可能被其他任務插單。

相對的,以介面來說,一個QA可能只需要具備測試的介面,而不需具備Leadership或是Integrity。

我自己的原則是,寧可多實作幾個interface,保留彈性,也要避免未來因為繼承或extend造成強耦合而無法修改的風險。
但interface的本身,還是要盡量符合單一職責的原則。

]]> 就是91 2010-10-17 15:44:39
91之ASP.NET由淺入深 不負責講座 Day19 - LoD/LKP 最少知識原則 https://ithelp.ithome.com.tw/articles/10054636?sc=rss.iron https://ithelp.ithome.com.tw/articles/10054636?sc=rss.iron 今天要介紹的東西有兩個名字,
Law of Demeter, LoD (狄米特法則)
Least Knowledge Principle, LKP (最少知識原則)
今天要介紹的東西有兩個名字,
Law of Demeter, LoD (狄米特法則)
Least Knowledge Principle, LKP (最少知識原則)
這兩個原則,其實是在講一樣的事。在SOLID原則中,拿來判斷class耦合性的原則之一。

會用到之前講到的『封裝變化』的概念。
定義
一個object應該對其他object有最少的瞭解。

簡單的說
任何一個object應該只要讓外部object知道,最少且缺一不可的資訊,就要可以正常的interact。

目的
用來解耦,也就是降低類別與類別之間耦合的程度。當耦合程度降低時,每一個class可以跟其他class互動的機會就會增加,reuse的機會就會增加。

How
如何降低本身class的資訊被外界知道太多,導致邏輯混亂或class被隨意改變狀態?
透過封裝以及visibility的控制,謹慎的考慮該使用public, protected, private 還是internal的能見度。

舉例
我們這邊舉個例子,承接著上次Daddy要刷牙、洗臉、大便的例子,我們要來說明,怎麼樣的設計可以符合LoD。

先來看看我們的class diagram:

我們現在的場景是,每天早上媽媽要叫爸爸起床,起床的動作,包含了叫爸爸去刷牙、洗臉跟大便。
所以我們的CallDaddyGetUp的內容如下:

public override void CallDaddyGetUp(AbstractDaddy thisDaddy)
       {
           
           AbstractToothBrush toolBrush = new 牙刷();

           #region 叫Daddy起床,要Daddy做的事

           thisDaddy.刷牙(toolBrush);
           thisDaddy.洗臉();
           thisDaddy.大便();

           #endregion           
       }

AbstractDaddy的class長這樣:

public abstract class AbstractDaddy 
{
    private AbstractToothBrush _toothBrush;
    public AbstractDaddy()
    {
        
    }

    public AbstractDaddy(AbstractToothBrush toothBrush) {
        this._toothBrush = toothBrush;
    }

    public abstract void 刷牙(AbstractToothBrush toothBrush);
    public abstract void 洗臉();        
    public abstract void 大便();        
}

對我們來說,如果刷牙()、洗臉()、大便()稱為『起床三部曲』,而且只有在起床的時候用的到,那麼我們就應該把這三個行為封裝在MyDaddy的Class裡面,稱為起床()的行為。
倘若全世界的每個Daddy都是做這樣的事,且不容改變。那我們就應該將此行為封裝在AbstractDaddy裡面,且不允許繼承的子類別變更,另一方面,也可以讓繼承的子類別不必重寫這樣的行為。

所以我們的AbstractDaddy變成下面這樣,對外只剩下GetUp(),而不讓外面的Mommy來決定Daddy起床要做哪一些事,Mommy只要專注於叫Daddy起床即可。

還記得我們提到封裝變化嗎?當未來需求異動,Daddy決定起床第一件事先大便,或是先喝水時,我們只需要修改GetUp()的內容即可。對外界來說,仍是Daddy的起床行為,而不用去管到底Daddy起床是先大便還是先刷牙。
所以我們的Mommy內容就變成下面這樣:

public override void CallDaddyGetUp(AbstractDaddy thisDaddy)
        {                     

            #region 叫Daddy起床,要Daddy做的事

            //thisDaddy.刷牙(toolBrush);
            //thisDaddy.洗臉();
            //thisDaddy.大便();
            thisDaddy.GetUp();

            #endregion            
        }

上面的例子,我們看到了,如何Daddy原本對外的方法,變成了只有一個,其餘都是屬於Daddy本身的行為。

第二個例子,則在剛剛的程式碼裡面已經露出一點端倪了,

我們先假設Daddy刷牙()這個方法是public的,
我們原本的Mommy要叫Daddy刷牙的code是長這樣:

public class MyMommy : AbstractMommy
{       

    /// <summary>
    /// Daddy刷牙要用的牙刷,由Mommy產生出來遞給他,但牙刷跟Mommy無直接關係,只是為了叫Daddy刷牙
    /// </summary>
    /// <param name="thisDaddy"></param>
    public override void CallDaddyBrushTooth(AbstractDaddy thisDaddy)
    {
        AbstractToothBrush toolBrush = new 牙刷();
        thisDaddy.刷牙(toolBrush);
    }
}

可以看到,Mommy只是為了叫Daddy刷牙,還要去生一根牙刷給Daddy,但是在這個case裡,牙刷對於Mommy來說,一點意義都沒有。
class關係就變成下圖所示:

我們覺得,應該讓Daddy自己想辦法去找牙刷就好,而不是需要Mommy什麼都準備的好好的,而且Mommy找的牙刷,也不一定合適Daddy用。
所以修正一下我們的class diagram,Mommy只叫Daddy刷牙,他要用啥刷是他的事情。

如此一來,牙刷怎麼改變,都與Mommy無關,Mommy也可以專心在叫Daddy刷牙這件事。未來Daddy刷牙的實際內容改變,Mommy與Daddy的互動關係也不需要改變。
Mommy的class改成這樣,單純多了:

Daddy則自己去生一把牙刷,或是隨意的想辦法完成刷牙這個行為即可。

可以看到這裡用new,其實是一個頗糟糕的用法。
沒錯,new牙刷的這個部分,還可以套用factory pattern來決定用哪一種牙刷。
也可以再將Clean的方法抽象成一個interface,再用IoC的方式,來決定究竟要用什麼樣的東西來Clean()即可。

結論
透過上面這樣的重構例子,可以看到重構完class之間的耦合性降低了,也因為耦合性降低,封裝變化後,不論是classs要重複使用時,可以降低一堆包袱與糾葛,或是需求異動時,通常場景類抽象的使用各個抽象介面的行為,幾乎都不需要改變到邏輯。
除非真的是商業邏輯的異動,如果只是場景類的商業邏輯異動,則只是重組各個抽象介面的行為邏輯。而不需要修改到抽象介面後的concrete class。

]]>
就是91 2010-10-16 18:05:36
91之ASP.NET由淺入深 不負責講座 Day18 - 開放封閉原則 https://ithelp.ithome.com.tw/articles/10054337?sc=rss.iron https://ithelp.ithome.com.tw/articles/10054337?sc=rss.iron 今天這一篇,要介紹的是開放封閉原則,也就是Open/Close Principle。

開放封閉原則,可以說是SOLID的總綱,也是任何一個物件導向設計,期望遵循的原則。
...]]> 今天這一篇,要介紹的是開放封閉原則,也就是Open/Close Principle。

開放封閉原則,可以說是SOLID的總綱,也是任何一個物件導向設計,期望遵循的原則。
1.定義
(1)軟體設計原則,應該對擴展開放,對修改封閉
(2)原文定義: Software entities like classes, modules and functions should be open for extension but closed for modifications.

2.簡單的說
簡單的說,一個class, function, modules在碰到需求變更或新增時,應該可以只增加code,而不去修改原本的code,就能滿足變化

3.不改code,那幾乎是不可能的
完全不用改code,是不太可能的,但是我們改的部分,應該只有組合或mapping concrete class的部分,而不是concrete class或function的內容。

4.為什麼要用開放封閉原則
Open/Close Principle其實比較像是SOLID裡面另外五個原則的總綱。帶來的好處有下列幾點:
(1)可測試性
當使用抽象介面將場景與concrete class隔開之後,我們就可以寫測試程式,透過簡單的OO方式與test framework的輔助,來模擬抽象介面背後的concrete class會具備什麼樣的行為

(2)重用性
要對修改封閉,擴展開放,通常就需具備高內聚力,也就是class與interface等粒度應該是,在符合需求與未來改變的前提下,粒度切到最小。粒度越小,重用性越高。

(3)可維護性
當不用去改舊的、已經存在的code,不用trace那老太婆的裹腳布的前人遺毒時,只需要自己撰寫新的code符合需求,然後隨插即用,當然是件快樂的事。不只可以提高可維護性,還可以改善維護人員的心情呢。

(4)擁抱改變
當面對改變時,都可以透過新增class來擴展原本的系統,不用有太多的包袱以及過多的擔憂,當然就能設計出類似真實世界隨時在改變的SOLID system。

5.如何實現
(1)抽象約束
透過abstract或interface,在run time的時候,才連接實際concrete class。也就是多型的應用,IoC的概念,低耦合的實踐。
在實際的運用場景,我們只需要專注在場景上要使用到的抽象介面,透過該介面提供的行為來組成與執行商業邏輯。
這樣場景的設計,才會抽象。不管實作內容(class或class的function)如何變化,都會且必須滿足場景的抽象使用。

(2)透過抽象介面當作接口,來設計場景。當需求變更時,該如何達到對擴展開放,對修改封閉呢?
(a)只異動metadata即能擴展系統
可以透過factory pattern或Dependency Injection,經過參數或reflection來達到使用新的concrete class完成新的需求。
而不需要直接修改舊的class內部邏輯,也不需修改場景的商業邏輯,只需要新增新的concrete class,且將mapping指向新增的concrete class即可。(可運用strategy patten)

(b)另一種偷吃步的方法,是使用繼承。透過繼承與覆寫父類別的方法,讓實際run time時,由子類別來實作父類別的功能,但新的需求與設計內容,則由子類別決定。
這樣也可以達到,只有新增子類別,不改父類別內容,場景只修改run time的執行instance為子類別,就達到開放封閉原則的標準。

[注意!!]但這是一種治標不治本的方式,如同之前文章講的,繼承是一種高耦合的行為,當這個系統要長久運作與維護,因為偷吃步而產生的繼承鏈長度,出來跑,總有一天要還的。

(3)封裝變化
設計的時候,應該將可能變化或不穩定的部分,封裝起來,以抽象介面隔開。透過Design patterns來封裝變化。

================================
最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.什麼是開放封閉原則?
2.可以用哪些方式來設計符合開放封閉原則的系統?
3.開放封閉原則的好處是啥?

]]> 就是91 2010-10-15 16:30:40
91之ASP.NET由淺入深 不負責講座 Day17 - 單一職責原則 https://ithelp.ithome.com.tw/articles/10054102?sc=rss.iron https://ithelp.ithome.com.tw/articles/10054102?sc=rss.iron 好的OO設計原則,可以達到高內聚低耦合的目的,
而什麼是好的設計原則呢? 就是SOLID原則,剛好是由五個(也有人稱六個)原則的首字母組成的縮寫,SOLID也就是穩固的意思。遵守這些...]]>
好的OO設計原則,可以達到高內聚低耦合的目的,
而什麼是好的設計原則呢? 就是SOLID原則,剛好是由五個(也有人稱六個)原則的首字母組成的縮寫,SOLID也就是穩固的意思。遵守這些原則,可以讓我們的設計更加穩固,更能擁抱變化。

今天這一篇,先介紹單一職責原則,也就是Single Responsibility Principle,簡稱SRP。
Single Responsibility Principle,簡稱SRP

1.定義
應該有,且只有一個原因,會引起class的變更。
原文解釋:There should never be more than one reason for a class to change.

2.備受爭議的原則
這是一個備受爭議的原則,因為要完全做到幾乎是不可能的。但那是實作上的權衡取捨,基本上它還是一個好的設計原則,只單純在實作上備受爭議,在設計上這才是OO的細度到了極致的境界。

3.簡單的說
一個class只負責一個職責的內容。
一個class如果要負責多個職責,其內容應該只是組合多個class,而每個被組合的class只負責單一職責,且通常這樣的組合都是透過接口(interface/abstract)。

4.單一職責的好處
(1)class複雜性降低,因為實現什麼職責都有很清楚的定義
(2)可讀性提高
(3)可維護性提高
(4)變更引起的風險降低,變更範圍降低,擴展性提高

單一職責,也就是高內聚力的實作方式。

5.實作上最困難的地方
原則很簡單,但實作上為何困難? 最難的就是定義所謂的『職責』,以及切割class的粒度取捨。
太細,就剁的太碎,失去實際應用上的目的。例如前一篇文章講的樂高積木,我們要的,應該是每一個單位小到我想要組合成任何模型,都不會有本質上的困難。
細到無窮止盡,可能就是原子的世界了,每一個人看原子,都不知道最後組出來會是長什麼樣子,更遑論應用它了。

6.取捨平衡點
至少要做到interface的單一職責!

7.舉例
我們定義一個電話功能的interface

public interface IPhone{
    public void dial(String phoneNumber);
    public void chat(Object o);
    public void hangUp();
}

這是頗常見的interface定義方式,但dial與hangUp其實應該是同一職責,而chat是另一個職責才對。所以我們應該是Phone要實作這兩個職責的interface。

如此一來,不論未來在修改或擴充電話的功能,都會更加清楚與獨立。

]]>
就是91 2010-10-14 20:46:22
91之ASP.NET由淺入深 不負責講座 Day16 - 高內聚、低耦合 https://ithelp.ithome.com.tw/articles/10053842?sc=rss.iron https://ithelp.ithome.com.tw/articles/10053842?sc=rss.iron 講完了OO的幾個概念,再來幾篇要來介紹好的OO原則跟設計方式。

今天先講很常聽到的『高內聚』、『低耦合』,大家都知道這是好的,但卻很難解釋什麼是內聚力,什麼是耦合,以及為什麼要高內...]]> 講完了OO的幾個概念,再來幾篇要來介紹好的OO原則跟設計方式。

今天先講很常聽到的『高內聚』、『低耦合』,大家都知道這是好的,但卻很難解釋什麼是內聚力,什麼是耦合,以及為什麼要高內聚,為什麼要低耦合。
1.高內聚
高內聚,簡單的說,就是有點『單一功能』的味道。
某個class或function負責處理的事情,已經無法再切分成別的class或function,稱為內聚力高。

2.低耦合
低耦合,簡單的說,就是相依性低。也有點類似『牽一髮動全身的程度』。
當一個需求異動,要改到很多地方,或是改了一個部分,很多地方都錯了,通常就是耦合性高。

3.舉例
高內聚低耦合如果要舉個例子來說,就像樂高積木。
樂高積木可以組合出各式各樣的模型,要重新組裝、異動,也都很有彈性。

(1)內聚,就是一塊一塊積木應該切分的很清楚、很細,且無法再切得更細。
如果是低內聚,就代表一塊積木都超大一塊的,當要組裝比較小的模型時,是無法拆開的。

假設有一塊積木是1號~10號這麼大,另一塊積木則是11號~20號。
但現在需求是需要組裝一個是5號~15號的模型出來,就做不到了。
除非再去重做一個5號~15號的積木。
所以如果是高內聚的設計方式,我們每一個單位都是一塊積木。
我們需要5號~15號,只需要把5號~15號這11塊積木組裝起來就可以了。

(2)耦合,就是積木與積木之間組裝的方式。
高耦合,就代表黏的拔不開。低耦合,就代表容易拔開組裝。
(所以樂高的積木是做的有凸起跟凹槽的,凸起與凹槽,就有點像介面)

如果是高耦合,就可能組裝積木的方式是用強力膠黏,當所有東西都用強力膠黏時,
假設以剛剛5號~15號積木的例子,現在8號要抽換成18號,是很難抽換的,得想辦法鋸斷強力膠黏的部分。
如果是低耦合,就可能是現在樂高的組合方式,透過積木之間的凸起與凹槽來做結合。或是像卡榫、螺絲一般,是方便組裝跟抽換的。只需要把8號前後拔開,再把18號放進來跟前後結合即可。

4.高內聚低耦合可達到的目的
需求異動容易抽換、修改與增加功能,而不會對太多既存功能造成影響。
每個功能重用性高、擴展性好、維護性高。

高內聚、低耦合是我們OO設計的目的,
而要盡量遵守的原則,則是SOLID原則。
實際上會面對到的設計問題,則可透過Design patterns來當作我們解決某個特定issue的藍圖。

]]> 就是91 2010-10-13 21:02:05
91之ASP.NET由淺入深 不負責講座 Day15 - Abstract與Interface https://ithelp.ithome.com.tw/articles/10053539?sc=rss.iron https://ithelp.ithome.com.tw/articles/10053539?sc=rss.iron 上次介紹了OO的三個特性:封裝、繼承跟多型,大家還記得嗎?有辦法用自己的話來說明與舉例子嗎?
如果還沒,建議您先去看前面那一篇:http://ithelp.ithome.com.tw/question/10053333

這一篇文章,要介紹更抽象的abstract與interface。
希望透過這篇文章,讓原本攪在一起的觀念可以釐清一下。
前言
抽象這個term本身就很抽象,我的自己用一段話來定義『抽象』:
抽象就是不管實作內容。抽象就是用實際世界的角度思考,而非寫code的角度思考

接下來,我們來說明Abstract與Interface:
1.Abstract
(1)不能被直接實體化使用,所以我們可以把實體化沒有任何意義的class宣告成abstract
(2)宣告成abstract的property與function都會強制子類別需overrides
(3)用來定義抽象的屬性、行為
(4)也是一種Super Class,可以在abstract class裡面,定義需被繼承且覆寫,以及可被繼承的實體屬性與方法,也就是方法內具備內容。

2.Interface
(1)類似contract的概念,實作介面就該履行內容
(2)抽象化的屬性與行為
(3)不需要擁有相同的父類別,也可以具備同樣的行為
(4)介面的定義應該簡潔明瞭 (SOLID原則中的L原則)

3.Abstract與Interface在系統中扮演的角色
(1)Abstract:決定了系統的Reuse程度。透過繼承與限制,可決定子類別的設計方式。
(2)Interface:決定了系統設計的抽象程度。透過實作介面,來定義物件之間的抽象行為與關係。

4.Abstract與Interface的差異
其實Interface可以當作abstract極致化的概念產物,
但是abstract class裡面,是可以包含實作的,這是最大的差異。

光這樣說,有一點難以理解。我們舉個例子來說明 (雖然單純用文字說明也不是挺好懂的)
PS:這個例子是Design Pattern裡面,Template Method pattern的實作case。
例如:
1.我們有一個interface,叫做『生活起居』,裡面有個方法是『起床』。
2.我們有一個abstract class,叫做『每個家庭的爸爸』,實作『生活起居』這個interface。
我們這個爸爸的abstract class裡面,定義了三個abstract protected(或public)的void,分別是『刷牙』、『洗臉』跟『大便』。因為定義成abstract,所以這三個方法實際的內容,得由子類別覆寫後來決定。
3.我們希望繼承『每個家庭的爸爸』的子類別,他們的『起床』行為,裡面就是要依序執行『每個家庭的爸爸』abstract class裡面的『刷牙』、『洗臉』跟『大便』這三個void。由這三個方法組成『每個家庭的爸爸』的『起床行為』。

4.倘若我們要限制子類別不可以修改此『起床』行為,那麼我們就寫成這樣
interface

    public interface ICommonLife
    {
        /// <summary>
        /// 起床
        /// </summary>        
        void GetUp();
    }

abstract class

    public abstract class AbstractDaddy : ICommonLife
    {
        public abstract void 刷牙();        
        public abstract void 洗臉();        
        public abstract void 大便();

        #region ICommonLife 成員

        /// <summary>
        /// 起床
        /// </summary>
        /// <remarks>
        /// 當繼承AbstractDaddy的時候,強迫繼承的子類別其GetUp行為,一定得是先刷牙、洗臉,再大便,才算完成起床的動作。
        /// 而實際的刷牙、洗臉跟大便的內容跟方式,則由子類別自行決定。
        /// 這樣的限制(或稱為reuse,因為子類別不需在重複定義這樣的起床行為),只有abstract可以做,interface做不了
        /// </remarks>
        public void GetUp()
        {
            this.刷牙();
            this.洗臉();
            this.大便();
        }
        #endregion
    }

結論:如同註解內所提到,因為interface無法加入實作內容 (也因此interface才可以定義抽象行為),所以要達到這個case裡面限制『起床』行為的需求,只能透過abstract來作。

================================
最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.什麼是abstract? 請舉例。
2.什麼是interface? 請舉例。
3.abstract與interface的差異? 請舉出只能用abstract,不可以用interface的例子。
4.abstract與interface在系統中扮演的角色為何?

]]> 就是91 2010-10-12 16:12:32
91之ASP.NET由淺入深 不負責講座 Day14 - Object Oriented (1) https://ithelp.ithome.com.tw/articles/10053333?sc=rss.iron https://ithelp.ithome.com.tw/articles/10053333?sc=rss.iron 把OO放在這麼後面講,或許有一些人會認為是不是太晚了。
因為通常training的需求,前半段是需要一點即戰力以及workshop的練習,所以Day1~Day13的說明,應該已經可以...]]>
把OO放在這麼後面講,或許有一些人會認為是不是太晚了。
因為通常training的需求,前半段是需要一點即戰力以及workshop的練習,所以Day1~Day13的說明,應該已經可以讓學員開始練習自己動手做幾個網頁,把學到的東西應用在上面了。

從Day14開始,就是進階的課程了,而進階的課程,OO的concept是一切的基礎,而OO也是可以很簡單,可以很抽象的東西。需要搭配很多例子(包括實例以及sample code)才比較容易說明清楚。

由於這邊篇幅有限,我會盡力的點出概念,也歡迎各位討論與回饋。
這一篇文章先介紹幾個概念:
1.類別跟實體
2.建構式
3.多載
4.封裝
5.繼承
6.多型

1.類別跟實體
(1)類別:具有相同屬性和功能的物件抽象集合
例如:class為豐田汽車,其屬性可能有型號、價錢等等...
(2)實體:一個真實的物件(instance)
例如:透過豐田汽車的class (類似spec),產生一台實體的豐田汽車,這台豐田汽車就擁有了豐田汽車這個class所定義的屬性、方法、事件等等特性..
(3)實體化:建立物件的過程,使用new關鍵字來建立

2.建構式
(1)對類別進行初始化的動作
(2)建構式與類別同名,無return值,也不需要void
(3)可透過overloading來達到不同方式的初始化動作
例如:
new TOYOTA("ALTIS");
new TOYOTA("CAMRY");

3.多載(overloading)
(1)相同function名稱,不同參數個數或不同型別
(2)在不改變父類別原方法的基礎上,新增子類別相同功能名稱,不同功能內容的作法
VB.NET中有Optional,請盡量避免這樣的設計,註:C#4.0也有
(3)其他類似的部分:
(a)overrides,覆寫,與父類別相同function名稱,參數個數與return型別相同。 強制覆寫其繼承自父類別的功能內容,使其滿足子類別的需求
(b)virtual,可覆寫。宣告為可覆寫後,繼承鏈中的子類別方可覆寫此屬性或方法。

4.封裝
(1)減少耦合
(2)讓用的人不用管內部如何運作,抽象化的使用
(3)類別內部可自由修改
(4)擁有清晰的對外介面
如同電燈開關,我們只需要知道怎樣可以開,怎樣可以關。不必知道實際背後是怎麼進行開關。代表背後的東西可以抽換、修改,而不會影響到使用上的邏輯。

5.繼承
(1)子類別有父類別非private的屬性和function
(2)子類別可擴展父類別所沒有的屬性和功能
(3)子類別可以用自己的方式,實現父類別的屬性和功能
(4)調用父類別,使用base關鍵字
(5)繼承的兩種使用方式:『不要用』跟『謹慎使用』
因為繼承本身就是一種強耦合的類別關連,例如當父類別需要修改某個方法內容,但已經有許多子類別繼承此方法。
可能只有一半的子類別要更新成新的方法內容,另一半要維持舊的方法方法內容。如此一來,就是強耦合造成的問題。

舉例:電燈都有開關的方法,子類別可以覆寫開關的方法內容,也可以繼承之後不覆寫。

6.多型
(1)多型的原理:當方法被呼叫時,無論物件是否被轉換為父類別,只有位於物件繼承鏈最末端的實作方法會被呼叫。也就是虛擬方法是按照其執行時類型,而非編譯時類型進行動態繫結呼叫的。
(2)不同的物件可以執行相同的動作,但要透過他們自己的實作程式碼來執行
(3)子類別以父類別的身份出現
(4)子類別在執行時以自己的方式來實作
(5)子類別以父類別的身份使用時,子類別特有的屬性和方法不可以使用
舉例:
(a)類別關係:
父類別為電燈,有宣告為public的turn on與turn off兩個function。
子類別為日光燈。

場景:如果客廳很暗,則打開電燈。
(1)原始版本:

if(客廳.IsDark){電燈.TurnOn();}

(2)多型版本(run time):

if(客廳.IsDark){日光燈.TurnOn();}

(3)多型進階版本(一點點IoC概念):

if(客廳.IsDark){Get電燈().TurnOn();}
private 電燈 Get電燈()
{
  //進行邏輯判斷後,return電燈或其子類別 instance
}

對場景來說,可以focus在『客廳』與『是否打開電燈』,而不管實際應用場景時,TurnOn這個動作是由父類別的電燈instance,或是子類別的日光燈所執行打開的動作。這樣的設計可以讓我們更專心於抽象地應用電燈的邏輯與功能。

================================
最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.類別與instance的差異?
2.overrides, overloading, virtual的差異?
3.封裝的意義與目的?
4.繼承要考慮的issue?繼承的兩種使用方式?為什麼?
5.多型的意義?多型的目的?舉例多型的應用

]]>
就是91 2010-10-11 17:13:32
91之ASP.NET由淺入深 不負責講座 Day13 - ADO.NET https://ithelp.ithome.com.tw/articles/10053214?sc=rss.iron https://ithelp.ithome.com.tw/articles/10053214?sc=rss.iron ADO.NET的範圍太廣,加上近年來的DataSourceControl與LINQ盛行,所以這邊我只做個簡介。
首先,最常看到糟糕的兩種現象,就是一個ASP.NET的工程師,
ADO.NET的範圍太廣,加上近年來的DataSourceControl與LINQ盛行,所以這邊我只做個簡介。
首先,最常看到糟糕的兩種現象,就是一個ASP.NET的工程師,
1.要存取DB,只會拖拉DataSourceControl,如SqlDataSource, LinqDataSource等等..
2.上面第一點也與這一點息息相關,就是沒有將邏輯與資料存取,和呈現層(也就是一般的UI)分隔開來。

為什麼使用DataSourceControl會有問題?如果會有問題,又為什麼ASP.NET要提供這樣子的server control供開發者使用呢?

首先,以目前ASP.NET Webform的設計原點來看,Page Life Cycle就是一個強耦合性的設計,另外Event model也很容易造成耦合性過高的問題。server control封裝後提供的方便,很容易開發人員會忽略server control背後的操作過程與方式。而DataSourceControl更是透過精靈的介面,讓開發人員可以幾近於無腦的一鍵到底,設好與DB存取的相關設定。

但,這意味著,在.aspx.cs裡面,負責UI層的程式就直接與資料存取層攪在一起,所有呈現UI、邏輯判斷以及DB存取,都被攪在一起。這是很嚴重的錯誤設計方式,完完全全的違背了高內聚低耦合的原則,既不容易做測試,碰到需求異動,也容易產生牽一髮而動全身的問題。

針對怎麼樣將UI與DB存取分開的部分,我們留在後續layer architecture再來探討。
而我們先針對ADO.NET來取代DataSourceControl做一下概念的介紹。

PS:DataSourceControl我唯一可以接受的是ObjectDataSource,因為它將Business logic與Data Access都透過一個ObjectDataSource做一個接口的隔離。

先補上ADO.NET的參考資料:http://msdn.microsoft.com/zh-tw/library/e80y5yhx(VS.80).aspx

ADO.NET入門的步驟,其實很簡單:
1.應該要先建立一段SqlConnection,而裡面會包含著ConnectionString。
目的:告訴ASP.NET,我再來這一段SQL是要對哪一個DB server進行操作,我的帳號密碼是什麼。
2.建立SqlCommand,需要用到剛剛的SqlConnection,以及我們需要執行的SQL statement。
目的:將要執行的SQL statement跟要連接的connection組合起來成為一段要執行的command。
3.Connection打開。
目的:真的要執行這段SQL statement了,請建立與DB之間的連線。
4.執行我們的SQL statement,可能沒有回傳值,可能有回傳值,用一個變數去接。
目的:得到執行SQL結果
5.關掉Connection。
目的:不佔用connection資源。

在ADO.NET中,還有兩個很重要的issue,
第一個是如何避免SQL injection,SQL injection(原理可參考:http://www.microsoft.com/taiwan/sql/SQL\_Injection\_G1.htm),
要解決SQL injection最基本的作法,就是使用parameter,各家db provider的範例可參考:http://msdn.microsoft.com/zh-tw/library/dw70f090.aspx

第二個則是交易(transaction)的概念,所謂的交易,簡單來說就是我會對DB進行多次異動,這多次異動如果是在同一筆交易中,這些異動應該同時成功(complete)或同時失敗且將資料還原為原本的狀態(rollback)。舉例來說,如同我們去提款機領錢,當我們已經選了提領五千塊,正對您的帳號資料進行扣款的動作,SQL已經執行完畢,但提款機突然壞了或停電造成交易中斷,導致您還沒領到真正的錢。這個交易就算失敗,錢不該從您的帳戶扣除,既然您沒實際拿到錢,就不應該扣除金額。

通常在ADO.NET裡面,使用到的transaction控管叫做TransactionScope,可以參考:http://msdn.microsoft.com/zh-tw/library/h5w5se33(v=VS.80).aspx
PS:如果是跨DB server的交易,也就是分散式交易,要留意MSDTC的問題。(一直是個很麻煩跟囉唆的問題,光設定防火牆就會讓人一肚子火了...)

]]>
就是91 2010-10-10 22:23:06
91之ASP.NET由淺入深 不負責講座 Day12 - Validator https://ithelp.ithome.com.tw/articles/10052942?sc=rss.iron https://ithelp.ithome.com.tw/articles/10052942?sc=rss.iron Validator算是很常用到的server control,很標準的將JavaScript與Server端物件封裝讓User可以透過設定就可以達到驗證的功能。
AS...]]>
Validator算是很常用到的server control,很標準的將JavaScript與Server端物件封裝讓User可以透過設定就可以達到驗證的功能。
ASP.NET提供的Validator的種類
1.RequiredFieldValidator
(a)必要輸入項目檢查
(b)不一定是空白
2.RegularExpressionValidator
(a)使用RegularExpression做檢查的rule
3.CompareValidator
(a)比較兩個控制項,或是控制項與常數值的等式與不等式
4.RangeValidator
(a)比較控制項的值需要符合起迄範圍
5.CustomValidator
(a)可自行定義client端與server端的驗證function
6.BaseValidator
(a)供擴充使用

issues you should know
1.Server端如何驗證:使用Page.IsValid
2.顯示錯誤訊息的方式:None、Static與Dynamic的差異
3.如何讓一個按鈕不觸發驗證:CauseValidation設成false
4.如何定義RequiredFieldValidator的初始值:InitialValue property
5.如何關掉client端的驗證: EnableClientScript = false
6.錯誤訊息可以使用圖像檔或聲音檔來表示: Text="<bgsound src='C:\Windows\Media\Windows Error.wav'>"
7.怎麼區分不同驗證群組:ValidationGroup
8.怎麼將錯誤訊息統一顯示在某個區塊:ValidationSummary
9.驗證失敗時,如何focus在該control上:SetFocusOnError設定為True

]]>
就是91 2010-10-09 03:26:16
91之ASP.NET由淺入深 不負責講座 Day11 - User State https://ithelp.ithome.com.tw/articles/10052697?sc=rss.iron https://ithelp.ithome.com.tw/articles/10052697?sc=rss.iron 介紹完使用JavaScript與user的互動之後,
接著就是在網頁上,通常我們會需要暫時記錄一些值,可能只有這一頁,可能只有這一個連線,可能需要跨頁,可能整個web site都是同...]]>
介紹完使用JavaScript與user的互動之後,
接著就是在網頁上,通常我們會需要暫時記錄一些值,可能只有這一頁,可能只有這一個連線,可能需要跨頁,可能整個web site都是同一份,
所以我們需要瞭解,在ASP.NET除了把值存入DB以外,還有哪些方式可以輔助我們記值。
他們的取捨平衡點、適用情況、該注意的問題有哪一些。
1.ViewState
-只存在於正在執行那一頁,通常用於postback記值(因為http無狀態)。
-原理是HTML上的hidden field,會透過Base-64編碼
-webconfig裡可以透過設定來加密:<pages viewStateEncryptionMode="Always" />
-通常server control會透過viewstate來保存其屬性值,也是傳統ASP.NET web form重整頁面最容易碰到的效率問題。可透過EnableViewState屬性開關。
-可存放可序列化的Object (一般程式用到普遍最大的應該也只是hash table吧 )

2.Session
-可存在於該連線,不同頁面,通常用來跨多頁傳些比較重要、需要安全的值。如果是前後頁面關連性的值,只需要用URL parameter即可。Session的意義比較像是在什麼樣的情況下,開始或變更了某個Session的值。
-類似跨網頁的全域變數
-佔用Server端記憶體,連線數量一暴增,記憶體很可能被操掛。
-預設存在記憶體裡,可以改存在Session State Server或DB。(如果放在Session State Server或者DB,存放的物件也必須是可序列化的)
-可設定存活時間,但發生錯誤或特定情況時,會導致Session遺失。請參考:http://www.dotblogs.com.tw/hatelove/archive/2009/12/25/sessionmiss.aspx

3.Cookies
-單一使用者,存在client端,讀的到就活著。
-需注意安全性考量,通常用在個人化portal或profile。
-有些single sign on會透過cookie(加密)來存放帳號資訊。
-String格式存放。

4.Cache
-吃Server記憶體。
-可設定時間、hit rate或先進先出來當作條件。
-通常應用在靜態網頁快取。

5.Application
在Server上所有User存取同一份。
當Web application重起或關閉時結束。
吃Server記憶體。

6.QueryString
-串在Url?後面那一串,key-value pair,每一組key-value pair用&區隔,例如url?key1=value1&key2=value2,value應該要經過UrlEncode編碼,避免錯誤
-如有重要資訊,可能要經過加密傳遞
-Url有長度限制
-用Request.QueryString[key]就可以得到該Key的Value

7.Config file & Resource file
-webconfig
(1)比較常用的是AppSetting:
取值WebConfigurationManager.AppSettings.Get("AppCode")
XML設定:

<appSettings>
    <!-- 我的AP環境設定參數 -->
    <add key="AppCode" value="http://www.dotblogs.com.tw/hatelove/" />
<appSettings>

(2)自訂webconfig區段
[ASP.NET]撰寫自己的 Configuration 區段 Part 1:不要再賴在 appSettings 的屋簷下了,寫個自己的 Configuration 區段吧~
[ASP.NET]撰寫自己的 Configuration 區段 Part 2: 將自己的組態組織成一個 Group
[ASP.NET]撰寫自己的 Configuration 區段 Part 3: 自訂組態集合

-Resource file: Resources.你的Resource檔名.ResourceManager.GetString(你的key)

附上參考來源 from MSDN magazine:

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.ViewState的適用情況,需考慮的情況,什麼東西可以放進去,存在於哪裡,只能存在多久?
2.Session的適用情況,需考慮的情況,什麼東西可以放進去,存在於哪裡,只能存在多久?
3.Cookies的適用情況,需考慮的情況,什麼東西可以放進去,存在於哪裡,只能存在多久?
4.Cache的適用情況,需考慮的情況,什麼東西可以放進去,存在於哪裡,只能存在多久?
5.QueryString的適用情況,需考慮的情況,什麼東西可以放進去,存在於哪裡,只能存在多久?
6.Config file & Resource file的適用情況,需考慮的情況,什麼東西可以放進去,存在於哪裡,只能存在多久?

]]>
就是91 2010-10-08 00:47:47
91之ASP.NET由淺入深 不負責講座 Day10 - 如何註冊執行一段JavaScript https://ithelp.ithome.com.tw/articles/10052571?sc=rss.iron https://ithelp.ithome.com.tw/articles/10052571?sc=rss.iron 從Winform要轉Webform,或從ASP要轉ASP.NET,寫網頁幾乎一定用的到的東西,「如何執行一段javascript」。

這邊舉了8+1種例子,各有好壞,在最一般的使用...]]> 從Winform要轉Webform,或從ASP要轉ASP.NET,寫網頁幾乎一定用的到的東西,「如何執行一段javascript」。

這邊舉了8+1種例子,各有好壞,在最一般的使用狀況下,這9種都可以跑,但是不一定適合,有的在特定狀況下會出現js error,有的會有效率的問題。所以得知道什麼樣的方式,最合適目前的狀況。

內容裡面的九種方式,其實在大部分的情況都可以用,但是用錯了潛在的問題很多,有可能只是沒測到,有可能只是剛好沒發生問題, 一定要謹慎使用,用對時機。

PS:因為內容太長,無法塞在內容裡面,所以把問題拉到描述這邊。
最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.如何在Server端註冊一段JavaScript,讓網頁可以執行?
2.為什麼這樣註冊,網頁上就可以執行這段JavaScript?(hint: JavaScript存在哪裡)
3.當畫面上有使用UpdatePanel時,該用什麼樣的方式註冊JavaScript?
4.JavaScript要Render在哪,效率才會比較好?
5.要怎麼樣動態的改變JavaScript的include或內容?
6.Response.Write()會有什麼問題?
8種例子
**1.Hard-code include js file in **
在區塊裡面hard-code寫死引用的js檔,引用後,可以直接使用該js裡面的function

<head runat="server">
    <title>怎麼註冊javascript</title>
    <script src="InCludeJScript.js" type="text/javascript"></script>

**2.Hard-code js functin in **
在區塊裡面hard-code寫死的javascript,在頁面裡面可以直接呼叫,
值得注意的是,若不是一開始就要執行的javascript,應該放到以後,如果在這邊的內容太多, 會導致loading時間過長,對user來說,body裡的內容才是「網頁內容」,就可能造成user認為網頁效率很慢的錯覺。
而且放在這個區塊如果是一開始就執行,會讀不到body裡面的DOM,因為內容還沒Render出來, 例如alert(document.getElementById(‘form1’)); 會是null。

<head runat="server">
    <title>怎麼註冊javascript</title>     
    <script type="text/javascript">
alert('head的時候,form為'+document.getElementById('form1'));
    //head裡面應該是放一些load進來就要執行的js,如果是供user觸發事件才要執行的,可以放在bottom
        function inhead() {
            alert('call js in head');
        }
    </script>

**3.Hard-code js functin after **
放在以後 hard-code寫死的javascript,這個時候alert(document.getElementById(‘form1’)); 就是[object]

    <script type="text/javascript">
alert('bottom的時候,form為'+document.getElementById('form1'));
    //如果不是一開始就用的到的js,記得放在bottom,這樣子網頁載入對使用者來講,才會覺得快
        function inbottom() {
            alert('call js in bottom');
        }
    </script>

4.Control Attribute.Add in server
在Server端替Control加上Attribute,attribute為事件,內容為javascript, 只要加過一次,textbox會幫忙記住,postback之後仍然會存在

/// <summary>
/// 在DOM上面直接加上屬性與要執行的javascript,只要加一次,postback仍然存在
/// </summary>
private void AddingAttributeOnDOM()
{        
    this.TextBox1.Attributes.Add("onchange", "alert('textbox onchange');");
}

5.ClientScript.RegisterStartupScript
在Server執行結束後,要執行的javascript,使用RegisterStartupScript,若畫面有ScriptManager,要記得透過ScriptManager註冊

/// <summary>
/// Render結束後要執行的javascript,有ScriptManager的話要透過ScriptManager註冊
/// </summary>
private void RegisterStartupScript()
{        
    if (ScriptManager.GetCurrent(this.Page) == null)
    {
        Page.ClientScript.RegisterStartupScript(this.Page.GetType(), "buttonStartup", "alert('Page.ClientScript.RegisterStartupScript');", true);
    }
    else
    {
        ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "buttonStartupBySM", "alert('ScriptManager.RegisterStartupScript');", true);
    }
}

6.ClientScript.RegisterClientScriptBlock
在Server端,要註冊js到網頁的最前面,(其實是body之後的最前面),透過RegisterClientScriptBlock(),一樣要透過ScriptManager

/// <summary>
/// 要Render在body裡面最前面的js
/// </summary>
private void RegisterClientScriptBlock()
{

    string blcokjs = @"function RegisterClientScriptBlock(){ alert('RegisterClientScriptBlock'); }";

    if (ScriptManager.GetCurrent(this.Page) == null)
    {
        Page.ClientScript.RegisterClientScriptBlock(this.Page.GetType(), "block", blcokjs, true);
    }
    else
    {
        ScriptManager.RegisterClientScriptBlock(this.Page, this.Page.GetType(), "block", blcokjs, true);
    }
}

7.Include js file by Page.Header.Controls.Add in server
Server端使用Page.Header.Controls.Add的方式,把js include在head區塊裡,每次load都要加,否則postback會掉

/// <summary>
/// 要每次Page Load都加入這段到head區塊裡
/// </summary>
private void addHeaderControl()
{
    HtmlGenericControl objjQuery = new HtmlGenericControl("script");
    objjQuery.Attributes.Add("language", "javascript");
    objjQuery.Attributes.Add("src", "HeaderControlJS.js");
    this.Page.Header.Controls.Add(objjQuery);

}

8.ClientScript.RegisterClientScriptInclude
Server端使用RegisterClientScriptInclude來動態引入js檔,也是每次load都要加,否則postback之後原始碼上就不會有這引入這段js

/// <summary>
/// 在server端include js,有Scriptmanager要透過Scriptmanager控制
/// 要每次Page Load都註冊,才會存在原始碼,postback才不會掉
/// </summary>
private void PageClientInclude()
{
    if (ScriptManager.GetCurrent(this.Page) == null)
    {
        Page.ClientScript.RegisterClientScriptInclude("PageClientInclude", "PageClientInclude.js");
    }
    else
    {
        ScriptManager.RegisterClientScriptInclude(this.Page, this.Page.GetType(), "PageClientInclude", "PageClientInclude.js");
    }
}

注意注意 No
在ASP.NET Webform中要執行一段JavaScript,不要再使用的一種方式,Response.Write(),這會導致webform畫面上的CSS失去作用,且有UpdatePanel的時候會有js error。

/// <summary>
/// 在ASP.NET裡,請不要再用下面的程式碼,因為會有兩個問題
/// 1.導致頁面的CSS失去作用
/// 2.如果畫面上有Scriptmanager+UpdatePanel,會出現exception
/// 請改寫成RegisterStartupScript()的方式
/// </summary>
private void CallJSbyResponse()
{ Response.Write(@"<script>alert('I am Response.write');</script>"); }

另外,附上一張重要的圖,是最後Render到畫面上的原始碼,因應使用方式不同,js擺放在不同的位置。

]]> 就是91 2010-10-07 11:31:55
91之ASP.NET由淺入深 不負責講座 Day9 - Page Life Cycle https://ithelp.ithome.com.tw/articles/10052353?sc=rss.iron https://ithelp.ithome.com.tw/articles/10052353?sc=rss.iron 一個ASP.NET Webform工程師,一定得知道的東西,就是頁面生命週期的順序,以及在每個事件該放什麼樣的code。這幾乎已經列在每一家開發Web系統公司面試題目必考題。
這邊簡...]]>
一個ASP.NET Webform工程師,一定得知道的東西,就是頁面生命週期的順序,以及在每個事件該放什麼樣的code。這幾乎已經列在每一家開發Web系統公司面試題目必考題。
這邊簡單做個整理,如果您還不知道,就趁機把ASP.NET Webform頁面運作的機制瞭解一下。

沒有瞭解頁面的生命週期、事件的順序,就沒法把對應的code寫在最對的事件上,不瞭解頁面生命週期的工程師,寫出來的code,一樣會動,但是寫出來的code,不一定對。只要遇到需求變更,bug就會一一浮現出來。

這也是為什麼,這麼多面試ASP.NET工程師的場合,都會問這問題的原因。因為一次就可以篩選出,真正有用心去瞭解ASP.NET頁面運作原理的工程師。而不是講了一嘴自己寫了幾年ASP.NET程式,寫過幾個大系統,連這都不知道的工程師。
Page Life Cycle
Page與Control事件週期圖如下:
左邊是頁面生命週期的階段,中間是頁面生命週期的事件,右邊則是寫程式比較少用到,但設計時必須得知道發生時間點的事件。

參考資料
1.ASP.NET 網頁存留週期概觀:http://msdn.microsoft.com/zh-tw/library/ms178472.aspx
2.ASP.NET Web 伺服器控制項事件模型:[ http://msdn.microsoft.com/zh-tw/library/y3bwdsh3.aspx]( http://msdn.microsoft.com/zh-tw/library/y3bwdsh3.aspx)
3.http://blogs.msdn.com/aspnetue/archive/2010/01/14/asp-net-page-life-cycle-diagram.aspx

注意
1.有一些事件,其實是方便開發者在頁面與控制項交替進行事件時,安插在中間供彈性使用(通常就是拿來覆寫掉某些資訊)。
2.事件順序跟事件常用目的很重要
3.常用事件記憶方式:
(a)先記住Init, Load, PreRender,然後在前面加上Pre,在後面加上Complete。
額外需要注意的是,PreRender已經有Pre,而Render是另外一個特定字(是個方法),所以PreRender只要在後面加Complete。
(b)Control: Render,是一個特殊方法,而非事件。主要用來繪製最後要Render的HTML。
(c)特殊事件:
(1)Control: LoadVeiwState事件,介於InitComplete與PreLoad之間
(2)Control event,介於Load與LoadComplete之間
(3)Control: SaveViewState事件,介於PreRenderComplate與Render方法之間

事件
1.PreInit
注意:
(a)檢查 IsPostBack 屬性判斷是否是第一次處理網頁。
(b)建立或重新建立動態控制項。
(1)在這事件動態建立控制項,控制項的生命週期才會完整,尤其是有完整的ViewState相關處理。
(c)動態設定MasterPage。
(1)在這事件以後,因為頁面會自行呼叫套上主題與樣式的動作,之後的頁面事件都不允許再改變套用的MasterPage。
(d)動態設定 Theme 屬性。
(1)同MasterPage的觀念,Skin與Theme相關的屬性,只能在這個事件設定,之後就不允許改變。
(e)讀取或設定設定檔屬性值。
(f)如果這次Request是PostBack,控制項的值尚未從ViewState還原。如果在這個階段設定控制項屬性,則在下一個事件中可能會覆寫其值。

叮嚀:
(a)這個事件,因為尚未LoadViewState, 所以要讀取畫面上最新的值,只能使用Request, 因為Request的建立在頁面『開始』的階段就建立了。
(b)常見的使用情況,例如根據畫面上的下拉選單來決定套用不同的MasterPage或Theme, 此時如果用DroDownList.SelectedValue,則沒法子達到目的。 應該使用Request[DropDownList.UniqueID]。

2.Init
在初始化所有控制項並套用任何面板設定之後引發。這個事件可以用於讀取或初始化控制項屬性。

3.InitComplete
由 Page 物件引發。這個事件可以用於處理需要完成所有初始設定的工作。

4.PreLoad
(a)如果需要在 Load 事件之前先處理網頁或控制項,請使用這個物件。
(1)在 Page 引發這個事件之前,會先載入其本身和所有控制項的檢視狀態,再處理 Request 執行個體 (Instance) 所包含的任何回傳資料。
(b)叮嚀
(1)從這個事件開始,因為已經把ViewState載入, 所以大部分控制項屬性或資料如果使用ViewState當暫存機制, 這個事件之後就可以比較安心的使用控制項的屬性了。

5.Load
(a)Page 會先呼叫 Page 上的 OnLoad 事件方法,再遞迴地為每個子控制項執行相同動作,它會為其每個子控制項執行相同動作,直到載入完網頁和所有控制項為止。
(b)OnLoad 事件方法可以用於設定控制項中的屬性,以及建立資料庫連接。

叮嚀
這邊有個特性蠻特別的, 就是子控制項Init之後,才會引發Page_Init (容器的Init都會比子控制項Init晚), 但是Load的事件,卻是Page_Load(容器)之後,才會引發子控制項Load。 同樣的道理,也可以印證在UserControl裡,UserControl就是一個容器。 因為這些容器有實作INamingContainer,所以後面的ClientID都會套上container的ID當作prefix。 而MasterPage,是繼承UserControl,所以也具備UserControl的特性,故MasterPage也可以巢狀使用)。通常我會在這邊開始做控制項與資料的繫結。

6.Control events
(a)這些控制項事件可以用於處理特定控制項事件,例如 Button 控制項的 Click 事件,或 TextBox 控制項 TextChanged 事件。
(b)在回傳要求中,如果網頁包含驗證程式控制項,則在執行任何處理之前,請先檢查 Page 和個別驗證控制項的 IsValid 屬性。

叮嚀
通常對client端操作UI來說,這個Control的事件,就代表了User的操作, 而在操作之前,我們都應該檢查畫面上要使用或存入的資料,以避免存入garbage data。 這也是為什麼頁面的『驗證』階段,會擺在Control event的最前面。

通常我也會在Control Event對應的動作處理完後, 呼叫自己寫的重新binding資料的method。例如修改的存檔按鈕完,要重新整理GridView內容。

7.LoadComplete
(a)這個事件可以用於需要載入網頁上所有其他控制項的工作。

叮嚀
通常在Control Event之後,會在這根據新資料對畫面UI的顯示隱藏控制的method。 還有狀態定義改變的method(例如按了新增的按鈕後,新增成功,狀態要變成修改)

也就是根據Control的執行後,要重新整理頁面相關資料,我會寫在這邊。 (寫在Control Event的最後其實也可以,但是這樣就是耦合性變高, 因為是自己去定義動作的先後順序,而非遵循頁面生命週期的順序)

8.PreRender
在這個事件發生之前:
(a)Page 物件會呼叫每個控制項和網頁的 EnsureChildControls。
(b)每個已設定其 DataSourceID 屬性的資料繫結控制項會呼叫其 DataBind 方法。如需詳細資訊,請參閱本主題稍後的資料繫結控制項的資料繫結事件。

每個控制項的 PreRender 事件都會發生在網頁上。這個事件可以用於對網頁或其控制項的內容進行最終的變更。

叮嚀
簡單來說,這會是我拿來定義一般頁面程式撰寫,最後的一個event。 還有什麼樣式、要註冊的js、屬性,要在server端改的,這是最後一個事件了。

正因為定位在最後一個事件,所以很常拿來當作覆寫屬性的事件。 講明白點,就是『我不管前面怎麼寫,到這邊就是要這樣做』的情況,就寫在這個事件。針對的,仍然是頁面或控制項提供的東西。

9.SaveStateComplete
(a)網頁和所有控制項的 ViewState 在這個事件發生之前就已儲存了。在這之後對網頁或控制項所做的任何變更都將會被忽略。
(b)這個事件可以用於執行需要儲存檢視狀態但不對控制項做任何變更的工作。

叮嚀
MSDN上寫,『在這之後,對網頁或控制項所做的任何變更,都將會被忽略。』 所以,在PreRender的事件,才會被定義為一般使用的最後一個事件。

10.Render
(a)這不是一個事件;
(b)相反地,在這個處理階段,Page 物件會呼叫每個控制項上的這個方法。所有 ASP.NET Web 伺服器控制項都有 Render 方法,它可以寫出傳送至瀏覽器之控制項的標記。
(c)如果您建立自訂控制項,通常會覆寫這個方法,以便輸出控制項的標記。不過,如果自訂控制項只包含標準 ASP.NET Web 伺服器控制項且無任何自訂標記,就不需要覆寫 Render 方法。如需詳細資訊,請參閱 開發自訂的 ASP.NET 伺服器控制項。
http://msdn.microsoft.com/zh-tw/library/zt27tfhy.aspx
(d)使用者控制項 (.ascx 檔案) 自動會包含呈現,因此不需要在程式碼中明確呈現控制項。

叮嚀
這不是一個事件!多神奇的一句話,沒錯,他只是個固定會在呈現階段,被呼叫的一個方法。 目的就是拿來『繪製』頁面或控制項。

為什麼TextBox長的像TextBox,為什麼Button長的像Button, 為什麼GridView最後長出來是Table, 為什麼DropDownList吐出來的原始碼是Select, 都是在這邊進行繪製的動作,也就是把Server端的WebControl相關資訊,繪製成相關的HTML。

我之前有個需求,當DropDownList唯讀時,要轉成label,也是用類似的作法,覆寫成其他HTML元素來呈現

11.Unload
(a)這個事件會先發生在每個控制項上,然後才發生在網頁上。在控制項中,這個事件可以用於對特定控制項執行最終清除,例如關閉控制項特定的資料庫連接。
(b)對於網頁本身,這個事件可以用於執行最終清除工作,例如關閉開啟中的檔案或資料庫連接,或是結束登入或其他要求特定的工作。

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.頁面的事件週期,有哪一些常見的事件,按照順序排出來。
2.上述的事件通常會拿來做什麼事?
3.要調整Theme、MasterPage、SkinID得在哪一個事件?
4.那個事件之後才可以使用ViewState? 沒有ViewState時該如何讀取Form上面控制項的值?
5.Render是用來幹嘛的?
6.什麼事件之後,就不能使用ViewState了?

]]>
就是91 2010-10-06 13:16:55
91之ASP.NET由淺入深 不負責講座 Day8 - Server Controls Introduction(4) https://ithelp.ithome.com.tw/articles/10052012?sc=rss.iron https://ithelp.ithome.com.tw/articles/10052012?sc=rss.iron 這一篇要介紹的是DropDownList, ListBox, RadioButtonList, CheckBoxList,其餘比較複雜的資料繫結控制項,如Repeater, DataList, ...]]> 這一篇要介紹的是DropDownList, ListBox, RadioButtonList, CheckBoxList,其餘比較複雜的資料繫結控制項,如Repeater, DataList, DataGrid, GridView, ListView等等,因為要注意的事項太多了,就請有興趣的人請自行至MSDN文件庫查閱,上面有完整的文件說明以及sample code。可以從目的當作切入點,去參考一些How To的文章。
DropDownList的介紹
1.MSDN reference:http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.dropdownlist(VS.80).aspx
2.屬性
(a)Items:清單控制項中項目的集合
(b)DataTextField:要Binding到Item.Text的欄位名稱
(c)DataValueField:要Binding到Item.Value的欄位名稱
(d)SelectedIndex:選取項目的索引,沒選為-1
(e)SelectedValue:選取項目的值
(f)SelectedItem:具有最低索引的選取項目
(g)AppendDataBoundItems:是否在資料繫結之前清除清單項目,通常拿來做第一個『請選擇』的項目,設定為true之後,binding的items為Append
(h)AutoPostBack:設定為true,則在client端onchange時呼叫__doPostBack()
3.方法
(a)DataBind(),給完DataSource之後,記得要呼叫DataBind()才會實際繫結。呼叫DataBind()之後,會將原本選取的狀態清空
(b)Items.Clear():將DropDownList的項目清空
4.事件:SelectedIndexChanged,當SelectedIndex與上次的值有改變的時候觸發
5.HTML tag
(a)DropDownList:<select>
(b)Items:<option>
6.注意
(a)Index的範圍:注意超出索引範圍的exception
(b)DataBind()之前,應設定DataTextField與DataValueField,給定DataSource後,再呼叫DataBind()
(c)呼叫DataBind()會清空選取的狀態,如果碰到postback後選取狀態掉了,很有可能就是DataBind()的部分沒有判斷 !IsPostBack
(d)連動式DropDownList的作法
(1)拉式選單的連動:http://social.msdn.microsoft.com/Forums/zh-TW/236/thread/9f0342e6-c717-4534-9bf8-02808332139b
(2)CascadingDropDown with PageMethods:http://bibby.be/2008/04/cascadingdropdown-with-pagemethods.html
(3)ASP.NET MVC 連動式下拉式選單:http://demo.tc/Post/547

ListBox的介紹
1.MSDN reference:http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.listbox(VS.80).aspx
2.屬性
(a)Rows:呈現列數
(b)SelectionMode:多選或單選
3.資料繫結方式同DropDownList
4.HTML tag
(a)ListBox:預設為<select size="4" />
(b)Rows:size屬性
(c)electionMode:multiple="multiple"
5.注意:常用變形例子:黑暗執行緒:CODE-用jQuery實作<select>選項上下移動(複選版)

RadioButtonList的介紹
1.MSDN reference:http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.radiobuttonlist(VS.80).aspx
2.屬性
(a)RepeatColumns:顯示的行數,會根據RepeatDirection調整為欄或列
(b)RepeatDirection:群組中選項按鈕的顯示方向,水平或垂直
(c)RepeatLayout:群組內選項按鈕的配置
(1)Table:顯示在table中
(2)Flow:不顯示table結構,通常用在最後一個radio後面還需要緊接著其他control,例如TextBox
3.資料繫結方式同DropDownList
4.用在單選,且需可以看到全部選項的清單
5.HTML tag
(a)Table:<table id="server control.ID"><tr><td><input type="radio" name="server control.ID"><label>XXX</label></input></td></tr></table>
(b)Flow:<span>取代<table<tr><td>

CheckBoxList的介紹
1.MSDN reference:http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.checkboxlist(VS.80).aspx
2.行為屬性同RadioButtonList
3.注意
(a)SelectedValue只選的到有勾選,且index最小的值
(b)如何確認有哪一些項目被勾選?可透過foreach checkboxlist.items + if(item.checked),也可參考:CheckBoxList複選的SelectedValue,用逗號串接

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.DropDownList該如何bind資料?
2.DropDownList該怎麼樣加入『請選擇』的項目,而不受datasource影響?
3.DropDownList對應的HTML為何?ListBox對應的HTML為何?Items對應的HTML為何?
4.CheckBoxList該如何得到勾選的值?
5.RadioButtonList與CheckBoxList該怎麼改變呈現的方向?
6.ListBox呈現的列數如何控制?對應的HTML屬性為何? 多選與單選該怎麼控制?對應的HTML屬性為何?

]]>
就是91 2010-10-05 00:27:36
91之ASP.NET由淺入深 不負責講座 Day7 - Server Controls Introduction(3) https://ithelp.ithome.com.tw/articles/10051827?sc=rss.iron https://ithelp.ithome.com.tw/articles/10051827?sc=rss.iron 這一篇要介紹的是Button, HyperLink, LinkButton, ImageButton,原本想把Image也放進來,因為Button, HyperLink, Image後面還帶著變...]]> 這一篇要介紹的是Button, HyperLink, LinkButton, ImageButton,原本想把Image也放進來,因為Button, HyperLink, Image後面還帶著變形的LinkButton與ImageButton,會比較完整。不過因為Image比較沒啥好介紹的,所以就先省略了,有興趣的人請自行至MSDN文件庫查閱。
Button的介紹
1.MSDN reference:http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.button(VS.80).aspx

2.屬性
(a)CauseValidation:設定為false,即可避免觸發同ValidationGroup的Validator驗證功能。通常用在取消、清畫面、關閉視窗等用途…
(b)CommandName:藉由設定 CommandName 屬性,[命令] 按鈕可以擁有與按鈕關聯的命令名稱,例如 Sort。這使您可以在 Web 網頁上建立多個 Button 控制項,並以程式設計的方式判斷已經按下哪個 Button 控制項。
(c)CommandArgument:搭配命令按鈕來使用 CommandArgument 屬性,提供有關所要執行命令的額外資訊,例如 Ascending。
(d)UseSubmitBehavior:可改變Render出來的HTML tag為submit或button。
(e)OnClientClick:透過此屬性,可直接設定client端的onclick事件要執行什麼樣的JavaScript,等同於thisButton.Attribute.add("onclick","my JavaScript");。

3.事件:OnClick,若有多個按鈕使用同一個委派方法,可透過e.CommandName來辨別此事件為哪一個Button觸發。可再透過e.CommandArgument取得需要的資訊。

4.HTML tag:根據UseSubmitBehavior而有所不同
(a)true:<input type='submit' />
(b)false:<input type='button' />

5.注意
(a)若只是需要使用client端JavaScript的動作,請盡量避免使用server control的Button,直接使用HTML control的button,可避免postback以及無謂的資源浪費。若非得用server control的button,又不希望觸發client端的onclick後postback,記得在JavaScript執行完後,加上return false;即可。
(b)Button放在一些資料繫結的server control裡面,例如GridView,CommandName會有一些關鍵字,會觸發GridView等control的特定事件,參考[http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.gridview.rowcommand(v=VS.80).aspx
](http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.gridview.rowcommand(v=VS.80).aspx&lt;br />
)

HyperLink的介紹
1.MSDN reference:http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.hyperlink(VS.80).aspx

2.屬性
(a)ImageUrl:以顯示為 ImageUrl 屬性所指定的影像,如果已設定 Text 和 ImageUrl 屬性,則以 ImageUrl 屬性為優先。如果沒有影像,將會顯示 Text 屬性中的文字。
(b)NavigateUrl:按一下 HyperLink 控制項時所要連結的 URL,對應到HTML <a>裡面的href屬性

3.Target
(a)_top:在無框架的完整視窗中呈現內容。
(b)_blank:在無框架的新視窗中呈現內容。
(c)_parent:在即時父代框架組中呈現內容。
(d)_search:在搜尋窗格中呈現內容。
(e)_self:在擁有焦點 (Focus) 的框架中呈現內容。

4.HTML tag:<a>

5.注意
(a)由於Text的部分仍可以用來顯示HTML,所以也要注意XSS,可參考 91之ASP.NET由淺入深 不負責講座 Day5 - Server Controls Introduction(1)
(b)若希望此hyperlink點選後沒導頁,第一種作法為將href設定為"#":回到本網頁的最上方。第二種作法為在HTML的onclick或href中,執行javascript:void(0);

LinkButton的介紹
1.MSDN reference:http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.linkbutton(VS.80).aspx

2.有Button行為的HyperLink

3.HTML tag:<a>,預設會Render出這樣的HTML:<a id="LinkButton1" href="javascript:__doPostBack('LinkButton1','')">LinkButton</a>

4.在server端可用Button的事件和屬性

ImageButton的介紹
1.MSDN reference:http://msdn.microsoft.com/zh-tw/library/system.web.ui.webcontrols.imagebutton(VS.80).aspx

2.有Button行為的Image

3.HTML tag:<input type="image" />

4.在server端可用Button的事件和屬性,也具備Image的屬性

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.Button該如何取消驗證?
2.如何辨認這次事件是由哪一個Button所觸發?
3.如何點選一個超連結後,設定開啟的window?
4.如何讓HyperLink點選後無反應?
5.如何讓HyperLink點選後PostBack,且在Server端觸發事件?
6.如何用圖片來呈現連結或按鈕?

]]>
就是91 2010-10-04 00:19:56
91之ASP.NET由淺入深 不負責講座 Day6 - Server Controls Introduction(2) https://ithelp.ithome.com.tw/articles/10051641?sc=rss.iron https://ithelp.ithome.com.tw/articles/10051641?sc=rss.iron 這篇要簡單介紹一下最常用的輸入項:TextBox。
TextBox的介紹:
1.MSDN官方網站說明MSDN 文件庫 reference

2.常用屬性
(a)Text:文字方塊中的文字,內建ViewState記值,PostBack後會自動記住。
(b)MaxLength:限制使用者輸入的指定字元數。
(c)ReadOnly:唯讀,需注意某一種特別弔詭的唯讀情況,請參考黑暗執行緒的文章TextBox.ReadOnly、Attributes["readonly"]及Disabled ,以及另一篇MSDN forum 阿尼的研究
(d)TextMode:可改變Render出來的HTML tag。
(e)AutoPostBack:設定為true,會在HTML DOM的onchange事件,加上呼叫__doPostBack()的function,當一旦觸發onchange則postback,將form的資料submit回自己這一頁處理。

3.常用server端事件
OnTextChanged,當TextBox的值與上一次的值有所改變時,即會觸發此事件。

4.HTML tag,根據TextMode而有所不同
(a)SingleLine: <input type='text' />
(b)MultiLine: <textarea />
(c)Password: <input type='password' />

5.一般用途
最常用的輸入項
(a)搭配Validators進行Client端與Server端的驗證
(b)外觀:
Textbox的顯示寬度是由它的 Columns 屬性決定 (以字元為單位)。
TextBox 控制項是多行文字方塊,它所顯示的列數將由 Rows 屬性決定。
若要顯示在 TextBox 控制項內換行的文字,應將 Wrap 屬性設定為 true。

6.注意
(a)TextMode=MultiLine時,MaxLength會失效,因為textarea無法使用MaxLength來限制輸入字元數。解決方式請參考:http://www.dotblogs.com.tw/hatelove/archive/2010/07/06/textarea\_maxlength.aspx
(b)AutoPostBack=false時,並不代表不會觸發server端的OnTextChanged事件,而是等到有任何回到server端處理時,只要值與上一次的值比較後有改變,就會觸發server端的對應事件 (這也是ViewState為何給兩次postback使用,以及event-driven model的基底)

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.TextBox對應的HTML tag有哪一些呢?是由哪一個屬性所控制呢?
2.TextBox的寬度,列數,可輸入字元數的屬性分別為何?
3.ReadOnly的注意事項
4.什麼時候會觸發server端的onTextChanged事件?
5.MaxLength在textarea失效,該如何解決?
6.為什麼TextBox的值PostBack後不用處理,值仍然存在?

]]> 就是91 2010-10-03 01:05:57
91之ASP.NET由淺入深 不負責講座 Day5 - Server Controls Introduction(1) https://ithelp.ithome.com.tw/articles/10051553?sc=rss.iron https://ithelp.ithome.com.tw/articles/10051553?sc=rss.iron 接下來會簡單介紹一些常見的server control,以及一些額外在開發上需要注意的事項。
這一篇文章先簡單介紹一下Label。
在開始介紹之前,我想先推薦幾個Refe...]]>
接下來會簡單介紹一些常見的server control,以及一些額外在開發上需要注意的事項。
這一篇文章先簡單介紹一下Label。
在開始介紹之前,我想先推薦幾個Reference,供大家參考,尤其是不知道server control的屬性、事件、方法或是適用情況與寫法時,可以參考一下:
1.MSDN文件庫
2.Visual Studio裡面的物件瀏覽器
3.MSDN forum

Label的介紹:
1.MSDN官方網站說明MSDN 文件庫 reference

2.屬性
Text,呈現的文字

3.HTML tag:<span>

4.一般用途:用來顯示文字,也有人會拿來Render HTML,其他也會拿來呈現文字或HTML的還有 Literal與PlaceHolder,他們的差異是:Literal與PlaceHolder不會顯示HTML tag,但要小心Cross-site scripting(XSS)。可參考:[HOW TO:利用將 HTML 編碼套用至字串的方法,防止會在 Web 應用程式中發生的指令碼攻擊](http://msdn.microsoft.com/zh-tw/library/a2a4yykt(v=VS.80).aspx&lt;br />
),以及擅用指令碼概觀

5.注意
在client端使用JavaScript修改Label內的文字,server端無法得知,用JavaScript修改後,postback後值會被還原,因為沒有使用ViewState記值。
解決方式:透過hidden暫存修改後的值,再assign回Text。可參考:[http://www.dotblogs.com.tw/hatelove/archive/2009/02/12/7125.aspx ](http://www.dotblogs.com.tw/hatelove/archive/2009/02/12/7125.aspx )

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.Label對應的HTML tag為何?
2.Label通常用在哪些地方?
3.Label與其他呈現HTML的server control差異在哪?
4.Label與JavaScript搭配時,需要注意什麼?
5.Label在呈現HTML時,可能會有什麼樣的問題?該怎麼避免?

]]>
就是91 2010-10-02 20:40:56
91之ASP.NET由淺入深 不負責講座 Day4 - Server Controls https://ithelp.ithome.com.tw/articles/10051361?sc=rss.iron https://ithelp.ithome.com.tw/articles/10051361?sc=rss.iron ASP.NET Webform最讓人津津樂道的,就是可以無痛開發web程式。
最讓人詬病的也是容易養出一堆只會拖拉控制項的無腦Web工程師。
Server control...]]>
ASP.NET Webform最讓人津津樂道的,就是可以無痛開發web程式。
最讓人詬病的也是容易養出一堆只會拖拉控制項的無腦Web工程師。
Server controls很好用,但一定要知道原理、目的,才會懂得變化。
這一篇先簡單帶到一些共同的特性,後續會再用一篇文章,把常用的control簡介一下。
Server control與HTML之間的關係?
Server control最後的結果也是Render成HTML與JavaScript,
developer要明白server control與HTML DOM的對應關係,例如:
例如TextBox的TextMode屬性有三種:
1.SingleLine,對應到HTML是<input type='text' />
2.MultiLine,對應到HTML是<textarea />
3.Password,對應到HTML是<input type='password' />

例如Button的UseSubmitBehavior屬性,
設定為true,對應到的HTML是<input type='submit' />
設定為false,對應到的HTML是<input type='button' />

與人溝通上也應避免使用錯誤的term而引起誤會,例如:
討論HTML應該講text,而不是講TextBox。
討論HTML應該講textarea,而不是TextBox。
討論server control應該講DropDownList,而非select,等等...

Server controls常見的ID, ClientID, UniqueID, SkinID差異在哪?
ID:在server端用來辨識唯一的Identity。
需要注意的是在data container中,例如template field,無法直接取用,要透過FindControl來找到該control並轉型使用。

ClientID:HTML上DOM的id。
需要注意的是當外面有container時,ClientID的prefix會加上container的id。
例如:MasterPage、UserControl、GridView等等...

UniqueID:HTML上DOM的name。
通常是Request.Form的時候要用到。

SkinID:類似server端的css一樣,只是幫server control套用某一些屬性(包括CssClass)
需要額外注意的是,有一些屬性無法套用、覆寫原有屬性的priority、要動態修改SkinID,只能在頁面Init事件之前。

What is server control : runat="server"
基本上有runat="server" 就可以稱的上是server control,因為可以從server端控制最後的HTML output stream。

Why server control?
透過server control,將generation of HTML and JavaScript封裝起來,可以用抽象的角度去使用這些Control達到方便使用以及重用性。

server control通常封裝了哪些東西?
1.Property
Property可以影響HTML的Render,決定最後什麼樣的屬性要長出什麼樣的HTML或javascript。例如:前面文章提到的AutoPostBack

2.Function
Function通常是一連串的邏輯或處理,定義成一個抽象意義的行為,例如DropDownList.DataBind()

3.Event
事件則是對應client端Render出來對應的DOM事件與ViewState,來決定是否要觸發相對應的事件。

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.Server control與HTML之間的關係?
2.ID, ClientID, UniqueID, SkinID是什麼?
3.怎樣算稱得上是server control?
4.為什麼需要server control?
5.Server control通常封裝了哪一些東西,方便developer使用?

]]>
就是91 2010-10-01 16:09:09
91之ASP.NET由淺入深 不負責講座 Day3 - ViewState https://ithelp.ithome.com.tw/articles/10051189?sc=rss.iron https://ithelp.ithome.com.tw/articles/10051189?sc=rss.iron ASP.NET Webform透過PostBack與ViewState來解決無狀態的http問題,以及Event-Driven Model的基底。
所以得要瞭解到底ViewState...]]>
ASP.NET Webform透過PostBack與ViewState來解決無狀態的http問題,以及Event-Driven Model的基底。
所以得要瞭解到底ViewState是什麼,以及其相關的issues。
stateless http
何謂無狀態的http,http並不會幫你記住任何事情,網頁的呈現,就是從server端下載一份document,然後在client的browser上呈現。在不同document之間,http並不會記住任何東西

ViewState的目的
ViewState對於client的網頁來說,完全用不到。ViewState的目的,是為了在postback之間記住上次的值。

Why ViewState
因為http stateless,而ASP.NET webform對post的處理,最常使用的就是postback,在server端觸發control event。如果沒有viewstate,那postback後,剛剛在網頁上輸入的值就會消失。
沒有ViewState的輔助,PostBack就失去了依據。

What is ViewState
ViewState簡單來說,就是類似一份dictionary,用來存放ASP.NET頁面上controls的狀態值給兩個連續的postback使用。
ViewState可以想像從asp延伸過來的,用hidden記值,把要記的都放在各個hidden裡面,方便等等重新Render的塞值。而下一次的postback,會再把這個hidden post回server端,就可以知道上一次controls的狀態值。

Where is ViewState
ASP.NET會將ViewState Render到HTML,並存放在hidden。
例如:HTML sample code

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTcxODU4OTc0MWRkXFVYlLGlkSawgvMi9dah0zKdlVI=" />

如何在code-behind自訂需要的ViewState
例如:

this.ViewState["myKey"] = "myValue";

Get與Set就類似Dictionary的用法。

ViewState的問題
為了記住兩次postback之間的control狀態值,得Render很多編碼後的文字到HTML中,會增加page size,所以可能造成頻寬問題。
且ViewState只有encode,而非encryption,要避免放有安全性議題的資料到viewstate

如何解決ViewState問題
1.不要用,例如EnableViewState設為false,可以設定Page或Server Controls不使用ViewState。
2.謹慎使用,例如:無安全性問題、頻寬充足、不需要用的就關掉、避免postback(如ajax)、採用其他方式記錄user state、頁面設計小一點、壓縮ViewState等等...

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.什麼是無狀態的http?
2.ViewState的目的為何?
3.沒有ViewState會有什麼問題?
4.到底什麼是ViewState?
5.ViewState存放在哪裡?
6.如何在code-behind自訂ViewState的存取?
7.ViewState有什麼樣的問題與特性?
8.要怎麼樣避免ViewState的問題?

]]>
就是91 2010-09-30 00:40:39
91之ASP.NET由淺入深 不負責講座 Day2 - PostBack https://ithelp.ithome.com.tw/articles/10051046?sc=rss.iron https://ithelp.ithome.com.tw/articles/10051046?sc=rss.iron You should know issues about PostBack
什麼是PostBack?
記得上一篇文章我們提到Post,也...]]>
You should know issues about PostBack
什麼是PostBack?
記得上一篇文章我們提到Post,也就是將form裡面的資料,經過submit,post到server端,server端可以接收到form的資料進行處理,且ASP.NET Webform只有一個form。

所以什麼是PostBack?
也就是Post+Back,Post給自己,或是Post回來自己這一頁。
將form裡面所有的資料,post回server端,且處理這個form的資料的URL,是同一個頁面來進行處理,我們稱為PostBack。

一般操作的方式,舉例來說:當Client端的User,點選Browser中網頁上的一個按鈕,這個click的動作向server發出Request至原本這個頁面,且將form裡面所有可獲得的資料post至server端。
該按鈕可能是存檔、送出、註冊、Next等等...這就是一個最常見的PostBack行為。

PostBack是怎麼運作的?
當form裡面,有server control的AutoPostBack設定為True時,則ASP.NET Webform會自動Render出一段ASP.NET產生的JavaScript:

function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

裡面有兩個參數,第一個參數為eventTarget, 簡單的說,就是PostBack是由哪一個物件所觸發的。第二個參數為eventArgument,簡單的說,就是觸發PostBack時,想要帶回去的argument值。
ASP.NET除了產生這一段JavaScript function以外,還會幫我們放上兩個hidden,分別為__EVENTTARGET跟__EVENTARGUMENT

<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />

目的就是當觸發__doPostBack()時,最後將theForm submit到server端後,server端可以知道這一次的PostBack相關資訊。
而設定AutoPostBack為True的server control,則會在Render出來的HTML DOM裡面,onchange的事件,加上javascript:setTimeOut(呼叫__doPostBack('參數1','參數2'))

也就是當值有所改變時,要觸發__doPostBack(),且將觸發的Control和需要帶回server端的值,塞到hidden裡面,然後submit form,將form裡所有資料post至server端自己這一頁做處理。(包括剛剛的兩個hidden)

如何判斷這一次的Request,是不是PostBack?
基本上,這個問題可以很簡單,也可以很困難,
簡單來說,Page有一個屬性為IsPostBack,如果是True,就代表這次的Request是PostBack送出的。False,則代表這個Request,不是PostBack送出的(通常是Get)。
困難的部分,在於IsPostBack是怎麼判斷的,這需要反組譯ASP.NET的code,裡面有相當多的條件(沒記錯的話,有8個情況),來決定最後return true or false,這邊我們就不深入探討。

PostBack的目的為何?
ASP.NET Webform何以如此簡易可以上手?很大的原因就是把http的stateless處理封裝起來,讓developer沒有感覺到stateless。另外很大的原因,就是透過server control讓developer可以不用撰寫太多HTML和JavaScript。
為什麼要把這堆東西都封裝起來?因為要讓習慣撰寫Winform程式的developer盡可能的無痛升級,讓他們感受不到開發習慣的改變。
可以PostBack,來達到event-driven model的開發方式。

至於剛剛說的http的stateless怎麼解決,我們留著下一篇講viewstate再提。
至於怎麼樣可以不寫太多HTML和JavaScript,我們留著之後講server control再提。

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.什麼是PostBack?
2.PostBack是怎麼運作的?
3.要怎麼判斷這一次Request是不是PostBack?
4.PostBack的目的為何?
5.當設定AutoPostBack為True時,Render出來的HTML會有什麼變化?
6.PostBack一定會做的事是什麼?
7.Server端如何知道這一次的PostBack由誰觸發?(知道之後,就可以觸發對應的Control Event)

]]>
就是91 2010-09-29 00:08:14
91之ASP.NET由淺入深 不負責講座 Day1 - Get/Post https://ithelp.ithome.com.tw/articles/10051001?sc=rss.iron https://ithelp.ithome.com.tw/articles/10051001?sc=rss.iron ASP.NET Webform的基本重點主要分成三類,
1.PostBack
2.ViewState
3.Server Controls
接下來幾...]]>
ASP.NET Webform的基本重點主要分成三類,
1.PostBack
2.ViewState
3.Server Controls
接下來幾天,會大概介紹一下,這幾個topic,有哪些相關的issue,以及菜鳥應該知道哪些東西。

額外說明:閱讀此文章,應該要對基本的HTML與JavaScript有一定的認知。
要撰寫ASP.NET Webform的程式,就要先知道http的兩個最常用的method,Get與Post。

何謂Get?
簡單的說,就是當Client端,User在Browser輸入一段URL之後,Browser透過URL跟該Server要這個URI資源,Server response一份document,也就是一份HTML+CSS+JavaScript的檔案,下載回來Client端,再透過Browser來parse與呈現此份document。

何謂Post?
當Client端的User在Browser的網頁中,在HTML的form裡面,輸入了一些資料後,將此form的資料submit出去,post給server端處理,server再將處理完該呈現的document response給client端的browser呈現處理完的網頁。

簡單來說,可以說Get幾乎只是透過URL跟server端要資料,唯一的條件,可能是QueryString或是REST上的URL。而Post則是將資料透過form submit,送給server端做處理。

為什麼要先講Get與Post,因為PostBack與Post有關,而PostBack在ASP.NET Webform要模擬Winform的Event Model息息相關。

註:ASP.NET Webform還有一個特色,網頁上只有一個form。

最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.什麼是client端?
2.什麼是server端?
3.網頁是存在哪裡,是透過何者呈現?
4.什麼是Get?
5.什麼是Post?

PS:文章中有一些term看不懂,沒關係,後面會慢慢的介紹到。也歡迎留言討論...

]]>
就是91 2010-09-28 21:54:51

paypal.me/twcctz50
http://blog.sina.com.tw/window/feed.php?ver=rss&type=entry&blog_id=48997
https://paypal.me/twcctz50?country.x=TW&locale.x=zh_TW
使用 PayPal.Me 連結付款給我: https://paypal.me/twcctz50?country.x=TW&locale.x=zh_TW 
健康是最好的禮物蛋黃油https://www.facebook.com/eggsoil  
在生寶妹之前,寶妹媽,就食用蛋黃油調養車禍後造成的心律不整等後遺症狀將近兩年,配合復健、整復治療和游泳運動等,逐漸恢復正常的心律。
直到懷寶妹後至今,感謝蛋黃油讓寶妹媽能恢復健康,給這意外來的祝福一個健康的生長環境。寶妹雖是家中最小的孩子,但也是最健康、幸福的孩子!惟有她是媽媽有豐富的母乳可供親餵。
至今,恭喜寶妹已經滿兩歲了!每天還是喜歡找媽媽喝餒餒睡覺,出生至今,身體健康,沒有感冒和生病的紀錄。祝福寶妹,能一直健康、快樂的成長、學習,成為眾人的祝福!
代工生產製造需一千斤
需用者請提早預約安排
每天補充蛋黃油可降低罹癌風險?!是的!!
昨晚,十多年老案主的弟弟周大哥說,二哥鼻咽癌治療恢復後的後遺症,又發作了!需配合醫師開的抗生素和補充細胞再生的營養素,又訂了10瓶50ml,補充瓶。
據了解個案案主,罹癌前的工作是從事印刷業。坦白說,有點醫學常識的人多半知道化學油墨對身體的傷害為何?
本科所學為設計的我,也曾在相關產業待過十幾年的時間,等到研究所和醫院合作完成論文後,才知道自己的工作:設計和教職,其實,都隱含著很高的罹癌風險!
我們都曾因此賠上過健康,但慶幸自己和案主們,都是有福之人!
感謝蛋黃油豐富的卵磷脂營養成份,除了維生素C之外,幾乎涵蓋了所有!在103學年間,我曾因超鐘點一週上課時數33小時,忙碌於家庭和工作間,哪時老二剛出生半年,做好月子後,馬上就恢復忙碌的教職工作,不出一個月,就為卵巢炎、併發盆腔炎,抗生素吃了半年,還是不見恢復的病痛所苦!
期間,幾乎每兩週跑醫院兩三趟,署基的婦科主任醫師,也建議我要跟校長請辭,調養身體為重!感謝校長體諒,讓我減課到16堂,撐到合約到期,沒有違約金的困擾,離職後,就回家照顧老二和調養自己的身體。
卵巢炎超痛的!併發盆腔炎更痛!從早痛到晚!醫師說,抗生素吃半年了,就不能再吃了!感謝醫師沒讓我繼續吃下去!
而是勸我調整工作、生活和飲食!於是,我又開始大量食用蛋黃油和自己料理三餐,就這樣子,吃了半年,某天,突然驚覺:卵巢不痛了耶!
感謝神!在恢復前的每一時刻,我被疼痛纏身,影響情緒和睡眠,每天都不知要多久,才會好?!只是一直做該做的事,好好吃飯、休息,調養身心!直到恢復時,才發覺:哇!幸好,半年就好了!
這是蛋黃油在我近十年的健康危機中,第二次救了我!感謝神創造各樣美好食物,保守、祝福我們能有機會恢復祂起初創造我們的美好!
感謝近十幾年來,因著每一次的健康危機,即時食用蛋黃油排毒、調養身體、恢復正常的心律,讓我能在身體得滋養後再懷孕生下健康的孩子們,也讓我們的生命得到延續。
為此,我才投入後來的時間、金錢、精神在服務和我一樣有需要的案主們身上。感謝大家一起陪我攜手走過已過的十幾年。祝福每個人都能有機會認知到維護健康的身體,其奧秘就在於養成正確的日常飲食習慣!
筆者:蛋黃油男
電話:02-24978169
手機:0989-422508
為資深個案自主健康管理設計師,
目前服務於品蔚養生設計事務所。
https://liker.social/invite/pRwYGraC
https://www.facebook.com/eggsoil
https://www.facebook.com/nectw721
https://www.instagram.com/0989422508mdc
https://www.pinterest.com/twcctz500
https://www.linkedin.com/in/twcctz500
https://www.twitter.com/twcctz500
https://currents.google.com/117454133619976175486
https://www.youtube.com/shorts/o8Jaud7px4w
https://www.youtube.com/channel/UC5E4_uNgGl_omnUVfL7fUyA
https://fitness-center-727.business.site/
https://g.page/r/CUt-sGCWmpP2EBM/review
https://matters.news/@twcctz500
https://pastebin.com/NgugYMZP   來自 @pastebin 
https://hardbin.com/ipfs/QmSmgLoGrr4dcJ4EFmszDXJAGbXW2oZU5nNgksdjG7bb3P/
https://zerobin.net/?b4e43b1d09b3dcbe#C4rH4SwtqM4v4BsehLtwVf2DSEhto1JRx8PzKIsmrYo=
https://zerobin.net/?4fdb0ea46d78f4a6#z7E38he/vzywjsvzxBwTBm5dvCaKc5Pei8nDkUflgOs=
https://privatebin.net/?8abe23c5b0253b66#7Vq3VyzThWp2ga7tvVBQ4om6aiYdqvma4bpfWYNKMCgG
https://medium.com/@twcctz50
https://about.me/twcctz500
paypal.me/twcctz50

ASP.NET 伺服器控制項開發 :: 2008 iT 邦幫忙鐵人賽 https://ithelp.ithome.com.tw/users/20007956/ironman zh-TW Mon, 06 Jun 2022 20:19:06 +0800 [ASP.NET 控制項實作 Day29] 解決 DropDownList 成員 Value 值相同產生的問題(續) https://ithelp.ithome.com.tw/articles/10013458?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013458?sc=rss.iron 接續上一文
接下來還要覆寫 LoadPostData 方法,取得 __EVENTARGUMENT 這個 HiddenField 的值,並判斷與原 SelectedIndex 屬性值是...]]>
接續上一文
接下來還要覆寫 LoadPostData 方法,取得 __EVENTARGUMENT 這個 HiddenField 的值,並判斷與原 SelectedIndex 屬性值是否不同,不同的話傳回 True,使其產生 SelectedIndexChanged 事件。

        Protected Overrides Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean
            Dim values As String()
            Dim iSelectedIndex As Integer

            Me.EnsureDataBound()
            values = postCollection.GetValues(postDataKey)

            If (Not values Is Nothing) Then
                iSelectedIndex = CInt(Me.Page.Request.Form("__EVENTARGUMENT"))
                If (Me.SelectedIndex <> iSelectedIndex) Then
                    MyBase.SetPostDataSelection(iSelectedIndex)
                    Return True
                End If
            End If
            Return False
        End Function

四、測試程式
在 TBDropDownList 的 SelectedIndexChanged 事件撰寫如下測試程式碼。

    Protected Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged
        Dim sText As String

        sText = String.Format("TBDropDownList: Index={0} Value={1}", DropDownList2.SelectedIndex, DropDownList2.SelectedValue)
        Me.Response.Write(sText)
    End Sub

執行程式,在 TBDropDownList 選取 "王五" 這個選項時,會正常顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。

接下選取 Value 值相同的 "陳六" 這個選項,也會正常引發 SelectedIndexChanged ,並顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/30/5830.aspx

]]>
jeff377 2008-10-30 21:23:12
[ASP.NET 控制項實作 Day29] 解決 DropDownList 成員 Value 值相同產生的問題 https://ithelp.ithome.com.tw/articles/10013457?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013457?sc=rss.iron DropDownList 控制頁的成員清單中,若有 ListItem 的 Value 值是相同的情形時,會造成 DropDownList 無法取得正確的 SelectedIndex 屬性值、且無...]]> DropDownList 控制頁的成員清單中,若有 ListItem 的 Value 值是相同的情形時,會造成 DropDownList 無法取得正確的 SelectedIndex 屬性值、且無法正確引發 SelectedIndexChanged 事件的問題;今天剛好在網路上看到有人在詢問此問題,所以本文將說明這個問題的源由,並修改 DropDownList 控制項來解決這個問題。
程式碼下載:ASP.NET Server Control - Day29.rar

一、DropDownList 的成員 Value 值相同產生的問題
我們先寫個測試程式來描述問題,在頁面上放置一個 DropDownList 控制項,設定 AutoPostBack=True,並加入四個 ListItem,其中 "王五" 及 "陳六" 二個 ListItem 的 Value 值相同。

    <asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True">
            <asp:ListItem Value="0">張三</asp:ListItem>
            <asp:ListItem Value="1">李四</asp:ListItem>
            <asp:ListItem Value="2">王五</asp:ListItem>
            <asp:ListItem Value="2">陳六</asp:ListItem>
    </asp:DropDownList>

在 DropDownList 的 SelectedIndexChanged 事件,輸出 DropDownList 的 SelectedIndex 及 SelectedValue 屬性值。

    Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
        Dim sText As String

        sText = String.Format("DropDownList: Index={0} Value={1}", DropDownList1.SelectedIndex, DropDownList1.SelectedValue)
        Me.Response.Write(sText)
    End Sub

執行程式,在 DropDownList 選取 "李四" 這個選項時,會正常顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。

接下來選取 "陳六" 這個選項時,竟然發生奇怪的現象,DorpDownList 竟然顯示相同 Value 值的 "王五" 這個成員的 SelectedIndex 及 SelectedValue 屬性值。

二、問題發生的原因
我們先看一下 DropDownList 輸出到用戶端的 HTML 原始碼。

<select name="DropDownList1" onchange="javascript:setTimeout('__doPostBack(\'DropDownList1\',\'\')', 0)" id="DropDownList1">
	<option selected="selected" value="0">張三</option>
	<option value="1">李四</option>
	<option value="2">王五</option>
	<option value="2">陳六</option>
</select>

DropDownList 是呼叫 __doPostBack 函式,只傳入 eventTarget參數 (對應到 __EVENTTARGET 這個 HiddenField) 為 DropDownList 的 ClientID;當 PostBack 回伺服端時,在 DropDownList 的 LoadPostData 方法中,會取得用戶端選取的 SelectedValue 值,並去尋找對應的成員的 SelectedIndex 值。可是問題來了,因為 "王五" 與 "陳六" 的 Value 是相同的值,當在尋找符合 Value 值的成員時,前面的選項 "王五" 會先符合條件而傳回該 Index 值,所以先造成取得錯誤的 SelectedIndex 。

Protected Overridable Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean
    Dim values As String() = postCollection.GetValues(postDataKey)
    Me.EnsureDataBound
    If (Not values Is Nothing) Then
        MyBase.ValidateEvent(postDataKey, values(0))
        Dim selectedIndex As Integer = Me.Items.FindByValueInternal(values(0), False)
        If (Me.SelectedIndex <> selectedIndex) Then
            MyBase.SetPostDataSelection(selectedIndex)
            Return True
        End If
    End If
    Return False
End Function

三、修改 DropDownList 控制項來解決問題
要解決這個問題最好的方式就是直接修改 DropDownList 控制項,自行處理前端呼叫 __doPostBack 的動作,將用戶端 DropDownList 選擇 SelectedIndex 一併傳回伺服端。所以我們繼承 DropDownList 命名為 TBDropDownList,覆寫 AddAttributesToRender 來自行輸出 PostBack 的用戶端指令碼,我們會用一個變數記錄 AutoPostBack 屬性,並強制將 AutoPostBack 屬性值設為 False,這是為了不要 MyBase 產生 PostBack 的指令碼;然後再自行輸出 AutoPostBack 用戶端指令碼,其中 __doPostBack 的 eventArgument 參數 (對應到 __EVENTARGUMENT 這個 HiddenField) 傳入 this.selectedIndex。

        Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)
            Dim bAutoPostBack As Boolean
            Dim sScript As String

            '記錄 AutoPostBack 值,並將 AutoPostBack 設為 False,不要讓 MyBase 產生 PostBack 的指令碼
            bAutoPostBack = Me.AutoPostBack
            Me.AutoPostBack = False

            MyBase.AddAttributesToRender(writer)

            If bAutoPostBack Then
                MyBase.Attributes.Remove("onchange")
                sScript = String.Format("__doPostBack('{0}',{1})", Me.ClientID, "this.selectedIndex")
                writer.AddAttribute(HtmlTextWriterAttribute.Onchange, sScript)
                Me.AutoPostBack = True
            End If
        End Sub

在頁面上放置一個 TBDropDownList 控制項,設定與上述案例相同的成員清單。

        <bee:TBDropDownList ID="DropDownList2" runat="server" AutoPostBack="True">
            <asp:ListItem Value="0">張三</asp:ListItem>
            <asp:ListItem Value="1">李四</asp:ListItem>
            <asp:ListItem Value="2">王五</asp:ListItem>
            <asp:ListItem Value="2">陳六</asp:ListItem>
        </bee:TBDropDownList>

執行程式查看 TBDropDownList 控制項的 HTML 原始碼,呼叫 __doPostBack 函式的參數已經被修改,eventArgument 參數會傳入該控制項的 selectedIndex。

<select name="DropDownList2" id="DropDownList2" onchange="__doPostBack('DropDownList2',this.selectedIndex)">
	<option selected="selected" value="0">張三</option>
	<option value="1">李四</option>
	<option value="2">王五</option>
	<option value="2">陳六</option>
</select>

[超過字數限制,下一篇接續本文]

]]>
jeff377 2008-10-30 21:15:23
[ASP.NET 控制項實作 Day28] 圖形驗證碼控制項(續) https://ithelp.ithome.com.tw/articles/10013365?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013365?sc=rss.iron 接續一上文
二、實作圖形驗證碼控制項
雖然我們可以使用 Image 控制項來呈現 ValidateCode.aspx 頁面產生的驗證碼圖...]]>
接續一上文
二、實作圖形驗證碼控制項
雖然我們可以使用 Image 控制項來呈現 ValidateCode.aspx 頁面產生的驗證碼圖形,可是這樣只處理一半的動作,因為沒有處理「使用者輸入的驗證碼」是否與「圖形驗證碼」相符,所以我們將實作一個圖形驗證碼控制項,來處理掉所有相關動作。
即然上面的示範使用 Image 控制項來呈現驗證碼,所以圖形驗證碼控制項就繼承 Image 命名為 TBValidateCode。

    < _
    Description("圖形驗證碼控制項"), _
    ToolboxData("<{0}:TBValidateCode runat=server></{0}:TBValidateCode>") _
    > _
    Public Class TBValidateCode
        Inherits System.Web.UI.WebControls.Image
    
    End

新增 ValidateCodeUrl 屬性,設定圖形驗證碼產生頁面的網址。

        ''' <summary>
        ''' 圖形驗證碼產生頁面網址。
        ''' </summary>
        < _
        Description("圖形驗證碼產生頁面網址"), _
        DefaultValue("") _
        > _
        Public Property ValidateCodeUrl() As String
            Get
                Return FValidateCodeUrl
            End Get
            Set(ByVal value As String)
                FValidateCodeUrl = value
            End Set
        End Property

覆寫 Render 方法,若未設定 ValidateCodeUrl 屬性,則預設為 ~/Page/ValidateCode.aspx 這個頁面。另外我們在圖形的 ondbclick 加上一段用戶端指令碼,其作用是讓用戶可以滑鼠二下來重新產生一個驗證碼圖形。

        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            Dim sUrl As String
            Dim sScript As String

            sUrl = Me.ValidateCodeUrl
            If String.IsNullOrEmpty(sUrl) Then
                sUrl = "~/Page/ValidateCode.aspx"
            End If
            If Me.BorderWidth = Unit.Empty Then
                Me.BorderWidth = Unit.Pixel(1)
            End If
            If Me.AlternateText = String.Empty Then
                Me.AlternateText = "圖形驗證碼"
            End If
            Me.ToolTip = "滑鼠點二下可重新產生驗證碼"
            Me.ImageUrl = sUrl
            If Not Me.DesignMode Then
                sScript = String.Format("this.src='{0}?flag='+Math.random();", Me.Page.ResolveClientUrl(sUrl))
                Me.Attributes("ondblclick") = sScript
            End If
            Me.Style(HtmlTextWriterStyle.Cursor) = "pointer"

            MyBase.Render(writer)
        End Sub

另外新增一個 ValidateCode 方法,用來檢查輸入驗證碼是否正確。還記得我們在產生驗證碼圖形時,同時把該驗證碼的值寫入 Session("_ValidateCode") 中吧,所以這個方法只是把用戶輸入的值與 Seesion 中的值做比對。

        ''' <summary>
        ''' 檢查輸入驗證碼是否正確。
        ''' </summary>
        ''' <param name="Code">輸入驗證碼。</param>
        ''' <returns>驗證成功傳回 True,反之傳回 False。</returns>
        Public Function ValidateCode(ByVal Code As String) As Boolean
            If Me.Page.Session(SessionKey) Is Nothing Then Return False
            If SameText(CCStr(Me.Page.Session(SessionKey)), Code) Then
                Return True
            Else
                Return False
            End If
        End Function

三、測試程式
在頁面放置一個 TBValidateCode 控制項,另外加一個文字框及按鈕,供使用者輸入驗證碼後按下「確定」鈕後到伺服端做輸入值比對的動作。

        <bee:TBValidateCode ID="TBValidateCode1" runat="server" />
        <bee:TBTextBox ID="txtCode" runat="server"></bee:TBTextBox>
        <bee:TBButton ID="TBButton1" runat="server" Text="確定" />

在「確定」鈕的 Click 事件中,我們使用 TBValidateCode 控制項的 ValidateCode 方法判斷驗證碼輸入的正確性。

    Protected Sub TBButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TBButton1.Click
        If TBValidateCode1.ValidateCode(txtCode.Text) Then
            Me.Response.Write("驗證碼輸入正確")
        Else
            Me.Response.Write("驗證碼輸入錯誤!")
        End If
    End Sub

執行程式,頁面就會隨機產生一個驗證碼圖形。

輸入正確的值按「確定」鈕,就會顯示「驗證碼輸入正確」的訊息。因為我們在同一頁面測試的關係,你會發現 PostBack 後驗證碼圖形又會重新產生,一般正常的做法是驗證正確後就導向另一個頁面。

當我們輸入錯誤的值,就會顯示「驗證碼輸入錯誤!」的訊息。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/29/5818.aspx

]]>
jeff377 2008-10-29 20:34:22
[ASP.NET 控制項實作 Day28] 圖形驗證碼控制項 https://ithelp.ithome.com.tw/articles/10013361?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013361?sc=rss.iron 在網頁上常把圖形驗證碼應用在登入或貼文的頁面中,因為圖形驗證碼具有機器不易識別的特性,可以防止機器人程式惡意的存取網頁。在本文中將實作一個圖形驗證碼的伺服器控制項,透過簡單的屬性設定就可以輕易地...]]> 在網頁上常把圖形驗證碼應用在登入或貼文的頁面中,因為圖形驗證碼具有機器不易識別的特性,可以防止機器人程式惡意的存取網頁。在本文中將實作一個圖形驗證碼的伺服器控制項,透過簡單的屬性設定就可以輕易地在網頁上套用圖形驗證碼。
程式碼下載:ASP.NET Server Control - Day28.rar

一、產生圖形驗證碼
我們先準備一個產生圖形驗證碼的頁面 (ValidateCode.aspx),這個頁面主要是繪製驗證碼圖形,並將其寫入記憶體資料流,最後使用 Response.BinaryWrite 將圖形輸出傳遞到用戶端。當我們輸出此驗證碼圖形的同時,會使用 Session("_ValidateCode") 來記錄驗證碼的值,以便後續與使用者輸入驗證碼做比對之用。

Partial Class ValidateCode
    Inherits System.Web.UI.Page

    ''' <summary>
    ''' 產生圖形驗證碼。
    ''' </summary>
    Public Function CreateValidateCodeImage(ByRef Code As String, ByVal CodeLength As Integer, _
        ByVal Width As Integer, ByVal Height As Integer, ByVal FontSize As Integer) As Bitmap
        Dim sCode As String = String.Empty
        '顏色列表,用於驗證碼、噪線、噪點
        Dim oColors As Color() = { _
            Drawing.Color.Black, Drawing.Color.Red, Drawing.Color.Blue, Drawing.Color.Green, _
            Drawing.Color.Orange, Drawing.Color.Brown, Drawing.Color.Brown, Drawing.Color.DarkBlue}
        '字體列表,用於驗證碼
        Dim oFontNames As String() = {"Times New Roman", "MS Mincho", "Book Antiqua", _
                                      "Gungsuh", "PMingLiU", "Impact"}
        '驗證碼的字元集,去掉了一些容易混淆的字元
        Dim oCharacter As Char() = {"2"c, "3"c, "4"c, "5"c, "6"c, "8"c, _
                                    "9"c, "A"c, "B"c, "C"c, "D"c, "E"c, _
                                    "F"c, "G"c, "H"c, "J"c, "K"c, "L"c, _
                                    "M"c, "N"c, "P"c, "R"c, "S"c, "T"c, _
                                    "W"c, "X"c, "Y"c}
        Dim oRnd As New Random()
        Dim oBmp As Bitmap
        Dim oGraphics As Graphics
        Dim N1 As Integer
        Dim oPoint1 As Drawing.Point
        Dim oPoint2 As Drawing.Point
        Dim sFontName As String
        Dim oFont As Font
        Dim oColor As Color

        '生成驗證碼字串
        For N1 = 0 To CodeLength - 1
            sCode += oCharacter(oRnd.Next(oCharacter.Length))
        Next

        oBmp = New Bitmap(Width, Height)
        oGraphics = Graphics.FromImage(oBmp)
        oGraphics.Clear(Drawing.Color.White)
        Try
            For N1 = 0 To 4
                '畫噪線
                oPoint1.X = oRnd.Next(Width)
                oPoint1.Y = oRnd.Next(Height)
                oPoint2.X = oRnd.Next(Width)
                oPoint2.Y = oRnd.Next(Height)
                oColor = oColors(oRnd.Next(oColors.Length))
                oGraphics.DrawLine(New Pen(oColor), oPoint1, oPoint2)
            Next

            For N1 = 0 To sCode.Length - 1
                '畫驗證碼字串
                sFontName = oFontNames(oRnd.Next(oFontNames.Length))
                oFont = New Font(sFontName, FontSize, FontStyle.Italic)
                oColor = oColors(oRnd.Next(oColors.Length))
                oGraphics.DrawString(sCode(N1).ToString(), oFont, New SolidBrush(oColor), CSng(N1) * FontSize + 10, CSng(8))
            Next

            For i As Integer = 0 To 30
                '畫噪點
                Dim x As Integer = oRnd.Next(oBmp.Width)
                Dim y As Integer = oRnd.Next(oBmp.Height)
                Dim clr As Color = oColors(oRnd.Next(oColors.Length))
                oBmp.SetPixel(x, y, clr)
            Next

            Code = sCode
            Return oBmp
        Finally
            oGraphics.Dispose()
        End Try
    End Function

    ''' <summary>
    ''' 產生圖形驗證碼。
    ''' </summary>
    Public Sub CreateValidateCodeImage(ByRef MemoryStream As MemoryStream, _
        ByRef Code As String, ByVal CodeLength As Integer, _
        ByVal Width As Integer, ByVal Height As Integer, ByVal FontSize As Integer)
        Dim oBmp As Bitmap

        oBmp = CreateValidateCodeImage(Code, CodeLength, Width, Height, FontSize)
        Try
            oBmp.Save(MemoryStream, ImageFormat.Png)
        Finally
            oBmp.Dispose()
        End Try
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim sCode As String = String.Empty
        '清除該頁輸出緩存,設置該頁無緩存
        Response.Buffer = True
        Response.ExpiresAbsolute = System.DateTime.Now.AddMilliseconds(0)
        Response.Expires = 0
        Response.CacheControl = "no-cache"
        Response.AppendHeader("Pragma", "No-Cache")
        '將驗證碼圖片寫入記憶體流,並將其以 "image/Png" 格式輸出
        Dim oStream As New MemoryStream()
        Try
            CreateValidateCodeImage(oStream, sCode, 4, 100, 40, 18)
            Me.Session("_ValidateCode") = sCode
            Response.ClearContent()
            Response.ContentType = "image/Png"
            Response.BinaryWrite(oStream.ToArray())
        Finally
            '釋放資源
            oStream.Dispose()
        End Try
    End Sub
End Class

我們將此頁面置於 ~/Page/ValidateCode.aspx,當要使用此頁面的圖形驗證碼,只需要在使用 Image 控制項,設定 ImageUrl 為此頁面即可。

<asp:Image ID="imgValidateCode" runat="server" ImageUrl="~/Page/ValidateCode.aspx" />

[超過字數限制,下一篇接續本文]

]]>
jeff377 2008-10-29 20:31:45
[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態(續2) https://ithelp.ithome.com.tw/articles/10013241?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013241?sc=rss.iron 接續上一文
接下來設定做為新增、編輯使用的 TBFormView 控制項,我們只使用 EditItemTemplate 來同時處理新增、刪除,所以 EditItemTemplate ...]]>
接續上一文
接下來設定做為新增、編輯使用的 TBFormView 控制項,我們只使用 EditItemTemplate 來同時處理新增、刪除,所以 EditItemTemplate 需要同時具有「新增」、「更新」、「取消」三個按鈕。其中 ProductID 為主索引欄位,所以我們使用 TBTextBox 來繫結 ProductID 欄位,設定 FormViewModeState.InsertMode="Enable" 使控制項在新增模式時為可編輯,設定 FormViewModeState.EditMode="Disable" 使控制項在修改模式是唯讀的。

        <bee:TBFormView ID="TBFormView1" runat="server" DataKeyNames="ProductID" DataSourceID="SqlDataSource1"
            DefaultMode="Edit" SingleTemplate="EditItemTemplate" BackColor="White" BorderColor="#CCCCCC"
            BorderStyle="None" BorderWidth="1px" CellPadding="3" GridLines="Both" Visible="False">
            <FooterStyle BackColor="White" ForeColor="#000066" />
            <RowStyle ForeColor="#000066" />
            <EditItemTemplate>
                ProductID:
                <bee:TBTextBox ID="TextBox1" runat="server" Text='<%# Bind("ProductID") %>'> 
                  <FormViewModeState EditMode="Disable" InsertMode="Enable">
                  </FormViewModeState>
                </bee:TBTextBox>

                '省略

                <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="True" CommandName="Insert"
                    Text="新增" />
                 <asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update"
                    Text="更新" />
                 <asp:LinkButton ID="UpdateCancelButton" runat="server" CausesValidation="False"
                    CommandName="Cancel" Text="取消" />
            </EditItemTemplate>
        </bee:TBFormView>

2. 測試新增模式
接下來執行程式,一開始為瀏覽模式,以 TBGridView 來呈現資料。

按下 Header 的「新增」鈕,就會隱藏 TBGridView,而切換到 TBFormView 的新增模式。其中繫結 ProductID 欄位的 TBTextBox 為可編輯模式,而下方的按鈕只會顯示「新增」及「取消」鈕。

在新增模式輸入完畢後,按下「新增」鈕,資料錄就會被寫入資料庫。

3. 測試修改模式
接下來測試修改模式,按下「編輯」鈕,就會隱藏 TBGridView,而切換到 TBFormView 的修改模式。其中繫結 ProductID 欄位的 TBTextBox 為唯讀模式,而下方的按鈕只會顯示「更新」及「取消」鈕。

在修改模式輸入完畢後,按下「更新」鈕,資料錄就會被寫入資料庫。

4. 頁面程式碼
示範了上述的操作後,接下來我們回頭看一下頁面的程式碼。你沒看錯,筆者也沒貼錯,真的是一行程式碼都沒有,因為所有相關動作都由控制項處理掉了。

Partial Class Day27
    Inherits System.Web.UI.Page

End Class

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx

]]>
jeff377 2008-10-28 13:57:23
[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態(續1) https://ithelp.ithome.com.tw/articles/10013239?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013239?sc=rss.iron 接續上一文
二、讓 TextBox 控制項可自行維護狀態
接下來擴展 TextBox 控制項,繼承 TextBox 命名為 TBText...]]>
接續上一文
二、讓 TextBox 控制項可自行維護狀態
接下來擴展 TextBox 控制項,繼承 TextBox 命名為 TBTextBox。新增 FormViewModeState 屬性 (TBFormViewModeState 型別),依 FormView Mode 來設定控制項狀。並覆寫 PreRender 方法,在此方法中呼叫 DoFormViewModeStatus 私有方法,依 FormView 的模式來處理控制項狀態。

    ''' <summary>
    ''' 文字框控制項。
    ''' </summary>
    < _
    Description("文字框控制項。"), _
    ToolboxData("<{0}:TBTextBox runat=server></{0}:TBTextBox>") _
    > _
    Public Class TBTextBox
        Inherits TextBox
        Private FFormViewModeState As TBFormViewModeState

        ''' <summary>
        ''' 依 FormViewMode 來設定控制項狀態。
        ''' </summary>
        < _
        Category(WebCommon.Category.Behavior), _
        NotifyParentProperty(True), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        DefaultValue("") _
        > _
        Public ReadOnly Property FormViewModeState() As TBFormViewModeState
            Get
                If FFormViewModeState Is Nothing Then
                    FFormViewModeState = New TBFormViewModeState
                End If
                Return FFormViewModeState
            End Get
        End Property

        ''' <summary>
        ''' 處理控制項狀態。
        ''' </summary>
        Private Sub DoControlStatus(ByVal ControlStatus As EControlState)
            Select Case ControlStatus
                Case EControlState.Enable
                    Me.Enabled = True
                Case EControlState.Disable
                    Me.Enabled = False
                Case EControlState.Hide
                    Me.Visible = False
            End Select
        End Sub

        ''' <summary>
        ''' 依 FormView 的模式來處理控制項狀態。
        ''' </summary>
        Private Sub DoFormViewModeStatus()
            Dim oFormView As FormView

            '若控制項置於 FormView 中,則依 FormView 的模式來處理控制項狀態
            If TypeOf Me.BindingContainer Is FormView Then
                oFormView = DirectCast(Me.BindingContainer, FormView)
                Select Case oFormView.CurrentMode
                    Case FormViewMode.Insert
                        DoControlStatus(Me.FormViewModeState.InsertMode)
                    Case FormViewMode.Edit
                        DoControlStatus(Me.FormViewModeState.EditMode)
                    Case FormViewMode.ReadOnly
                        DoControlStatus(Me.FormViewModeState.BrowseMode)
                End Select
            End If
        End Sub

        ''' <summary>
        ''' 覆寫。引發 PreRender 事件。
        ''' </summary>
        Protected Overrides Sub OnPreRender(ByVal e As EventArgs)
            MyBase.OnPreRender(e)
            '依 FormView 的模式來處理控制項狀態
            DoFormViewModeStatus()
        End Sub

    End Class

三、測試程式
1. 設定控制項相關屬性
我們使用 Northwnd 資料庫的 Products資料表為例,以 GridView+FormView 示範資料「新增/修改/刪除」的操作。在頁面拖曳 SqlDataSource 控制項後,在頁面上的使用 TBGridView 來顯示瀏覽資料。TBGridView 的 FormViewID 設為關連的 TBFormVIew 控制項;另外有使用到 TBCommandField,設定 ShowHeaderNewButton=True,讓命令列具有「新增」鈕。

        <bee:TBGridView ID="TBGridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID"
            DataSourceID="SqlDataSource1" FormViewID="TBFormView1">
            <Columns>
                <bee:TBCommandField ShowDeleteButton="True" ShowEditButton="True" 
                    ShowHeaderNewButton="True" >
                </bee:TBCommandField>
                
                '省略
                
            </Columns>
        </bee:TBGridView>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx

]]>
jeff377 2008-10-28 13:53:32
[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態 https://ithelp.ithome.com.tw/articles/10013233?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013233?sc=rss.iron GridV...]]> GridView+FormView 示範資料 新增/修改/刪除(進階篇:伺服器控制項) 一文中,示範了擴展 GridView 及 FormView 控制項,讓 GridView 可以透過屬性與 FormView 做關連來處理資料的「新增/修改/刪除」的動作。因為在該案例中,只使用 FormView 的 EditTemplate 同時處理「新增」及「修改」的動作,所以還需要自行撰寫部分程式碼去判斷控制項在新增或修改的啟用狀態,例如編號欄位在新增時為啟用,修改時就不啟用。在該文最後也提及其實有辨法讓這個案例達到零程式碼的目標,那就是讓控制項 (如 TextBox) 自行判斷所在的 FormView 的 CurrentMode,自行決定本身是否要「啟用/不啟用」、「顯示/隱藏」等狀態。本文以 TextBox 為例,說明如何修改 TextBox 讓它可以達到上述的需求。
程式碼下載:ASP.NET Server Control - Day27.rar
Northwnd 資料庫下載:NORTHWND.rar

一、TBFormViewModeState 類別

我們先定義 EControlState (控制項狀態) 列舉,描述控制項在特定模式的狀態為何。

    ''' <summary>
    ''' 控制項狀態列舉。
    ''' </summary>
    Public Enum EControlState
        ''' <summary>
        ''' 不設定。
        ''' </summary>
        NotSet = 0
        ''' <summary>
        ''' 啟用。
        ''' </summary>
        Enable = 1
        ''' <summary>
        ''' 不啟用。
        ''' </summary>
        Disable = 2
        ''' <summary>
        ''' 隱藏。
        ''' </summary>
        Hide = 3
    End Enum

再來定義 TBFormViewModeState 類別,用來設定控制項在各種 FormView 模式 (瀏覽、新增、修改) 中的控制項狀態。

''' <summary>
''' 依 FormViewMode 來設定控制項狀態。
''' </summary>
< _
Serializable(), _
TypeConverter(GetType(ExpandableObjectConverter)) _
> _
Public Class TBFormViewModeState
    Private FInsertMode As EControlState = EControlState.NotSet
    Private FEditMode As EControlState = EControlState.NotSet
    Private FBrowseMode As EControlState = EControlState.NotSet

    ''' <summary>
    ''' 在新增模式(FormViewMode=Insert)的控制項狀態。
    ''' </summary>
    < _
    NotifyParentProperty(True), _
    DefaultValue(GetType(EControlState), "NotSet") _
    > _
    Public Property InsertMode() As EControlState
        Get
            Return FInsertMode
        End Get
        Set(ByVal value As EControlState)
            FInsertMode = value
        End Set
    End Property

    ''' <summary>
    ''' 在編輯模式(FormViewMode=Edit)的控制項狀態。
    ''' </summary>
    < _
    NotifyParentProperty(True), _
    DefaultValue(GetType(EControlState), "NotSet") _
    > _
    Public Property EditMode() As EControlState
        Get
            Return FEditMode
        End Get
        Set(ByVal value As EControlState)
            FEditMode = value
        End Set
    End Property

    ''' <summary>
    ''' 在瀏覽模式(FormViewMode=ReadOnly)的控制項狀態。
    ''' </summary>
    < _
    NotifyParentProperty(True), _
    DefaultValue(GetType(EControlState), "NotSet") _
    > _
    Public Property BrowseMode() As EControlState
        Get
            Return FBrowseMode
        End Get
        Set(ByVal value As EControlState)
            FBrowseMode = value
        End Set
    End Property
End Class

定義為 TBFormViewModeState 型別的屬性是屬於複雜屬性,要套用 TypeConverter(GetType(ExpandableObjectConverter)),讓該屬性可在屬性視窗 (PropertyGrid) 擴展以便設定屬性值,如下圖所示。

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx

]]> jeff377 2008-10-28 13:45:43
[ASP.NET 控制項實作 Day26] 讓你的 GridView 與眾不同 https://ithelp.ithome.com.tw/articles/10013209?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013209?sc=rss.iron 在網路上可以找到相當多擴展 GridView 控制項功能的文章,在筆者的部落格中也有多篇提及擴展 GridView、DataControlField、BoundFIeld 功能的相關文章,在本文...]]> 在網路上可以找到相當多擴展 GridView 控制項功能的文章,在筆者的部落格中也有多篇提及擴展 GridView、DataControlField、BoundFIeld 功能的相關文章,在本文將這些關於擴展 GridView 控制項功能及欄位類別的相關文章做一整理簡介,若需要擴展 GridView 相關功能時可以做為參考。
1. 擴展 GridView 控制項 - 無資料時顯示標題列
摘要:當 GridView 繫結的 DataSource 資料筆數為 0 時,會依 EmptyDataTemplate 及 EmptyDataText 的設定來顯示無資料的狀態。若我們希望 GridView 在無資料時,可以顯示欄位標題,有一種作法是在 EmptyDataTemplate 中手動在設定一個標題列,不過這種作法很麻煩。本文擴展 GridView 控制項,直接透過屬性設定就可以在無資料顯示欄位標題。

2. 擴展 GridView 控制項 - 支援 Excel 及 Word 匯出
摘要:GridView 匯出 Excel 及 Word 文件是蠻常使用的需求,此篇文章將擴展 GridView 控制項提供匯出 Excel 及 Word 文件的方法。一般在 GridView 匯出的常見下列問題也會在此一併被解決。

3. GridView+FormView 示範資料 新增/修改/刪除(進階篇:伺服器控制項)
摘要:擴展 GridView 及 FormView 控制項,在 GridView 控制項中新增 FormViewID 屬性,關連至指定的 FormView 控制項 ID,就可以讓 GridView 結合 FormView 來做資料異動的動作。

4. 擴展 CommandField 類別 - 刪除提示訊息
摘要:新增 DeleteConfirmMessage 屬性,設定刪除提示確認訊息。

5. 擴展 CommandField 類別 - 刪除提示訊息含欄位值
摘要:設定刪除提示確認訊息中可包含指定 DataField 欄位值,明確提示要刪除的資料列。

6. 讓 CheckBoxField 繫結非布林值(0 或 1)欄位
摘要:CheckBoxField 若繫結的欄位值為 0 或 1 時 (非布林值) 會發生錯誤,本文擴展 CheckBoxField 類別,讓 CheckBoxField 有辨法繫結 0 或 1 的欄位值。

7. 擴展 CheckBoxField 類別 - 支援非布林值的雙向繫結
摘要:CheckBoxField 繫結的欄位值並無法直接使用 CBool 轉型為布林值,例如 "T/F"、"是/否" 之類的資料,若希望使用 CheckBoxField 來顯示就比較麻煩,一般的作法都是轉為 TemplateField,自行撰寫資料繫結的函式,而且只能支援單向繫結。在本文直接改寫 CheckBoxField 類別,讓 CheckBoxField 可以直接雙向繫結 "T/F" 或 "是/否" 之類的資料。

8. 擴展 CommandField 類別 - Header 加入新增鈕
摘要:支援在 CommandField 的 Header 的部分加入「新增」鈕,執行新增鈕會引發 RowCommand 事件。

9. GridView 自動編號欄位 - TBSerialNumberField
摘要:繼承 DataControlField 來撰寫自動編號欄位,若 GridView 需要自動編號欄位時只需加入欄位即可。

10. 自訂 GridVie 欄位類別 - 實作 TBDropDownField 欄位類別
摘要:支援在 GridView 中顯示下拉清單的欄位類別。

11. 自訂 GridView 欄位 - 日期欄位
摘要:支援在 GridView 中顯示日期下拉選單編輯的欄位類別。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/27/5793.aspx

]]>
jeff377 2008-10-27 22:37:14
[ASP.NET 控制項實作 Day25] 自訂 GridView 欄位 - 日期欄位(續) https://ithelp.ithome.com.tw/articles/10013091?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013091?sc=rss.iron 接續上一文
四、覆寫 ExtractValuesFromCell 方法 - 擷取儲存格的欄位值
當用戶端使用 GridView 編輯後執...]]>
接續上一文
四、覆寫 ExtractValuesFromCell 方法 - 擷取儲存格的欄位值
當用戶端使用 GridView 編輯後執行更新動作時,會呼叫 ExtractValuesFromCell 方法,來取得儲存格的欄位值,以便寫入資料來源。所以我們要覆寫 ExtractValuesFromCell 方法,將 Cell 或 TDateEdit 控制項的值取出填入具 IOrderedDictionary 介面的物件。

        ''' <summary>
        ''' 使用指定 DataControlFieldCell 的值填入指定的 IDictionary 物件。 
        ''' </summary>
        ''' <param name="Dictionary">用於儲存指定儲存格的值。</param>
        ''' <param name="Cell">包含要擷取值的儲存格。</param>
        ''' <param name="RowState">資料列的狀態。</param>
        ''' <param name="IncludeReadOnly">true 表示包含唯讀欄位的值,否則為 false。</param>
        Public Overrides Sub ExtractValuesFromCell( _
            ByVal Dictionary As IOrderedDictionary, _
            ByVal Cell As DataControlFieldCell, _
            ByVal RowState As DataControlRowState, _
            ByVal IncludeReadOnly As Boolean)

            Dim oControl As Control = Nothing
            Dim sDataField As String = Me.DataField
            Dim oValue As Object = Nothing
            Dim sNullDisplayText As String = Me.NullDisplayText
            Dim oDateEdit As TBDateEdit

            If (((RowState And DataControlRowState.Insert) = DataControlRowState.Normal) OrElse Me.InsertVisible) Then
                If (Cell.Controls.Count > 0) Then
                    oControl = Cell.Controls.Item(0)
                    oDateEdit = TryCast(oControl, TBDateEdit)
                    If (Not oDateEdit Is Nothing) Then
                        oValue = oDateEdit.Text
                    End If
                ElseIf IncludeReadOnly Then
                    Dim s As String = Cell.Text
                    If (s = " ") Then
                        oValue = String.Empty
                    ElseIf (Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) Then
                        oValue = HttpUtility.HtmlDecode(s)
                    Else
                        oValue = s
                    End If
                End If

                If (Not oValue Is Nothing) Then
                    If TypeOf oValue Is String Then
                        If (CStr(oValue).Length = 0) AndAlso Me.ConvertEmptyStringToNull Then
                            oValue = Nothing
                        ElseIf (CStr(oValue) = sNullDisplayText) AndAlso (sNullDisplayText.Length > 0) Then
                            oValue = Nothing
                        End If
                    End If

                    If Dictionary.Contains(sDataField) Then
                        Dictionary.Item(sDataField) = oValue
                    Else
                        Dictionary.Add(sDataField, oValue)
                    End If
                End If
            End If
        End Sub

五、測試程式
我們使用 Northwnd 資料庫的 Employees 資料表為例,在 GridView 加入自訂的 TBDateField 欄位繫結 BirthDate 欄位,另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 BirthDate 欄位來做比較。

            <bee:TBDateField DataField="BirthDate" HeaderText="BirthDate" 
                SortExpression="BirthDate" DataFormatString="{0:d}" 
                ApplyFormatInEditMode="True" CalendarStyle="Winter" />            
            <asp:BoundField DataField="BirthDate" HeaderText="BirthDate" 
                SortExpression="BirthDate" DataFormatString="{0:d}" 
                ApplyFormatInEditMode="True" ReadOnly="true" />

執行程式,在編輯資料列時,TBDateField 就會以 TDateEdit 控制項來進行編輯。

使用 TDateEdit 編輯欄位值後,按「更新」鈕,資料就會被寫回資料庫。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/26/5777.aspx

]]>
jeff377 2008-10-26 17:04:50
[ASP.NET 控制項實作 Day25] 自訂 GridView 欄位 - 日期欄位 https://ithelp.ithome.com.tw/articles/10013083?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013083?sc=rss.iron 前二篇文章介紹了自訂 GridView 使用的下拉清單欄位 (TBDropDownField),對如何繼承 BoundField 類別下來改寫自訂欄位應該有進一步的了解。在 GridView 中...]]> 前二篇文章介紹了自訂 GridView 使用的下拉清單欄位 (TBDropDownField),對如何繼承 BoundField 類別下來改寫自訂欄位應該有進一步的了解。在 GridView 中輸入日期也常蠻常見的需求,在本文將再實作一個 GridView 使用的日期欄位,在欄位儲存格使用 TBDateEdit 控制項來編輯資料。
程式碼下載:ASP.NET Server Control - Day25.rar
Northwnd 資料庫下載:NORTHWND.rar

一、繼承 TBBaseBoundField 實作 TDateField
GridView 的日期欄位需要繫結資料,一般的作法是由 BoundField 繼承下來改寫;不過我們之前已經有繼承 BoundField 製作一個 TBBaseBoundField 的自訂欄位基底類別 (詳見「 [ASP.NET 控制項實作 Day23] 自訂 GridVie 欄位類別 - 實作 TBDropDownField 欄位類別」 一文),所以我們要實作的日期欄位直接繼承 TBBaseBoundField 命名為 TDateField,並覆寫 CreateField 方法,傳回 TDateField 物件。

    ''' <summary>
    ''' 日期欄位。
    ''' </summary>
    Public Class TBDateField
        Inherits TBBaseBoundField

        Protected Overrides Function CreateField() As DataControlField
            Return New TBDateField()
        End Function
    End Class

自訂欄位類別主要是要覆寫 InitializeDataCell 方法做資料儲存格初始化、覆寫 OnDataBindField 方法將欄位值繫結至 BoundField 物件、覆寫 ExtractValuesFromCell 方法來擷取儲存格的欄位值,下面我們將針對這幾個需要覆寫的方法做一說明。

二、覆寫 InitializeDataCell 方法 - 資料儲存格初始化
首先覆寫 InitializeDataCell 方法處理資料儲存格初始化,當唯讀狀態時使用 Cell 來呈現資料;若為編輯狀態時,則在 Cell 中加入 TBDateEdit 控制項,並將 TBDateField 的屬性設定給 TBDateEdit 控制項的相關屬性。然後將儲存格 (DataControlFieldCell) 或日期控制項 (TDateEdit) 的 DataBinding 事件導向 OnDataBindField 事件處理方法。

        ''' <summary>
        ''' 資料儲存格初始化。
        ''' </summary>
        ''' <param name="Cell">要初始化的儲存格。</param>
        ''' <param name="RowState">資料列狀態。</param>
        Protected Overrides Sub InitializeDataCell(ByVal Cell As DataControlFieldCell, ByVal RowState As DataControlRowState)
            Dim oDateEdit As TBDateEdit
            Dim oControl As Control

            If Me.CellIsEdit(RowState) Then
                '編輯狀態在儲存格加入 TBDateEdit 控制項
                oDateEdit = New TBDateEdit()
                oDateEdit.FirstDayOfWeek = Me.FirstDayOfWeek
                oDateEdit.ShowWeekNumbers = Me.ShowWeekNumbers
                oDateEdit.CalendarStyle = Me.CalendarStyle
                oDateEdit.Lang = Me.Lang
                oDateEdit.ShowTime = Me.ShowTime
                oControl = oDateEdit
                Cell.Controls.Add(oControl)
            Else
                oControl = Cell
            End If

            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then
                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)
            End If
        End Sub

TDateEdit 控制項為筆者自行撰寫的日期控制項,TDateEdit 控制項的相關細節可以參考筆者部落格下面幾篇文章有進一步說明。
日期控制項實作教學(1) - 結合 JavaScript
日期控制項實作教學(2) - PostBack 與 事件
TBDateEdit 日期控制項 - 1.0.0.0 版 (Open Source)

三、覆寫 OnDataBindField 方法 - 將欄位值繫結至 BoundField 物件
當 GridView 執行 DataBind 時,每個儲存格的 DataBinding 事件都會被導向 OnDataBindField 方法,此方法中我們會由資料來源取得指定欄位值,處理此欄位值的格式化時,將欄位值呈現在 Cell 或 TDateEdit 控制項上。

        ''' <summary>
        ''' 將欄位值繫結至 BoundField 物件。 
        ''' </summary>
        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)
            Dim oControl As Control
            Dim oDateEdit As TBDateEdit
            Dim oNamingContainer As Control
            Dim oDataValue As Object            '欄位值
            Dim bEncode As Boolean              '是否編碼
            Dim sText As String                 '格式化字串

            oControl = DirectCast(sender, Control)
            oNamingContainer = oControl.NamingContainer
            oDataValue = Me.GetValue(oNamingContainer)
            bEncode = ((Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) AndAlso TypeOf oControl Is TableCell)
            sText = Me.FormatDataValue(oDataValue, bEncode)

            If TypeOf oControl Is TableCell Then
                If (sText.Length = 0) Then
                    sText = " "
                End If
                DirectCast(oControl, TableCell).Text = sText
            Else
                If Not TypeOf oControl Is TBDateEdit Then
                    Throw New HttpException(String.Format("{0}: Wrong Control Type", Me.DataField))
                End If

                oDateEdit = DirectCast(oControl, TBDateEdit)
                If Me.ApplyFormatInEditMode Then
                    oDateEdit.Text = sText
                ElseIf (Not oDataValue Is Nothing) Then
                    oDateEdit.Text = oDataValue.ToString
                End If
            End If
        End Sub

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx

]]>
jeff377 2008-10-26 16:56:36
[ASP.NET 控制項實作 Day24] TBDropDownField 的 Items 屬性的資料繫結(續) https://ithelp.ithome.com.tw/articles/10013047?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013047?sc=rss.iron 接續上一文
三、由關連的資料來源擷取資料
再來就是重點就是要處理 PerformSelecrt 私有方法,來取得 Items 屬性的成員...]]>
接續上一文
三、由關連的資料來源擷取資料
再來就是重點就是要處理 PerformSelecrt 私有方法,來取得 Items 屬性的成員清單內容。PerformSelect 方法的作用是去尋找頁面上的具 IDataSource 介面的控制項,並執行此資料來源的 Select 方法,以取得資料來設定 Items 的清單內容。
step1. 尋找資料來源控制項
PerformSelect 方法中有使用 FindControlEx 方法,它是自訂援尋控制項的多載方法,是取代 FindControl 進階方法。程式碼中使用 FindControlEx 去是頁面中以遞迴方式尋找具有 IDataSource 介面的控制項,且 ID 屬性值為 TBDropDownList.ID 的屬性值。
step2. 執行資料來源控制項的 Select 方法
當找到資料來源控制項後 (如 SqlDataSource、ObjectDataSource ...等等),執行其 DataSourceView.Select 方法,此方法需入一個 DataSourceViewSelectCallback 函式當作參數,當資料來源控制項取得資料後回呼我們指定的 OnDataSourceViewSelectCallback 函式中做後序處理。
step3. 將取得的資料來設定生 Items 的清單內容
在 OnDataSourceViewSelectCallback 函式中接到回傳的具 IEnumerable 介面的資料,有可能是 DataView、DataTable ...等型別的資料。利用 DataBinder.GetPropertyValue 來取得 DataTextField 及 DataValueField 設定的欄位值,逐一建立 ListItem 項目,並加入 Items 集合屬性中。

        ''' <summary>
        ''' 從關聯的資料來源擷取資料。
        ''' </summary>
        Private Sub PerformSelect()
            Dim oControl As Control
            Dim oDataSource As IDataSource
            Dim oDataSourceView As DataSourceView

            '若未設定 DataSourceID 屬性則離開
            If StrIsEmpty(Me.DataSourceID) Then Exit Sub
            '找到具 IDataSource 介面的控制項
            oControl = FindControlEx(Me.Control.Page, GetType(IDataSource), "ID", Me.DataSourceID)
            If oControl Is Nothing Then Exit Sub

            oDataSource = DirectCast(oControl, IDataSource)
            oDataSourceView = oDataSource.GetView(String.Empty)
            oDataSourceView.Select(DataSourceSelectArguments.Empty, _
                        New DataSourceViewSelectCallback(AddressOf Me.OnDataSourceViewSelectCallback))
        End Sub

        ''' <summary>
        ''' 擷取資料的回呼函式。
        ''' </summary>
        ''' <param name="data">取得的資料。</param>
        Private Sub OnDataSourceViewSelectCallback(ByVal data As IEnumerable)
            Dim oCollection As ICollection
            Dim oValue As Object
            Dim oItem As ListItem

            Me.Items.Clear()
            If data Is Nothing Then Exit Sub

            oCollection = TryCast(data, ICollection)
            Me.Items.Capacity = oCollection.Count

            For Each oValue In data
                oItem = New ListItem()
                If StrIsNotEmpty(Me.DataTextField) Then
                    oItem.Text = DataBinder.GetPropertyValue(oValue, DataTextField, Nothing)
                End If
                If StrIsNotEmpty(Me.DataValueField) Then
                    oItem.Value = DataBinder.GetPropertyValue(oValue, DataValueField, Nothing)
                End If
                Me.Items.Add(oItem)
            Next
        End Sub

四、測試程式
使用上篇中同一個案例做測試,同樣以 Northwnd 資料庫的 Products 資料表為例。在 GridView 加入自訂的 TBDropDownField 欄位繫結 CategoryID 欄位,並設定 DataSourceID、DataTextField、DataValueField 屬性;另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 CategoryID 欄位來做比較。

                <bee:TBDropDownField  HeaderText="CategoryID"  
                    SortExpression="CategoryID" DataField="CategoryID" 
                    DataTextField="CategoryName" DataValueField="CategoryID" 

DataSourceID="SqlDataSource2">
                </bee:TBDropDownField>
                <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
                    SortExpression="CategoryID"  ReadOnly="true" />

執行程式,在 GridView 瀏覽的模式時,TBDropDownField 的儲存格已經會呈現 Items 對應成員的顯示文字。

執行資料列編輯時,也可以正常顯示下拉清單的內容。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx

]]>
jeff377 2008-10-25 18:11:28
[ASP.NET 控制項實作 Day24] TBDropDownField 的 Items 屬性的資料繫結 https://ithelp.ithome.com.tw/articles/10013041?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013041?sc=rss.iron 上篇中我們實作了 GridView 的 TBDropDownField 欄位類別,不過眼尖的讀者不知有沒有發覺我們並處理 Items 屬性取得成員清單的動作,而是直接設定儲存格內含的 TBDro...]]> 上篇中我們實作了 GridView 的 TBDropDownField 欄位類別,不過眼尖的讀者不知有沒有發覺我們並處理 Items 屬性取得成員清單的動作,而是直接設定儲存格內含的 TBDropDownList 控制項相關屬性 (DataSourceID、DataTextField、DataValueField 屬性) 後,就由 TDropDownList 控制項自行處理 Items 屬性的資料繫結。當 GridView 的資料列是編輯狀態時,下拉清單會顯示出 Items 的文字內容;可是瀏覽狀態的資料列,卻是顯示欄位原始值,無法呈現 Items 的文字內容。本文將說明如何自行處理 TBDropDownField 的 Items 屬性的資料繫結動作,並使唯讀狀態的資料列也可以呈現 Items 的文字內容。

程式碼下載:ASP.NET Server Control - Day24.rar
Northwnd 資料庫下載:NORTHWND.rar

一、Items 屬性的問題
我們重新看一次原本 TBDropDownField 類別在處理 Items 屬性的資料繫結取得清單內容的程式碼,在覆寫 InitializeDataCell 方法中,當儲存格為編輯模式時,會呈現 TBDropDownList 控制項並設定取得 Items 清單內容的相關屬性,讓 TBDropDownList 自行去處理它的 Items 屬性的清單內容。

'由資料來源控制項取得清單項目
oDropDownList.DataSourceID = Me.DataSourceID
oDropDownList.DataTextField = Me.DataTextField
oDropDownList.DataValueField = Me.DataValueField

不知你有沒有發覺,我們無論在 InitializeDataCell 及 OnDataBindField 方法中,都沒有針對 TBDropDownList 控制項做任何 DataBind 動作,那它是怎麼從 DataSourceID 關聯的資料來源擷取資料呢?因為 GridView 在執行 DataBind 時,就會要求所有的子控制項做 DataBind,所以我們只要設定好 BDropDownList 控制項相關屬性後,當 TBDropDownList 自動被要求資料繫結時就會取得 Items 的清單內容。
當然使用 TBDropDownList 控制項去處理 Items 的資料繫結動作最簡單,可是這樣唯讀的儲存格只能顯示原始欄位值,無法呈現 Items 中對應成員的文字;除非無論唯讀或編輯狀態,都要建立 TBDropDownList 控制項去取得 Items 清單內容,而唯讀欄位使用 TBDropDownList.Items 去找到對應成員的顯示文字,不過這樣的作法會怪怪的,而且沒有執行效能率。所以比較好的辨法,就是由 TBDropDownField 類別自行處理 Items 的資料繫結,同時提供給唯讀狀態的
DataControlFieldCell 及編輯狀態的 TBDropDownList 使用。

二、由 TBDropDownField 類別處理 Items 屬性的資料繫結
我們要自行處理 Items 屬性來取得成員清單,在 InitializeDataCell 方法中無須處理 Items 屬性,只需產生儲存格需要的子控制項,未來在執行子控制項的 DataBinding 時的 OnDataBindField 方法中再來處理 Items 屬性。

        Protected Overrides Sub InitializeDataCell( _
            ByVal Cell As DataControlFieldCell, _
            ByVal RowState As DataControlRowState)

            Dim oDropDownList As TBDropDownList
            Dim oControl As Control

            If Me.CellIsEdit(RowState) Then
                oDropDownList = New TBDropDownList()
                oControl = oDropDownList
                Cell.Controls.Add(oControl)
            Else
                oControl = Cell
            End If

            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then
                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)
            End If
        End Sub

在 OnDataBindField 方法中,我們加上一段處理 Items 屬性的程式碼如下,會利用 PerformSelecrt 私有方法,由關聯的資料來源 (即 DataSrouceID 指定的資料來源控制項) 擷取資料並產生 Items 的成員清單,在後面會詳細講解 PerformSelecrt 方法處理擷取資料的細節。因為 TBDropDownField 每個資料儲存格都會執行 OnDataBindField 方法,但 Items 取得成員清單的動作只需做一次即可,所以會以 FIsPerformSelect 區域變數來判斷是否已取得 Items 的成員清單,若已取過就不重新取得,這樣比較有執行效能。

            If Not Me.DesignMode Then
                If Not FIsPerformSelect Then
                    '從關聯的資料來源擷取資料
                    PerformSelect()
                    FIsPerformSelect = True
                End If
            End If

當取得儲存儲的對應的欄位值時,依此欄位值由 Items 集合去取得對應的 ListItem 成員,並以此 ListItem.Text 的文字內容來做顯示。

            '由 Items 去取得對應成員的顯示內容
            oListItem = Me.Items.FindByValue(CCStr(sText))
            If oListItem IsNot Nothing Then
                sText = oListItem.Text
            End If

若是由 TBDropDownList 所引發的 OnDataBindField 方法時,使用 SetItems 私有方法將 TBDropDownField.Items 屬性複製給 TBDropDownList.Item 屬性。

                ODropDownList = DirectCast(oControl, TBDropDownList)
                SetItems(ODropDownList)

SetItems 私有方法的程式碼如下。

        Private Sub SetItems(ByVal DropDownList As TBDropDownList)
            Dim oItems() As ListItem

            If Not Me.DesignMode Then
                ReDim oItems(Me.Items.Count - 1)
                Me.Items.CopyTo(oItems, 0)
                DropDownList.Items.AddRange(oItems)
            End If
        End Sub

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx

]]>
jeff377 2008-10-25 18:09:12
[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續3) https://ithelp.ithome.com.tw/articles/10012977?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012977?sc=rss.iron 接續上一文
四、測試程式
辛苦寫好 TBDropDownField 欄位類別時,接下來就是驗收成果的時候。我們以 Northwnd 資料...]]>
接續上一文
四、測試程式
辛苦寫好 TBDropDownField 欄位類別時,接下來就是驗收成果的時候。我們以 Northwnd 資料庫的 Products 資料表為例,將 TBDropDownList .DataField 設為 CategoryID 欄位來做測試。首先我們測試沒有 DataSoruceID 的情況,在 GridView 加入自訂的 TBDropDownField 欄位繫結 CategoryID 欄位,另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 CategoryID 欄位來做比較。

                <bee:TBDropDownField  HeaderText="CategoryID"  
                    SortExpression="CategoryID" DataField="CategoryID" >
                    <Items>
                    <asp:ListItem Value="">未對應</asp:ListItem>
                    <asp:ListItem Value="2">Condiments</asp:ListItem>
                    <asp:ListItem Value="3">Confections</asp:ListItem>
                    </Items>
                </bee:TBDropDownField>
                <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
                    SortExpression="CategoryID"  ReadOnly="true" />

執行程式,在 GridView 在唯讀模式,TBDropDownFIeld 可以正確的繫結 CategoryID 欄位值。

編輯某筆資料列進入編輯狀態,就會顯示 TBDropDownList 控制項,清單成員為我們在 Items 設定的內容。

使用 TBDropDownList 來做編輯欄位值,按下更新鈕,這時會執行 TBDropDownField.ExtractValuesFromCell 方法,取得儲存格中的值;最後由資料來源控制項將欄位值寫回資料庫。

接下來測試設定 TBDropDownField.DataSourceID 的情況,把 DataSourcID 指向含 Categories 資料表內容的 SqlDataSoruce 控制項。

                <bee:TBDropDownField  HeaderText="CategoryID"  
                    SortExpression="CategoryID" DataField="CategoryID" 
                    DataTextField="CategoryName" DataValueField="CategoryID" DataSourceID="SqlDataSource2">
                </bee:TBDropDownField>

執行程式查看結果,可以發現 TBDropDownList 控制項的清單內容也可以正常顯示 SqlDataSoruce 控制項取得資料。

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx

]]>
jeff377 2008-10-24 00:32:30
[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續2) https://ithelp.ithome.com.tw/articles/10012973?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012973?sc=rss.iron 接續上一文
step4. 處理資料繫結
當 GridView 控制項在執行資料繫結時,儲存格的控制項就會引發 DataBinding 事...]]>
接續上一文
step4. 處理資料繫結
當 GridView 控制項在執行資料繫結時,儲存格的控制項就會引發 DataBinding 事件,而這些事件會被導向 OnDataBindField 方法來統一處理儲存格中控制項的繫結動作。

       ''' <summary>
        ''' 將欄位值繫結至 BoundField 物件。 
        ''' </summary>
        ''' <param name="sender">控制項。</param>
        ''' <param name="e">事件引數。</param>
        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)
            Dim oControl As Control
            Dim ODropDownList As TBDropDownList
            Dim oNamingContainer As Control
            Dim oDataValue As Object            '欄位值
            Dim bEncode As Boolean              '是否編碼
            Dim sText As String                 '格式化字串

            oControl = DirectCast(sender, Control)
            oNamingContainer = oControl.NamingContainer
            oDataValue = Me.GetValue(oNamingContainer)
            bEncode = ((Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) AndAlso TypeOf oControl Is TableCell)
            sText = Me.FormatDataValue(oDataValue, bEncode)

            If TypeOf oControl Is TableCell Then
                If (sText.Length = 0) Then
                    sText = " "
                End If
                DirectCast(oControl, TableCell).Text = sText
            Else
                If Not TypeOf oControl Is TBDropDownList Then
                    Throw New HttpException(String.Format("{0}: Wrong Control Type", Me.DataField))
                End If

                ODropDownList = DirectCast(oControl, TBDropDownList)

                If Me.ApplyFormatInEditMode Then
                    ODropDownList.Text = sText
                ElseIf (Not oDataValue Is Nothing) Then
                    ODropDownList.Text = oDataValue.ToString
                End If
            End If
        End Sub

step5. 取得儲存格中的值
另外我們還需要覆寫 ExtractValuesFromCell 方法,取得儲存格中的值。這個方法是當 GridView 的編輯資料要準備寫入資料庫時,會經由 ExtractValuesFromCell 方法此來取得每個儲存格的值,並將這些欄位值加入 Dictionary 參數中,這個準備寫入的欄位值集合,可以在 DataSource 控制項的寫入資料庫的相關方法中取得使用。

        ''' <summary>
        ''' 使用指定 DataControlFieldCell 物件的值填入指定的 System.Collections.IDictionary 物件。 
        ''' </summary>
        ''' <param name="Dictionary">用於儲存指定儲存格的值。</param>
        ''' <param name="Cell">包含要擷取值的儲存格。</param>
        ''' <param name="RowState">資料列的狀態。</param>
        ''' <param name="IncludeReadOnly">true 表示包含唯讀欄位的值,否則為 false。</param>
        Public Overrides Sub ExtractValuesFromCell( _
            ByVal Dictionary As IOrderedDictionary, _
            ByVal Cell As DataControlFieldCell, _
            ByVal RowState As DataControlRowState, _
            ByVal IncludeReadOnly As Boolean)

            Dim oControl As Control = Nothing
            Dim sDataField As String = Me.DataField
            Dim oValue As Object = Nothing
            Dim sNullDisplayText As String = Me.NullDisplayText
            Dim oDropDownList As TBDropDownList

            If (((RowState And DataControlRowState.Insert) = DataControlRowState.Normal) OrElse Me.InsertVisible) Then
                If (Cell.Controls.Count > 0) Then
                    oControl = Cell.Controls.Item(0)
                    oDropDownList = TryCast(oControl, TBDropDownList)
                    If (Not oDropDownList Is Nothing) Then
                        oValue = oDropDownList.Text
                    End If
                ElseIf IncludeReadOnly Then
                    Dim s As String = Cell.Text
                    If (s = " ") Then
                        oValue = String.Empty
                    ElseIf (Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) Then
                        oValue = HttpUtility.HtmlDecode(s)
                    Else
                        oValue = s
                    End If
                End If

                If (Not oValue Is Nothing) Then
                    If TypeOf oValue Is String Then
                        If (CStr(oValue).Length = 0) AndAlso Me.ConvertEmptyStringToNull Then
                            oValue = Nothing
                        ElseIf (CStr(oValue) = sNullDisplayText) AndAlso (sNullDisplayText.Length > 0) Then
                            oValue = Nothing
                        End If
                    End If

                    If Dictionary.Contains(sDataField) Then
                        Dictionary.Item(sDataField) = oValue
                    Else
                        Dictionary.Add(sDataField, oValue)
                    End If
                End If
            End If
        End Sub

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx

]]>
jeff377 2008-10-24 00:31:32
[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續1) https://ithelp.ithome.com.tw/articles/10012971?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012971?sc=rss.iron 接續上一文
step2. 加入 TBBaseBoundField 的屬性
TBBaseBoundField 類別會內含 DropDown...]]>
接續上一文
step2. 加入 TBBaseBoundField 的屬性
TBBaseBoundField 類別會內含 DropDownList 控制項,所以加入設定 DropDownList 控制項的對應屬性;我們在 TBBaseBoundField 類別加入了 Items 、DataSourceID、DataTextField、DataValueField 屬性。其中 Items 屬性的型別與 DropDownList.Items 屬性相同,都是 ListItemCollection 集合類別,且 Items 屬性會儲存於 ViewState 中。

        ''' <summary>
        ''' 清單項目集合。
        ''' </summary>
        < _
        Description("清單項目集合。"), _
        DefaultValue(CStr(Nothing)), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
        Editor(GetType(ListItemsCollectionEditor), GetType(UITypeEditor)), _
        MergableProperty(False), _
        Category("Default")> _
        Public Overridable ReadOnly Property Items() As ListItemCollection
            Get
                If (FItems Is Nothing) Then
                    FItems = New ListItemCollection()
                    If MyBase.IsTrackingViewState Then
                        CType(FItems, IStateManager).TrackViewState()
                    End If
                End If
                Return FItems
            End Get
        End Property

        ''' <summary>
        ''' 資料來源控制項的 ID 屬性。
        ''' </summary>
        Public Property DataSourceID() As String
            Get
                Return FDataSourceID
            End Get
            Set(ByVal value As String)
                FDataSourceID = value
            End Set
        End Property

        ''' <summary>
        ''' 提供清單項目文字內容的資料來源的欄位。
        ''' </summary>
        < _
        Description("提供清單項目文字內容的資料來源的欄位。"), _
        DefaultValue("") _
        > _
        Public Property DataTextField() As String
            Get
                Return FDataTextField
            End Get
            Set(ByVal value As String)
                FDataTextField = value
            End Set
        End Property

        ''' <summary>
        ''' 提供清單項目值的資料來源的欄位。
        ''' </summary>
        Public Property DataValueField() As String
            Get
                Return FDataValueField
            End Get
            Set(ByVal value As String)
                FDataValueField = value
            End Set
        End Property

step3.建立儲存格內含的控制項
GridView 是以儲存格 (DataControlFieldCell) 為單位,我們要覆寫 InitializeDataCell 方法來建立儲存格中的控制項;當儲存格為可編輯狀態時,就建立 DropDownList 控制項並加入儲存格中,在此使用上篇文章提及的 TBDropDownList 控制項來取代,以解決清單成員不存在造成錯誤的問題。若未設定 DataSourceID 屬性時,則由 Items 屬性取得自訂的清單項目;若有設定 DataSourceID 屬性,則由資料來源控制項 (如 SqlDataSource、ObjectDataSource 控制項) 來取得清單項目。
當建立儲存格中的控制項後,需要以 AddHeadler 的方法,將此控制項的 DataBinding 事件導向 OnDataBindField 這個事件處理方法,我們要在 OnDataBindField 處理資料繫結的動作。

        ''' <summary>
        ''' 資料儲存格初始化。
        ''' </summary>
        ''' <param name="Cell">要初始化的儲存格。</param>
        ''' <param name="RowState">資料列狀態。</param>
        Protected Overrides Sub InitializeDataCell( _
            ByVal Cell As DataControlFieldCell, _
            ByVal RowState As DataControlRowState)

            Dim oDropDownList As TBDropDownList
            Dim oItems() As ListItem
            Dim oControl As Control

            If Me.CellIsEdit(RowState) Then
                oDropDownList = New TBDropDownList()
                oControl = oDropDownList
                Cell.Controls.Add(oControl)

                If Not Me.DesignMode Then
                    If StrIsEmpty(Me.DataSourceID) Then
                        '自訂清單項目
                        ReDim oItems(Me.Items.Count - 1)
                        Me.Items.CopyTo(oItems, 0)
                        oDropDownList.Items.AddRange(oItems)
                    Else
                        '由資料來源控制項取得清單項目
                        oDropDownList.DataSourceID = Me.DataSourceID
                        oDropDownList.DataTextField = Me.DataTextField
                        oDropDownList.DataValueField = Me.DataValueField
                    End If
                End If
            Else
                oControl = Cell
            End If

            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then
                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)
            End If

        End Sub

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx

]]>
jeff377 2008-10-24 00:25:02
[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位 https://ithelp.ithome.com.tw/articles/10012965?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012965?sc=rss.iron GridView 是 ASP.NET 中一個相當常用的控制項,在 GridView 可加入 BoundField、CheckBoxField、CommandField、TemplateField...]]> GridView 是 ASP.NET 中一個相當常用的控制項,在 GridView 可加入 BoundField、CheckBoxField、CommandField、TemplateField ... 等不同型別的欄位,可是偏偏沒有提供在 GridView 中可呈現 DropDownList 的欄位型別;遇到這類需求時,一般的作法都是使用 TemplateField 來處理。雖然 TemplateField 具有相當好的設計彈性。可是在當 GridView 需要動態產生欄位的需求時,TemplateField 就相當麻煩,要寫一堆程式碼自行去處理資料繫結的動作。相互比較起來,BoundField、CheckBoxField ...等這類事先定義類型的欄位,在 GridView 要動態產生這些欄位就相當方便。如果我們可以把一些常用的 GridView 的欄位,都做成類似 BoundField 一樣,只要設定欄位的屬性就好,這樣使用上就會方便許多,所以在本文將以實作 DropDownList 欄位為例,讓大家了解如何去自訂 GridView 的欄位類別。
程式碼下載:ASP.NET Server Control - Day23.rar
Northwnd 資料庫下載:NORTHWND.rar

一、選擇合適的父類別
一般自訂 GridView 的欄位類別時,大都是由 DataControlField 或 BoundField 繼承下來改寫。若是欄位不需繫結資料(如 CommandFIeld),可以由 DataControlFIeld 繼承下來,若是欄位需要做資料繫結時(如 CheckBoxFIld,可以直接由 BoundField 繼承下來改寫比較方便。
DataControlField 類別是所有類型欄位的基底類別,BoundField 類別也是由 DataControlField 類別繼承下來擴展了資料繫結部分的功能,所以我們要實作含 DropDownList 的欄位,也是由 BoundField 繼承下來改寫。

二、自訂欄位基底類別
在此我們不直接繼承 BoundFIeld,而是先撰寫一個繼承 BoundField 命名為 TBBaseBoundField 的基底類別,此類別提供一些通用的屬性及方法,使我們更方便去撰寫自訂的欄位類別。

    ''' <summary>
    ''' 資料欄位基礎類別。
    ''' </summary>
    Public MustInherit Class TBBaseBoundField
        Inherits BoundField

        Private FRowIndex As Integer = 0

        ''' <summary>
        ''' 資料列是否為編輯模式。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Public Function RowStateIsEdit(ByVal RowState As DataControlRowState) As Boolean
            Return (RowState And DataControlRowState.Edit) <> DataControlRowState.Normal
        End Function

        ''' <summary>
        ''' 資料列是否為新增模式。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Public Function RowStateIsInsert(ByVal RowState As DataControlRowState) As Boolean
            Return (RowState And DataControlRowState.Insert) <> DataControlRowState.Normal
        End Function

        ''' <summary>
        ''' 資料列是否為編輯或新增模式。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Public Function RowStateIsEditOrInsert(ByVal RowState As DataControlRowState) As Boolean
            Return RowStateIsEdit(RowState) OrElse RowStateIsInsert(RowState)
        End Function

        ''' <summary>
        ''' 判斷儲存格是否可編輯(新增/修改)。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Friend Function CellIsEdit(ByVal RowState As DataControlRowState) As Boolean
            Return (Not Me.ReadOnly) AndAlso RowStateIsEditOrInsert(RowState)
        End Function

        ''' <summary>
        ''' 資料列索引。
        ''' </summary>
        Friend ReadOnly Property RowIndex() As Integer
            Get
                Return FRowIndex
            End Get
        End Property

        ''' <summary>
        ''' 儲存格初始化。
        ''' </summary>
        ''' <param name="Cell">要初始化的儲存格。</param>
        ''' <param name="CellType">儲存格類型。</param>
        ''' <param name="RowState">資料列狀態。</param>
        ''' <param name="RowIndex">資料列之以零起始的索引。</param>
        Public Overrides Sub InitializeCell(ByVal Cell As DataControlFieldCell, ByVal CellType As DataControlCellType, _
            ByVal RowState As DataControlRowState, ByVal RowIndex As Integer)

            FRowIndex = RowIndex
            MyBase.InitializeCell(Cell, CellType, RowState, RowIndex)
        End Sub

        ''' <summary>
        ''' 是否需要執行資料繫結。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Friend Function RequiresDataBinding(ByVal RowState As DataControlRowState) As Boolean
            If MyBase.Visible AndAlso StrIsNotEmpty(MyBase.DataField) AndAlso RowStateIsEdit(RowState) Then
                Return True
            Else
                Return False
            End If
        End Function
    End Class

三、實作 TBDropDownField 欄位類別
step1. 繼承 TBBaseBoundField 類別
首先新增一個類別,繼承 TBBaseBoundField 命名為 TBDropDownFIeld 類別,覆寫 CreateField 方法,傳回 TBDropDownFIeld 物件。

    Public Class TBDropDownField
        Inherits TBBaseBoundField

        Protected Overrides Function CreateField() As System.Web.UI.WebControls.DataControlField
            Return New TBDropDownField()
        End Function
    End Class

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx

]]>
jeff377 2008-10-24 00:19:12
[ASP.NET 控制項實作 Day22] 讓 DropDownList 不再因項目清單不存在而造成錯誤(續) https://ithelp.ithome.com.tw/articles/10012915?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012915?sc=rss.iron 接續上篇文章內容
三、解決 TBDropDownList 設定 DataSourceID 造成資料無法繫結的問題
要解決上述 TBDro...]]>
接續上篇文章內容
三、解決 TBDropDownList 設定 DataSourceID 造成資料無法繫結的問題
要解決上述 TBDropDownList 設定 DataSourceID 問題,需在設定 SelectedValue 屬性時,若 Items.Count=0 先用一個 FCachedSelectedValue 變數將正確的值先暫存下來,然後覆寫 PerformDataBinding 方法,當 DorpDownList 取得 DataSoruceID 所對應的項目清單內容後,因為這時 Items 的內容才會完整取回,再去設定一次 SelectedValue 屬性就可以正確的繫結資料。

    Public Class TBDropDownList
        Inherits DropDownList

        Private FCachedSelectedValue As String

        ''' <summary>
        ''' 覆寫 SelectedValue 屬性。
        ''' </summary>
        Public Overrides Property SelectedValue() As String
            Get
                Return MyBase.SelectedValue
            End Get
            Set(ByVal value As String)
                If Me.Items.Count <> 0 Then
                    Dim oItem As ListItem = Me.Items.FindByValue(value)
                    If (oItem Is Nothing) Then
                        Me.SelectedIndex = -1 '當 Items 不存在時 
                    Else
                        MyBase.SelectedValue = value
                    End If
                Else
                    FCachedSelectedValue = value
                End If
            End Set
        End Property

        Protected Overrides Sub PerformDataBinding(ByVal data As System.Collections.IEnumerable)
            MyBase.PerformDataBinding(data)

            'DataSoruceID 資料繫結後再設定 SelectedValue 屬性值
            If (Not FCachedSelectedValue Is Nothing) Then
                Me.SelectedValue = FCachedSelectedValue
            End If
        End Sub

    End Class

重新執行程式,切換到編輯模式時,TBDropDownList 就可以正確的繫結欄位值了。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx

]]>
jeff377 2008-10-23 07:03:54
[ASP.NET 控制項實作 Day22] 讓 DropDownList 不再因項目清單不存在而造成錯誤 https://ithelp.ithome.com.tw/articles/10012909?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012909?sc=rss.iron DropDownList 控制項常常會因為項目清單中不存在繫結的欄位,而發生以下的錯誤訊息。因為繫結資料的不完整或異常就會造成這樣的異常錯誤,在設計上實在是相當困擾,而且最麻煩的是這個錯誤在頁面...]]> DropDownList 控制項常常會因為項目清單中不存在繫結的欄位,而發生以下的錯誤訊息。因為繫結資料的不完整或異常就會造成這樣的異常錯誤,在設計上實在是相當困擾,而且最麻煩的是這個錯誤在頁面的程式碼也無法使用 Try ... Catch 方式來略過錯誤。其實最簡單的方式就去直接去修改 DropDownList 控制項,讓 DropDownList 控制項繫結資料時,就算欄位值不存在清單項目中也不要釋出錯誤,本文就要說明如何繼承 DorpDownList 下來修改,來有效解決這個問題。

程式碼下載:ASP.NET Server Control - Day22.rar
Northwnd 資料庫下載:NORTHWND.rar

一、覆寫 SelectedValue 屬性解決資料繫結的問題
DropDownList 控制項繫結錯誤的原因,可以由上圖的錯誤訊息可以大概得知是寫入 SelectedValue 屬性時發生的錯誤;所以我們繼承 DorpDownList 下來命名為 TBDropDownList,並覆寫 SelectedValue 屬性來解決這個問題。解決方式是在寫入 SelectedValue 屬性時,先判斷準備寫入的值是否存在項目清單中,存在的話才寫入 SelectedValue 屬性,若不存在則直接設定 SelectedIndex 屬性為 -1。

    Public Class TBDropDownList
        Inherits DropDownList

        ''' <summary>
        ''' 覆寫 SelectedValue 屬性。
        ''' </summary>
        Public Overrides Property SelectedValue() As String
            Get
                Return MyBase.SelectedValue
            End Get
            Set(ByVal value As String)
                Dim oItem As ListItem = Me.Items.FindByValue(value)
                If (oItem Is Nothing) Then
                    Me.SelectedIndex = -1 '當 Items 不存在時 
                    Exit Property
                Else
                    MyBase.SelectedValue = value
                End If
            End Set
        End Property

    End Class

我們以 Northwnd 資料庫的 Products 資料表做為測試資料,事先定義 DropDownList 的 Items 內容,其中第一個加入 "未對應" 的項目,將 SelectedValue 屬性繫結至 CategoryID 欄位。

                <bee:TBDropDownList ID="DropDownList1" runat="server" 
                    SelectedValue='<%# Bind("CategoryID") %>'>
                    <asp:ListItem Value="">未對應</asp:ListItem>
                    <asp:ListItem Value="2">Condiments</asp:ListItem>
                    <asp:ListItem Value="3">Confections</asp:ListItem>
                </bee:TBDropDownList>

當資料的 CategoryID 欄位值不存在於 DropDownList 的 Items 集合屬性中時,就會顯示第一個 "未對應" 的項目。

二、TBDropDownList 設定 DataSoruceID 產生的問題
上述的解決方法在筆者的「讓 DropDownList DataBind 不再發生錯誤」一文中已經有提及,不過有讀者發現另一個問題,就是當 DropDownList 設定 DataSourceID 時卻會發生資料無法正常繫結,以下就來解決這個問題。
我們設定 TBDropDownList 的 DataSoruceID 來取得項目清單的內容,將 DataSourceID 設定為另一個取得 Categories 資料表內容的 SqlDataSource 控制項。

                <bee:TBDropDownList ID="DropDownList1" runat="server" 
                    SelectedValue='<%# Bind("CategoryID") %>' DataSourceID="SqlDataSource2" 
                    DataTextField="CategoryName" DataValueField="CategoryID">
                </bee:TBDropDownList>
                <asp:SqlDataSource ID="SqlDataSource2" runat="server" 
                    ConnectionString="<%$ ConnectionStrings:Northwnd %>" 
                    SelectCommand="SELECT CategoryID, CategoryName, Description, Picture FROM Categories" 
                    ProviderName="<%$ ConnectionStrings:Northwnd.ProviderName %>" >
                </asp:SqlDataSource>

當執行程式時,FormView 原本在瀏覽模式時的 CategoryID 欄位值為 7 (CategoryName 應為 Product)。

當按下「編輯」時切換到 EditItemTemplate 時,改用 TBDropDownList 繫結 CategoryID 欄位值,可以這時欲無法繫結正確的值。

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx

]]>
jeff377 2008-10-23 06:59:56
[ASP.NET 控制項實作 Day21] 實作控制項智慧標籤(續) https://ithelp.ithome.com.tw/articles/10012897?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012897?sc=rss.iron 接續 [ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 一文
step2. 在智慧標籤面板加入屬性項目
DesignerA...]]>
接續 [ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 一文
step2. 在智慧標籤面板加入屬性項目
DesignerActionPropertyItem 類別是設定智慧標籤面上的屬性項目,DesignerActionPropertyItem 建構函式的第一個參數(memberName) 為屬性名稱,這個屬性指的是 TBDateEditActionList 類別中的屬性,所以要在 TBDateEditActionList 新增一個對應的屬性。
例如在智慧標籤中加入 AutoPostBack 屬性項目,則在 TBDateEditActionList 類別需有一個對應 AutoPostBack 屬性。

            oItems.Add(New DesignerActionPropertyItem("AutoPostBack", _
                "AutoPostBack", "Behavior", "是否引發 PostBack 動作。"))

TBDateEditActionList.AutoPostBack 屬性如下,其中 Me.Component 指的是目前的 TDateEdit 控制項,透過 GetPropertyValue 及 SetPropertyValue 方法來存取控制項的指定屬性。

        ''' <summary>
        ''' 是否引發 PostBack 動作。
        ''' </summary>
        Public Property AutoPostBack() As Boolean
            Get
                Return CType(GetPropertyValue(Me.Component, "AutoPostBack"), Boolean)
            End Get
            Set(ByVal value As Boolean)
                SetPropertyValue(Me.Component, "AutoPostBack", value)
            End Set
        End Property

    ''' <summary>
    ''' 設定物件的屬性值。
    ''' </summary>
    ''' <param name="Component">屬性值將要設定的物件。</param>
    ''' <param name="PropertyName">屬性名稱。</param>
    ''' <param name="Value">新值。</param>
    Public Shared Sub SetPropertyValue(ByVal Component As Object, ByVal PropertyName As String, ByVal Value As Object)
        Dim Prop As PropertyDescriptor = TypeDescriptor.GetProperties(Component).Item(PropertyName)
        Prop.SetValue(Component, Value)
    End Sub

    ''' <summary>
    ''' 取得物件的屬性值。
    ''' </summary>
    ''' <param name="Component">具有要擷取屬性的物件。</param>
    ''' <param name="PropertyName">屬性名稱。</param>
    Public Shared Function GetPropertyValue(ByVal Component As Object, ByVal PropertyName As String) As Object
        Dim Prop As PropertyDescriptor = TypeDescriptor.GetProperties(Component).Item(PropertyName)
        Return Prop.GetValue(Component)
    End Function

step3. 在智慧標籤面板加入方法項目
DesignerActionMethodItem 類別是設定智慧標籤面上的方法項目,DesignerActionPropertyItem 建構函式的第二個參數(memberName) 為方法名稱,這個方法指的是 TBDateEditActionList 類別中的方法,所以要在 TBDateEditActionList 新增一個對應的方法。
例如在智慧標籤中加入 About 方法項目,則在 TBDateEditActionList 類別需有一個對應 About 方法。

            oItems.Add(New DesignerActionMethodItem(Me, "About", _
                "關於 TDateEdit 控制項", "About", _
                "關於 TDateEdit 控制項。", True))

TBDateEditActionList 的 About 方法只是單純顯示一個訊息視窗,一般你可以在這方法加入任何想在設計階段處理的動作。例如自動產生 GridView 的欄位、在 FormView 加入控制項並自動排版,這些都可以在此實現的。

        Public Sub About()
            MsgBox("TDateEdit 是結合 The Coolest DHTML Calendar 日期選擇器實作的控制項")
        End Sub

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx

]]>
jeff377 2008-10-22 18:02:28
[ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 https://ithelp.ithome.com.tw/articles/10012896?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012896?sc=rss.iron 控制項通常會把常用屬性或功能顯示在智慧標籤中,提供使用者更簡便的快速設定,例如下圖為 GridView 的智慧。若要製作控制項的智慧標籤,需實作控制項的 ActionList 加入智慧標籤中要顯...]]> 控制項通常會把常用屬性或功能顯示在智慧標籤中,提供使用者更簡便的快速設定,例如下圖為 GridView 的智慧。若要製作控制項的智慧標籤,需實作控制項的 ActionList 加入智慧標籤中要顯示的項目,在本文將以 TDateEdit 控制項為例,進一步說明控制項的智慧標籤的實作方式。

程式碼下載:ASP.NET Server Control - Day21.rar

一、TDateEdit 控制項介紹
TDateEdit 控制項是筆者之前在部落格中實作的一個日期控制項,如下圖所示。它是結合 JavaScript 的 The Coolest DHTML Calendar 日期選擇器實作的控制項,我已將 TDateEdit 控制項的相關程式碼含入 Bee.Web.dll 組件中。TDateEdit 控制項的相關細節可以參考筆者部落格下面幾篇文章有進一步說明,本文將以 TDateEdit 控制項為例,只針對實作智慧標籤的部分做進一步說明。
日期控制項實作教學(1) - 結合 JavaScript
日期控制項實作教學(2) - PostBack 與 事件
TBDateEdit 日期控制項 - 1.0.0.0 版 (Open Source)

二、控制項加入智慧標籤
控制項要加入智慧標籤要實作控制項的 Designer,我們繼承 ControlDesigner 命名為 TBDateEditDesigner,然後覆寫 ActionLists 屬性,此屬性即是傳回智慧標籤中所包含的項目清單集合。在 ActionLists 屬性中一般會先加入父類別的 ActionLists 屬性,再加入自訂的 ActionList 類別,這樣才可以保留原父類別中智慧標籤的項目清單。

    ''' <summary>
    ''' TBDateEdit 控制項的設計模式行為。
    ''' </summary>
    Public Class TBDateEditDesigner
        Inherits System.Web.UI.Design.ControlDesigner

        ''' <summary>
        ''' 取得控制項設計工具的動作清單集合。
        ''' </summary>
        Public Overrides ReadOnly Property ActionLists() As DesignerActionListCollection
            Get
                Dim oActionLists As New DesignerActionListCollection()
                oActionLists.AddRange(MyBase.ActionLists)
                oActionLists.Add(New TBDateEditActionList(Me))
                Return oActionLists
            End Get
        End Property

    End Class

我們自訂的 ActionList 為 TBDateEditActionList 類別,它在智慧標籤呈現的項目清單如下圖所示,接下去我們會說明 TBDateEditActionList 類別的內容。

三、自訂智慧標籤面板的項目清單集合
DesignerActionList 類別定義用於建立智慧標籤面板的項目清單的基底類別,所以我們首先繼承 DesignerActionList 命名為 TBDateEditActionList。

    ''' <summary>
    ''' 定義 TBDateEdit 控制項智慧標籤面板的項目清單集合。
    ''' </summary>
    Public Class TBDateEditActionList
        Inherits DesignerActionList

        ''' <summary>
        ''' 建構函式。
        ''' </summary>
        Public Sub New(ByVal owner As ControlDesigner)
            MyBase.New(owner.Component)
        End Sub

    End Class

接下來要覆寫 GetSortedActionItems 方法,它會回傳 DesignerActionItemCollection 集合型別,此集合中會傳回要顯示在智慧標籤面板的項目清單集合,所以我們要在 DesignerActionItemCollection 集合中加入我們要呈現的項目清單內容。

        ''' <summary>
        ''' 傳回要顯示在智慧標籤面板的項目清單集合。
        ''' </summary>
        Public Overrides Function GetSortedActionItems() As System.ComponentModel.Design.DesignerActionItemCollection
            Dim oItems As New DesignerActionItemCollection()

            '在此加入智慧標籤面板的項目清單	           

            Return oItems
        End Function

step1. 在智慧標籤面板加入靜態標題項目
首先介紹 DesignerActionHeaderItem 類別,它是設定靜態標題項目,例如我們在 TDateEdit 的智慧標籤中加入「行為」、「外觀」二個標題項目,其中 DesignerActionHeaderItem 建構函式的 category 參數是群組名稱,我們可以將相關的項目歸類到同一個群組。

Dim oItems As New DesignerActionItemCollection()

oItems.Add(New DesignerActionHeaderItem("行為", "Behavior"))
oItems.Add(New DesignerActionHeaderItem("外觀", "Appearance"))

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx

]]>
jeff377 2008-10-22 18:01:29
[ASP.NET 控制項實作 Day20] 偵錯設計階段的程式碼 https://ithelp.ithome.com.tw/articles/10012807?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012807?sc=rss.iron 上篇我們介紹了自訂 Designer 來輸出控制項設計階段的 HTML 碼,可是若你去對針 Designer 的程式碼下中斷點,你會發覺根本無法偵錯。因為程式在執行階段時期,根本不會執行 Des...]]> 上篇我們介紹了自訂 Designer 來輸出控制項設計階段的 HTML 碼,可是若你去對針 Designer 的程式碼下中斷點,你會發覺根本無法偵錯。因為程式在執行階段時期,根本不會執行 Designer 相關類別,所以你在 Designer 類別中下的中斷點完全無效;當然不可能這樣寫程式碼而用感覺去偵錯,本文將告訴你如何去偵錯設計階段的程式碼。
一、設計階段程式碼的錯誤
如果撰寫 Designer、Editor、ActionList 等設計階段的程式碼,當這些設計階段的程式碼發生錯誤,可能會發生設計頁面中控制項的錯誤情形,如下圖所示。因為控制項專案本身非啟動專案,在測試網站的設計頁面若控制項發生異常時會直接釋出錯誤,無法偵錯設計階段的程式碼;若真得要偵錯誤設計階段的問題,就要使用另一個 VS2008 來偵錯。

二、設定起始外部程式
要偵錯控制項設計階段的程式碼,要先將控制項專案(Bee.Web)設定為啟時專案。然後設定控制項專案的「屬性」,在「偵錯」頁籤中的起始動作選擇「起始外部程式」,選擇 VS2008 的執行檔位置,預設為 C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe。

三、開始偵錯設計階段程式碼
step1. 控制項專案開始偵錯
在設計階要偵錯的程式碼下中斷點,在控制項專案按下 F5 開始偵錯,這時會啟動另一個新的 VS2008 執行檔。

step2. 在新的 VS2008 的工具箱加入控制項
在新的 VS2008 中新增一個測試網站,在工具箱按右鍵執行「選擇項目」開啟「選擇工具箱項目」視窗,然後按「瀏覽」鈕按選擇控制項組件(Bee.Web.dll),將要偵錯的控制項加入工具箱中。


step3. 將控制項拖曳至頁面做設計動作
在新的 VS2008 中,將控制項拖曳至頁面,就會開始執行設計階段的程式碼,特定的設計動作就會執行到相對的設計階段程式碼,當執行到之前下的中斷點時就可以開始偵錯了。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/21/5741.aspx

]]>
jeff377 2008-10-21 00:28:45
[ASP.NET 控制項實作 Day19] 控制項設計階段的外觀 https://ithelp.ithome.com.tw/articles/10012682?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012682?sc=rss.iron 有一些控制項在執行階段是不會呈現,也就是說控制項本身在執行階段不會 Render 出 HTML 碼,例如 SqlDataSoruce、ScriptManager 這類控制項;那它們在設計階段的頁...]]> 有一些控制項在執行階段是不會呈現,也就是說控制項本身在執行階段不會 Render 出 HTML 碼,例如 SqlDataSoruce、ScriptManager 這類控制項;那它們在設計階段的頁面是如何呈現出來呢?本文將針對控制項設計階段的外觀做進一步的說明。
程式碼下載:ASP.NET Server Control - Day19.rar
一、控制項設計階段的 HTML 碼
Web 伺服器控制項的設計模式行為都是透過 ControlDesigner 來處理,連設計階段時控制項的外觀也是如此;控制項在設計階段與執行執行時呈現的外觀不一定相同,當然大部分會儘量一致,使其能所見即所得。
控制項在設計階段的 HTML 碼是透 ControlDesigner.GetDesignTimeHtml 方法來處理,在 ControlDesigner.GetDesignTimeHtml 預設會執行控制項的 RenderControl 方法,所以大部分的情況下設計階段與執行階段輸出的 HTML 碼會相同。當控制項的 Visible=False 時,執行階段是完全不會輸出 HTML 碼,可是在設計階段時會特別將控制項設定 Visible=True,使控制項能完整呈現。

ControlDesigner.GetDesignTimeHtml 方法

Public Overridable Function GetDesignTimeHtml() As String
    Dim writer As New StringWriter(CultureInfo.InvariantCulture)
    Dim writer2 As New DesignTimeHtmlTextWriter(writer)
    Dim errorDesignTimeHtml As String = Nothing
    Dim flag As Boolean = False
    Dim visible As Boolean = True
    Dim viewControl As Control = Nothing
    Try 
        viewControl = Me.ViewControl
        visible = viewControl.Visible
        If Not visible Then
            viewControl.Visible = True
            flag = Not Me.UsePreviewControl
        End If
        viewControl.RenderControl(writer2)
        errorDesignTimeHtml = writer.ToString
    Catch exception As Exception
        errorDesignTimeHtml = Me.GetErrorDesignTimeHtml(exception)
    Finally
        If flag Then
            viewControl.Visible = visible
        End If
    End Try
    If ((Not errorDesignTimeHtml Is Nothing) AndAlso (errorDesignTimeHtml.Length <> 0)) Then
        Return errorDesignTimeHtml
    End If
    Return Me.GetEmptyDesignTimeHtml
End Function

二、自訂控制項的 Designer
以 TBToolbar 為例,若我們在 RenderContents 方法未針對 Items.Count=0 做輸出 HTML 的處理,會發現未設定 Items 屬性時,在設計頁面上完全看不到 TBToolbar 控制項;像這種控制項設計階段的 HTML 碼,就可以自訂控制項的 Designer 來處理。

繼承 ControlDesigner 命名為 TBToolbarDesigner,這個類別是用來擴充 TBToolbar 控制項的設計模式行為。我們可以覆寫 GetDesignTimeHtml 方法,處理設計階段表示控制項的 HTML 標記,此方法回傳的 HTML 原始碼就是控制項呈現在設計頁面的外觀。所以我們可以在 TBToolbar.Items.Count=0 時,輸出一段提示的 HTML 碼,這樣當 TBToolbar 未設定 Items 屬性時一樣可以在設計頁面上呈現控制項。

    ''' <summary>
    ''' 擴充 TBToolbar 控制項的設計模式行為。
    ''' </summary>
    Public Class TBToolbarDesigner
        Inherits System.Web.UI.Design.ControlDesigner

        ''' <summary>
        ''' 用來在設計階段表示控制項的 HTML 標記。
        ''' </summary>
        Public Overrides Function GetDesignTimeHtml() As String
            Dim sHTML As String
            Dim oControl As TBToolbar

            oControl = CType(ViewControl, TBToolbar)
            If oControl.Items.Count = 0 Then
                sHTML = "<div style=""background-color: #C0C0C0; border:solid 1px; width:200px"">請設定 Items 屬性</div>"
            Else
                sHTML = MyBase.GetDesignTimeHtml()
            End If
            Return sHTML
        End Function

    End Class

在 TBToolbar 控制項套用 DesignerAttribute 設定自訂的 TBToolbarDesigner 類別。

    <Designer(GetType(TBToolbarDesigner))> _
    Public Class TBToolbar
        Inherits WebControl

    End Class

重建控制項組件,切換到設計頁面上的看 TBToolbar 控制項未設定 Items 屬性時的外觀,就是我們在 TBToolbarDesigner.GetDesignTimeHtml 方法回傳的 HTML 碼。

如果你覺得上述設計階段的控制項有點太陽春,我們也可以輸出類似 SqlDataSource 控制項的外觀,將未設定 Items 屬性時輸出 HTML 改呼叫 CreatePlaceHolderDesignTimeHtml 方法。

            If oControl.Items.Count = 0 Then
                sHTML = MyBase.CreatePlaceHolderDesignTimeHtml("請設定 Items 屬性")
            Else
                sHTML = MyBase.GetDesignTimeHtml()
            End If

來看一下這樣修改後的結果,是不是比較專業一點了呢。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/20/5726.aspx

]]>
jeff377 2008-10-20 02:25:29
[ASP.NET 控制項實作 Day18] 修改集合屬性編輯器 https://ithelp.ithome.com.tw/articles/10012636?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012636?sc=rss.iron 上篇我們實作了「集合屬性包含不同型別的成員」,不過若有去使用屬性視窗編輯 TBToolbar 的 Items 屬性,你會發覺這個集合屬性編輯器無法加入我們定義不同型別的成員,只能加入最原始的集合...]]> 上篇我們實作了「集合屬性包含不同型別的成員」,不過若有去使用屬性視窗編輯 TBToolbar 的 Items 屬性,你會發覺這個集合屬性編輯器無法加入我們定義不同型別的成員,只能加入最原始的集合成員。是不是只能在 aspx 程式碼中手動去輸入呢?當然不需要這樣人工作業,只要改掉集合屬性編輯器就可以達到我們的需求,本文將介紹修改集合屬性編輯器的相關作法。
程式碼下載:ASP.NET Server Control - Day18.rar

一、自訂集合屬性編輯器
我們先看一下 TBToolbar.Items 屬性套用的 EditorAttribute,它是使用 CollectionEditor 類別來當作屬性編輯器,所以我們就是要繼承 CollectionEditor 類別下來修改成自訂的屬性編輯器。

< _
Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _
> _
Public ReadOnly Property Items() As TBToolbarItemCollection

新增一個繼承 CollectionEditor 的 TBToolbarItemCollectionEditor 類別,並加入建構函式。此類別屬於 Bee.WebControls.Design 命名空間,通常我們會把設計階段使用的類別歸類到特別的命名空間便於管理及使用。

Namespace WebControls.Design
    Public Class TBToolbarItemCollectionEditor
        Inherits CollectionEditor

        ''' <summary>
        ''' 建構函式。
        ''' </summary>
        ''' <param name="Type">型別。</param>
        Public Sub New(ByVal Type As Type)
            MyBase.New(Type)
        End Sub

    End Class
End Namespace

我們可以先修改 Items 屬性的 EditorAttribute,看看我們自訂的 TBToolbarItemCollectionEditor 是否能正常運作。不過這個屬性編輯器跟原本的沒什麼差異,因為我們只是單純繼承下來沒做任何異動,接下去我們就要開始來修改這個屬性編輯器。

< _
Editor(GetType(TBToolbarItemCollectionEditor), GetType(UITypeEditor)) _
> _
Public ReadOnly Property Items() As TBToolbarItemCollection

二、加入不同型別的集合成員
再來我們就要著手修改集合屬性編輯器,讓它可以加入不同型別的集合成員。覆寫 CollectionEditor 的 CanSelectMultipleInstances 方法傳回 True,這個方法是設定 CollectionEditor 是否允許加入多種不同型別的集合成員。

        Protected Overrides Function CanSelectMultipleInstances() As Boolean
            Return True
        End Function

再來覆寫 CreateNewItemTypes 方法,這個方法是取得這個集合編輯器可包含的資料型別,將集合可包含的資料型別以陣列傳回。

        ''' <summary>
        ''' 取得這個集合編輯器可包含的資料型別。
        ''' </summary>
        ''' <returns>這個集合可包含的資料型別陣列。</returns>
        Protected Overrides Function CreateNewItemTypes() As System.Type()
            Dim ItemTypes(2) As System.Type
            ItemTypes(0) = GetType(TBToolbarButton)
            ItemTypes(1) = GetType(TBToolbarTextbox)
            ItemTypes(2) = GetType(TBToolbarLabel)
            Return ItemTypes
        End Function

重建控制項組件,使用 Items 的集合屬性編輯器,就可以發現「加入」鈕的下拉清單就會出現我們所定義的三種型別的集合成員,如此可以加入不同型別的成員了。

三、設定清單項目的顯示文字
在成員清單項目中預設會顯示成員含命名空間的型別,若我們要修改成比較有識別的顯示文字,例如 TBToolbarButton(Key=Add) 可以顯示「按鈕-Add」,這時可以覆寫 GetDisplayText 方法來設定清單項目的顯示文字。

        ''' <summary>
        ''' 取出指定清單項目的顯示文字。
        ''' </summary>
        Protected Overrides Function GetDisplayText(ByVal value As Object) As String
            If TypeOf value Is TBToolbarButton Then
                Return String.Format("按鈕 - {0}", CType(value, TBToolbarButton).Key)
            ElseIf TypeOf value Is TBToolbarTextbox Then
                Return "文字框"
            ElseIf TypeOf value Is TBToolbarLabel Then
                Return String.Format("標籤 - {0}", CType(value, TBToolbarLabel).Text)
            Else
                Return value.GetType.Name
            End If
        End Function

四、集合編輯器的屬性視窗的屬性描述
一般屬性視窗下面都會有屬性描述,可以集合屬性編輯器中的屬性視窗下面竟沒有屬性描述。若我們要讓它的屬性描述可以顯示,可以覆寫 CreateCollectionForm 方法,取得集合屬性編輯表單,再去設定表單上的 PropertyGrid.HelpVisible
= True 即可。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/19/5721.aspx

]]>
jeff377 2008-10-19 00:13:21
[ASP.NET 控制項實作 Day17] 集合屬性包含不同型別的成員 https://ithelp.ithome.com.tw/articles/10012600?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012600?sc=rss.iron 我們知道在 GridView 的 Columns 集合屬性中,可以包含不同型別的欄位,如 BoundFIeld、CheckBoxField、HyperLinkField ...等不同型別的欄位。...]]> 我們知道在 GridView 的 Columns 集合屬性中,可以包含不同型別的欄位,如 BoundFIeld、CheckBoxField、HyperLinkField ...等不同型別的欄位。如果我們希望工具列中不只包含按鈕,可以包含其他不同類型的子控制項,那該怎麼做呢?本文就以上篇中的 TBToolbar 控制項為案例,讓 Items 集合屬性可以加入 Button、TextBox、Label ...等不同的子控制項。
程式碼下載:ASP.NET Server Control - Day17.rar
一、不同型別的集合成員
我們的需求是讓工具列可以加入 Button、TextBox、Label 三種子控制項,所以繼承原來的 TBToolbarItem (只保留 Enabled 屬性),新增了 TBToolbarButton、TBToolbarTextbox、TBToolbarLabel 三個類別。

這些新增的成員類別都是繼承至 TBToolbarItem,所以在 aspx 程式碼中,手動輸入 Items 的成員時,就會列出這幾種定義的成員型別。

二、建立不同型別集合成員的子控制項
因為 Items 屬性的成員具不同型別,所以我們要改寫 RenderContents 方法,判斷成員型別來建立對應類型的子控制項。若為 TBToolbarButton 型別建立 Button 控制項、若為 TBToolbarTextbox 型別則建立 TextBox 控制項、若為 TBToolbarLabel 型別則建立 Label 控制項。其中 TBToolbarButton 建立的控制項為 TBButton,這個控制項是我們在「 [ASP.NET 控制項實作 Day3] 擴展現有伺服器控制項功能」一文中實作的具詢問訊息的按鈕控制項。

        ''' <summary>
        ''' 覆寫 RenderContents 方法。
        ''' </summary>
        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
            Dim oItem As TBToolbarItem
            Dim oControl As Control

            For Each oItem In Me.Items
                If TypeOf oItem Is TBToolbarButton Then
                    '建立 Button 控制項
                    oControl = CreateToolbarButton(CType(oItem, TBToolbarButton))
                ElseIf TypeOf oItem Is TBToolbarTextbox Then
                    '建立 Textbox 控制項
                    oControl = CreateToolbarTextbox(CType(oItem, TBToolbarTextbox))
                Else
                    '建立 Label 控制項
                    oControl = CreateToolbarLabel(CType(oItem, TBToolbarLabel))
                End If
                Me.Controls.Add(oControl)
            Next

            MyBase.RenderContents(writer)
        End Sub

        ''' <summary>
        ''' 建立工具列按鈕。
        ''' </summary>
        Private Function CreateToolbarButton(ByVal Item As TBToolbarButton) As Control
            Dim oButton As TBButton
            Dim sScript As String

            oButton = New TBButton()
            oButton.Text = Item.Text
            oButton.Enabled = Item.Enabled
            oButton.ID = Item.Key
            oButton.ConfirmMessage = Item.ConfirmMessage
            sScript = Me.Page.ClientScript.GetPostBackEventReference(Me, Item.Key)
            oButton.OnClientClick = sScript

            Return oButton
        End Function

        ''' <summary>
        ''' 建立工具列文字框。
        ''' </summary>
        Private Function CreateToolbarTextbox(ByVal Item As TBToolbarTextbox) As Control
            Dim oTextBox As TextBox

            oTextBox = New TextBox
            Return oTextBox
        End Function

        ''' <summary>
        ''' 建立工具列標籤。
        ''' </summary>
        Private Function CreateToolbarLabel(ByVal Item As TBToolbarLabel) As Control
            Dim oLabel As Label

            oLabel = New Label()
            oLabel.Text = Item.Text
            Return oLabel
        End Function

我們手動在 aspx 程式碼中輸入不同型別的成員,TBToolbar 控制項就會呈現對應的子控制項。

三、執行程式
執行程式,就可以在瀏覽器看到呈現的工具列,當按下「刪除」時也會出現我們定義的詢問訊息。

輸出的 HTML 碼如下

<span id="TBToolbar1">
<input type="submit" name="TBToolbar1$Add" value="新增" onclick="__doPostBack('TBToolbar1','Add');" id="TBToolbar1_Add" />
<input type="submit" name="TBToolbar1$Edit" value="修改" onclick="__doPostBack('TBToolbar1','Edit');" id="TBToolbar1_Edit" />
<input type="submit" name="TBToolbar1$Delete" value="刪除" onclick="if (confirm('確定刪除嗎?')==false) {return false;}__doPostBack('TBToolbar1','Delete');" id="TBToolbar1_Delete" />
<span>關鍵字</span>
<input name="TBToolbar1$ctl01" type="text" />
<input type="submit" name="TBToolbar1$Search" value="搜尋" onclick="__doPostBack('TBToolbar1','Search');" id="TBToolbar1_Search" />
</span>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/18/5718.aspx

]]>
jeff377 2008-10-18 00:05:57
[ASP.NET 控制項實作 Day16] 繼承 WebControl 實作 Toolbar 控制項 https://ithelp.ithome.com.tw/articles/10012507?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012507?sc=rss.iron 前面我們討論過「繼承 CompositeControl 實作 Toolbar 控制項」,本文將繼承 WebControl 來實作同樣功能的 Toolbar 控制項,用不同的方式來實作同一個控制項...]]> 前面我們討論過「繼承 CompositeControl 實作 Toolbar 控制項」,本文將繼承 WebControl 來實作同樣功能的 Toolbar 控制項,用不同的方式來實作同一個控制項,進而比較二者之間的差異。
程式碼下載:ASP.NET Server Control - Day16.rar

一、繼承 WebControl 實作 TBToolbar 控制項
step1. 新增繼承 WebControl 的 TBToolbar 控制項
新增繼承 WebControl 的 TBToolbar 控制項,你也可以直接原修改原 TBToolbar 控制項,繼承對象由 CompositeControl 更改為 WebControl即可。跟之前一樣在 TBToolbar 控制項加入 Items 屬性及 Click 事件。
另外 TBToolbar 控制項需實作 INamingContainer 界面,此界面很特殊沒有任何屬性或方法,INamingContainer 界面的作用是子控制項的 ClientID 會在前面加上父控制項的 ClickID,使每個子控制項有唯一的 ClientID。

step2. 建立工具列按鈕集合
覆寫 RenderContents 方法,將原本 TBToolbar (複合控制項) 的 CreateChildControls 方法中建立工具列按鈕程式碼,搬移至 RenderContents 方法即可。

        Private Sub ButtonClickEventHandler(ByVal sender As Object, ByVal e As EventArgs)
            Dim oButton As Button
            Dim oEventArgs As ClickEventArgs

            oButton = CType(sender, Button)
            oEventArgs = New ClickEventArgs()
            oEventArgs.Key = oButton.ID
            OnClick(oEventArgs)
        End Sub

        ''' <summary>
        ''' 覆寫 RenderContents 方法。
        ''' </summary>
        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
            Dim oItem As TBToolbarItem
            Dim oButton As Button

            For Each oItem In Me.Items
                oButton = New Button()
                oButton.Text = oItem.Text
                oButton.Enabled = oItem.Enabled
                oButton.ID = oItem.Key
                AddHandler oButton.Click, AddressOf ButtonClickEventHandler
                Me.Controls.Add(oButton)
            Next

            If Me.Items.Count = 0 AndAlso Me.DesignMode Then
                oButton = New Button()
                oButton.Text = "請設定 Items 屬性。"
                Me.Controls.Add(oButton)
            End If

            MyBase.RenderContents(writer)
        End Sub

上述的直接搬移過來的程式碼還有個問題,就是原來的使用 AddHandler 來處理按鈕事件的方式變成沒有作用了?因為現在不是複合式控制項,當前端的按鈕 PostBack 傳回伺服端時,TBToolbar 不會事先建立子控制槓,所以機制會找不到原來產生的按鈕,也就無法使用 AddHandler 來處理事件了。

AddHandler oButton.Click, AddressOf ButtonClickEventHandler

step3. 處理 Click 事件
因為不能使用 AddHandler 來處理按鈕事件,所以我們就自行使用 Page.ClientScript.GetPostBackEventReference 方法來產生 PostBack 動作的用戶端指令碼,按鈕的 OnClientClick 去執行 PostBack 的動作。

            For Each oItem In Me.Items
                oButton = New Button()
                oButton.Text = oItem.Text
                oButton.Enabled = oItem.Enabled
                oButton.ID = oItem.Key
                sScript = Me.Page.ClientScript.GetPostBackEventReference(Me, oItem.Key)
                oButton.OnClientClick = sScript
                Me.Controls.Add(oButton)
            Next

TBToolar 控制項輸出的 HTML 碼如下

<span id="TBToolbar1">
<input type="submit" name="TBToolbar1$Add" value="新增" onclick="__doPostBack('TBToolbar1','Add');" 

id="TBToolbar1_Add" />
<input type="submit" name="TBToolbar1$Edit" value="修改" onclick="__doPostBack('TBToolbar1','Edit');" 

id="TBToolbar1_Edit" />
<input type="submit" name="TBToolbar1$Delete" value="刪除" onclick="__doPostBack('TBToolbar1','Delete');" 

id="TBToolbar1_Delete" />
</span>

要自行處理 PostBack 的事件,需實作 IPostBackEventHandler 介面,在 RaisePostBackEvent 方法來引發 TBToolbar 的 Click 事件。

    Public Class TBToolbar
        Inherits WebControl
        Implements INamingContainer
        Implements IPostBackEventHandler

        Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements 

System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
            Dim oEventArgs As ClickEventArgs

            oEventArgs = New ClickEventArgs()
            oEventArgs.Key = eventArgument
            Me.OnClick(oEventArgs)
        End Sub

    End Class

二、測試程式
在測試頁面上放置 TBToolbar 控制項,在 Click 事件撰寫測試程式碼。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/17/5706.aspx

]]>
jeff377 2008-10-17 00:05:40
[ASP.NET 控制項實作 Day15] 複合控制項隱藏的問題 https://ithelp.ithome.com.tw/articles/10012425?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012425?sc=rss.iron 上一篇我們使用複合控制項(繼承 CompositeControl)的方式來實作 TBToolbar 控制項,本文將針對複合控制項做一些測試,說明在使用複合控制項要注意的一些問題。
程...]]>
上一篇我們使用複合控制項(繼承 CompositeControl)的方式來實作 TBToolbar 控制項,本文將針對複合控制項做一些測試,說明在使用複合控制項要注意的一些問題。
程式碼下載:ASP.NET Server Control - Day15.rar
一、複合控制項建立子控制項的時機
還記得我們之前介紹複合控制項時有談到 CompositeControl 類別會確保我們存取子控制項時,它的子控制項一定會事先建立;也就是當我們使用 Controls 屬性去存取子控制項時,一定會執行 CreateChildControls 方法,以確保子控制項事先被建立。我們看一下 CompositeControl 類別的 Controls 屬性的寫法就可以了解其中的原由,在存取 CompositeControl.Controls 屬性時,它會先執行 Control.EnsureChildControls 方法;而 EnsureChildControls 方法會去判斷子控制項是否已建立,若未建立會去執行 CreateChildControls 方法,這也就是為什麼 CompositeControl 有辨法確保子控制項事先被建立的原因。

CompositeControl.Controls 屬性如下

Public Overrides ReadOnly Property Controls As ControlCollection
    Get
        Me.EnsureChildControls
        Return MyBase.Controls
    End Get
End Property

Control.EnsureChildControls 方法如下

Protected Overridable Sub EnsureChildControls()
    If (Not Me.ChildControlsCreated AndAlso Not Me.flags.Item(&H100)) Then
        Me.flags.Set(&H100)
        Try 
            Me.ResolveAdapter
            If (Not Me._adapter Is Nothing) Then
                Me._adapter.CreateChildControls
            Else
                Me.CreateChildControls
            End If
            Me.ChildControlsCreated = True
        Finally
            Me.flags.Clear(&H100)
        End Try
    End If
End Sub

二、複合控制項隱藏的問題
我們以上篇的 TBToolbar 控制項為例,撰寫一些測試案例來說明複合控制項的問題。在撰寫測試案例之前,我們先修改一下 TBToolbar 控制項,覆寫 LoadViewState 及 SaveViewState 方法,將 Items 屬性儲存於 ViewState 中以維持狀態。

在測試頁面上放置「測試一」、「測試二」、「PostBack」三個按鈕,這三個按鈕的動作如下。
「測試一」按鈕:在工具列直接新增一個按鈕。
「測試二」按鈕:先使用 FindControl 取得工具列的按鈕,然後在在工具列再新增一個按鈕。
「PostBack」按鈕:單純執行 PostBack,不撰寫程式碼。

三個按鈕的程式碼如下所示。

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles 

Button1.Click
        Dim oItem As TBToolbarItem

        '加入新按鈕
        oItem = New TBToolbarItem()
        oItem.Text = "新按鈕"
        oItem.Key = "NewButton"
        TBToolbar1.Items.Add(oItem)
        Me.Response.Write("「測試一」按鈕")
    End Sub

    Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles 

Button2.Click
        Dim oItem As TBToolbarItem
        Dim oButton As Button

        '先執行 FindControl 去取得 ID="Add" 的按鈕
        oButton = TBToolbar1.FindControl("Add")

        '再加入新按鈕
        oItem = New TBToolbarItem()
        oItem.Text = "新按鈕"
        oItem.Key = "NewButton"
        TBToolbar1.Items.Add(oItem)
        Me.Response.Write("「測試二」按鈕")
    End Sub

    Protected Sub Button3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles 

Button3.Click
        '單純 PostBack,無程式碼
        Me.Response.Write("「PostBack」按鈕")
    End Sub

案例一:執行「測試一」按鈕,在工具列直接新增一個按鈕。
當按下「測試一」按鈕時,工具列可以正常加入我們新增的按鈕。

案例二:執行「測試二」按鈕,先使用 FindControl 取得工具列的按鈕,然後在在工具列再新增一個按鈕。
重新執行程式,當按下「測試二」按鈕時,你會發現奇怪的現象,工具列竟然沒有加入我們新增的按鈕?

此時再按下「PostBack」按鈕,工具列才會出現我們剛剛加入的按鈕。

為什麼會發生這種怪現象呢?其實原因很簡單,因為 FindControl 時會去存取 Controls 屬性,而這時子控制項已經被建立了;而之前再用 Items 屬性加入新按鈕,它已經不會在重建子控制項,導致第一時間沒有加入新按鈕。不過 Items 屬性會被存在 ViewState 中,所以當執行「PostBack」按鈕時,就會出現我們剛剛新增的按鈕。

三、解決方式
要解決上述「測試二」的問題,只要覆寫 TBToolbar 控制項的 Render 方法,在 Render 前執行 RecreateChildControls 方法,強制重建子控制項。

        ''' <summary>
        ''' 覆寫 Render 方法。
        ''' </summary>
        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            Me.RecreateChildControls()
            MyBase.Render(writer)
        End Sub

再一次執行「測試二」的動作,就會發現執行結果就會正常了。

四、結語
在複合控制項的 Render 前執行 RecreateChildControls 方法可以強制重建子控制項,可是這樣又會引發另一個問題,那就是當直接存取子控制項去修改子控制項的屬性後,一旦在 Render 又重建子控制項,那之前設定子控制項狀態又被全部重建了,所以需特別注意有這樣的情形。另外複合控制項有可能重覆執行建立子控制的動作,在執行效能上也比較不佳。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/16/5695.aspx

]]>
jeff377 2008-10-16 00:14:12
[ASP.NET 控制項實作 Day14] 繼承 CompositeControl 實作 Toolbar 控制項 https://ithelp.ithome.com.tw/articles/10012339?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012339?sc=rss.iron 之前我們簡單介紹過繼承 CompositeControl 來實作複合控制項,在本文我們將以 Toolbar 控制項為例,以複合控制項的作法(繼承 CompositeControl )來實作 To...]]> 之前我們簡單介紹過繼承 CompositeControl 來實作複合控制項,在本文我們將以 Toolbar 控制項為例,以複合控制項的作法(繼承 CompositeControl )來實作 Toolbar 控制項,此工具列控制項包含 Items 屬性來描述工具列項目集合,依 Items 屬性的設定來建立工具列按鈕,另外包含 Click 事件可以得知使用按了那個按鈕。
程式碼下載:ASP.NET Server Control - Day14.rar
一、工具列項目集合類別
工具列包含多個按鈕,新增 TBToolbarItem 類別來描述工具列項目,TBToolbarItem 類別包含 Key、Text、Enabled 三個屬性;而 TBToolbarItemCollection 為 TBToolbarItem 的集合類別來描述工具列按鈕集合。

二、實作 TBToolbar 控制項
step1. 新增繼承 CompositeControl 的 TBToolbar 控制項

    < _
    Description("工具列控制項。"), _
    ParseChildren(True, "Items"), _
    ToolboxData("<{0}:TBToolbar runat=server ></{0}:TBToolbar>") _
    > _
    Public Class TBToolbar
        Inherits CompositeControl
    End Class 

step2. 新增 Items 屬性,描述工具列項目集合

        ''' <summary>
        ''' 工具列項目集合。
        ''' </summary>
        < _
        Description("工具列項目集合。"), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
        Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _
        > _
        Public ReadOnly Property Items() As TBToolbarItemCollection
            Get
                If FItems Is Nothing Then
                    FItems = New TBToolbarItemCollection()
                End If
                Return FItems
            End Get
        End Property

step3. 新增 Click 事件
TBToolbar 類別新增 Click 事件,當按下按鈕時會引發 Click 事件,由 Click 的事件引數 e.Key 可以得知使用者按了那個按鈕。

        ''' <summary>
        ''' Click 事件引數。
        ''' </summary>
        Public Class ClickEventArgs
            Inherits System.EventArgs
            Private FKey As String = String.Empty

            ''' <summary>
            ''' 項目鍵值。
            ''' </summary>
            Public Property Key() As String
                Get
                    Return FKey
                End Get
                Set(ByVal value As String)
                    FKey = value
                End Set
            End Property
        End Class

        ''' <summary>
        ''' 按下工具列按鈕所引發的事件。
        ''' </summary>
        < _
        Description("按下工具列按鈕所引發的事件。") _
        > _
        Public Event Click(ByVal sender As Object, ByVal e As ClickEventArgs)

        ''' <summary>
        ''' 引發 Click 事件。
        ''' </summary>
        Protected Overridable Sub OnClick(ByVal e As ClickEventArgs)
            RaiseEvent Click(Me, e)
        End Sub

step4. 建立工具列按鈕集合
覆寫 CreateChildControls 方法,依 Items 屬性的設定,來建立工具列中的按鈕集合。每個按鈕的 Click 事件都導向 ButtonClickEventHandler 方法,來處理所有按鈕的 Click 動作,並引發 TBToolbar 的 Click 事件。

        Private Sub ButtonClickEventHandler(ByVal sender As Object, ByVal e As EventArgs)
            Dim oButton As Button
            Dim oEventArgs As ClickEventArgs

            oButton = CType(sender, Button)
            oEventArgs = New ClickEventArgs()
            oEventArgs.Key = oButton.ID
            OnClick(oEventArgs)
        End Sub

        ''' <summary>
        ''' 建立子控制項。
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            Dim oItem As TBToolbarItem
            Dim oButton As Button

            For Each oItem In Me.Items
                oButton = New Button()
                oButton.Text = oItem.Text
                oButton.Enabled = oItem.Enabled
                oButton.ID = oItem.Key
                AddHandler oButton.Click, AddressOf ButtonClickEventHandler
                Me.Controls.Add(oButton)
            Next
            MyBase.CreateChildControls()
        End Sub

三、測試程式
在頁面拖曳 TBToolbar 控制項,並設定 Items 屬性,如入新增、修改、刪除三個按鈕。

在 TBToolbar 控制項的 Click 事件加入測試程式碼,輸出引發 Click 事件的 e.Key。

    Protected Sub TBToolbar1_Click(ByVal sender As Object, ByVal e As Bee.Web.WebControls.TBToolbar.ClickEventArgs) Handles TBToolbar1.Click
        Me.Response.Write(String.Format("您按了 {0}", e.Key))
    End Sub

執行程式,當按了工具列上的按鈕時,就會引發 Click 事件,並輸出該按鈕對應的 Key。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/15/5687.aspx

]]>
jeff377 2008-10-15 00:13:50
[ASP.NET 控制項實作 Day13] Flash 控制項 https://ithelp.ithome.com.tw/articles/10012267?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012267?sc=rss.iron Flash 也是網頁常用的 ActiveX 插件,在本文中將繼承 TBActiveX 下來撰寫 TBFlash 控制項,用來輸出網頁套用 Flash 的相關 HTML 碼。
程式碼下...]]>
Flash 也是網頁常用的 ActiveX 插件,在本文中將繼承 TBActiveX 下來撰寫 TBFlash 控制項,用來輸出網頁套用 Flash 的相關 HTML 碼。
程式碼下載:ASP.NET Server Control - Day13.rar

一、網頁 Flash 的原始 HTML 碼
我們先觀查在網頁中套用 Flash 插件的原始 HTML 碼,以點部落首頁抬頭的 Flash 原始碼為例如下,其中 <object> tag 的 codebase attribute 是指 Flash 插件的下載位置及版本。

<object id="ShockwaveFlash2" height="90" width="728" 
  codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" 
  classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
<param value="http://files.dotblogs.com.tw/dotjum/ad/debug.swf" name="movie"/>
<param value="high" name="quality"/>
<param value="#000000" name="bgcolor"/>
<embed height="90" width="728" type="application/x-shockwave-flash" 
  pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" 
  src="http://files.dotblogs.com.tw/dotjum/ad/debug.swf"/>
</object>

在 <object> tag 中必要的 attribute 為 classid、codebase、movie、width、height,而 <embed> tag 的必要 attribute 為 src、pluginspage、width、height,其他選擇性的 attribute 可參閱以下網頁。

Flash OBJECT and EMBED tag attributes
http://kb.adobe.com/selfservice/viewContent.do?externalId=tn\_12701

二、實作 TFlash 控制項
了解 Flash 的原始 HTML 碼後,我們就可以開始著手撰寫 TBFlash 控制項,想辨法來輸出所需要的 HTML 碼。

step1. 新增 TBFlash 控制項繼承至 TBActiveX
我們先在 TBActiveX 控制項新增一個 CodeBase 屬性,用來設定 ActiveX 插入的下載位置及版本,然後新增 TBFlash 控制項繼承至 TBActiveX,並在建構函式中設定 MyBase.ClassId 及 MyBase.CodeBase 屬性。

    Public Class TBFlash
        Inherits TBActiveX

        ''' <summary>
        ''' 建構函式。
        ''' </summary>
        Sub New()
            MyBase.ClassId = "D27CDB6E-AE6D-11CF-96B8-444553540000"
            MyBase.CodeBase = "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0"
        End Sub
    End Class 

step2. 加入相關屬性
在 TBFlash 加入 MovieUrl 及 Quality 屬性,MovieUrl 為 Flash 檔案來源,Quality 為影音品質。

step3. 輸出 Flash 相關參數
覆寫 CreateChildControls 方法,輸出 MovieUrl 及 Quality 屬性對應的參數,以及在 Params 集合屬性設定的參數。

        ''' <summary>
        ''' 加入 MediaPlayer 參數。
        ''' </summary>
        ''' <param name="Name">參數名稱。</param>
        ''' <param name="Value">參數值。</param>
        Private Sub AddParam(ByVal Name As String, ByVal Value As String)
            Dim oParam As TBActiveXParam

            oParam = New TBActiveXParam(Name, Value)
            Me.Params.Add(oParam)
        End Sub

        ''' <summary>
        ''' 建立 Embed 標記。
        ''' </summary>
        Private Function CreateEmbed() As HtmlControls.HtmlGenericControl
            Dim oEmbed As HtmlControls.HtmlGenericControl
            Dim oParam As TBActiveXParam

            oEmbed = New HtmlControls.HtmlGenericControl()
            oEmbed.TagName = "embed"
            oEmbed.Attributes("src") = Me.ResolveClientUrl(Me.MovieUrl)
            oEmbed.Attributes("pluginspage") = "http://www.macromedia.com/go/getflashplayer"
            oEmbed.Attributes("height") = Me.Height.ToString
            oEmbed.Attributes("width") = Me.Width.ToString

            'Embed 的 Attributes 加入 Params 集合屬性的設定
            For Each oParam In Me.Params
                If oParam.Name <> "movie" Then
                    oEmbed.Attributes(oParam.Name) = oParam.Value
                End If
            Next
            Return oEmbed
        End Function

        ''' <summary>
        ''' 建立子控制項。
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            Dim oEmbed As HtmlControls.HtmlGenericControl

            '加入 movie 參數
            AddParam("movie", Me.ResolveClientUrl(Me.MovieUrl))

            '加入 quality 參數
            If Me.Quality <> EQuality.NotSet Then
                AddParam("quality", Me.Quality.ToString.ToLower)
            End If

            MyBase.CreateChildControls()

            oEmbed = CreateEmbed()
            Me.Controls.Add(oEmbed)
        End Sub

三、測試程式
在頁面拖曳 TBFlash 控制項,設定 MovieUrl 及 Quality 屬性,若有需要加入其他參數,可自行設定 Params 集合屬性。執行程式就可以在頁面上看到呈現出來的 Flash。

        <bee:TBFlash ID="TBFlash1" runat="server" Height="90px" 
            MovieUrl="http://files.dotblogs.com.tw/dotjum/ad/debug.swf" Quality="High" 
            Width="728px">
        </bee:TBFlash>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/14/5674.aspx

]]>
jeff377 2008-10-14 00:16:30
[ASP.NET 控制項實作 Day12] 繼承 TBActiveX 重新改寫 TBMediaPlayer 控制項 https://ithelp.ithome.com.tw/articles/10012196?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012196?sc=rss.iron 上篇介紹的 TBActiveX 控制項,它可以支援網頁 Media Player 的設定,這跟前面提及的 TBMediaPlayer 功能相同。TBActiveX 具有網頁設定 ActiveX ...]]> 上篇介紹的 TBActiveX 控制項,它可以支援網頁 Media Player 的設定,這跟前面提及的 TBMediaPlayer 功能相同。TBActiveX 具有網頁設定 ActiveX 通用屬性,所以 TBMediaPlayer 基本上是可以由 TBActiveX 繼承下來,再加入 Media Player 特有的屬性即可。本文將原來的 TBMediaPlayer 控制項,繼承的父類別由 WebControl 改為 TBActiveX 類別,重新改寫 TBMediaPlayer 控制項。
程式碼下載:ASP.NET Server Control - Day12.rar

一、改寫 TBMediaPlayer 控制項
TBMediaPlayer 控制項原本是繼承 WebControl,現改繼承對象為 TBActiveX,來重新改寫 TBMediaPlayer 控制項。

step1. TBMediaPlayer 繼承至 TBActiveX
新增 TBMediaPlayer 控制項,繼承至 TBActiveX,並在建構函式設定 Media Player ActiveX 的 ClassId。

    Public Class TBMediaPlayer
        Inherits TBActiveX

        ''' <summary>
        ''' 建構函式。
        ''' </summary>
        Sub New()
            MyBase.ClassId = "6BF52A52-394A-11D3-B153-00C04F79FAA6"
        End Sub
    End Class

step2. 加入相關屬性
跟原來的 TBMediaPlayer 控制項一樣,加入 Url、AutoStart、UIMode 三個屬性,可視情形加入需要設定的屬性。

step3. 加入 Media Player 參數
覆寫 CreateChildControls 方法,動態依屬性設定在 Params 集合屬性加入參數。雖然 TBMediaPlayer 控制項目前只有 Url、AutoStart、UIMode 三個屬性,但是父類別 TBActiveX 具有 Params 集合屬性,所以開發人員可以視需求加入其他未定義的參數。

        ''' <summary>
        ''' 加入 MediaPlayer 參數。
        ''' </summary>
        ''' <param name="Name">參數名稱。</param>
        ''' <param name="Value">參數值。</param>
        Private Sub AddParam(ByVal Name As String, ByVal Value As String)
            Dim oParam As TBActiveXParam

            oParam = New TBActiveXParam(Name, Value)
            Me.Params.Add(oParam)
        End Sub

        ''' <summary>
        ''' 覆寫 CreateChildControls 方法。
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            '加入 Url 參數
            If Me.Url <> String.Empty Then
                AddParam("URL", Me.ResolveClientUrl(Me.Url))
            End If
            '加入 autoStart 參數
            If Me.AutoStart Then
                AddParam("autoStart", "true")
            End If
            '加入 uiMode 參數
            If Me.UIMode <> EUIMode.NotSet Then
                AddParam("uiMode", Me.UIMode.ToString)
            End If
            MyBase.CreateChildControls()
        End Sub

二、執行程式
在頁面拖曳 TBMediaPlayer 控制項,設定 Url、AutoStart、UIMode 屬性,若有需要加入其他參數,可自行設定 Params 集合屬性。執行程式就可以在頁面上看到呈現出來的 Media Player。

        <bee:TBMediaPlayer ID="TBMediaPlayer1" runat="server" AutoStart="True" 
            Height="249px" Url="D:\Movie_01.wmv" Width="250px">
        </bee:TBMediaPlayer>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/13/5663.aspx

]]>
jeff377 2008-10-13 00:13:29
[ASP.NET 控制項實作 Day11] ActiveX 伺服器控制項 https://ithelp.ithome.com.tw/articles/10012159?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012159?sc=rss.iron Media Player 與 Flash 之類在網頁上執行的外掛控制項,都是屬於 ActiveX 控制項,它們套用在 HTML 碼中的方式差不多,除了要指定 ClassID 以外,ActiveX...]]> Media Player 與 Flash 之類在網頁上執行的外掛控制項,都是屬於 ActiveX 控制項,它們套用在 HTML 碼中的方式差不多,除了要指定 ClassID 以外,ActiveX 使用的參數(相當於 ActiveX 控制項的屬性)以 Param Tag 來表示。本文標題命名為「ActiveX 伺服器控制項」就是避免誤解為 ActiveX 控制項,而是在 ASP.NET 中輸出 ActiveX 相關 HTML 碼的伺服器控制項;我們可透過 ActiveX 伺服器控制項可以用來輸出網頁上引用 ActiveX 的通用 HTML 碼,另外 ActiveX 的參數會以集合屬性來呈現,所以也會一併學習到集合屬性的撰寫方式。
程式碼下載:ASP.NET Server Control - Day11.rar

一、集合屬性
ActiveX 的 Param 參數是集合屬性,所以我們定義了 TBActiveParam 類別描述 ActiveX 參數,包含 Name 及 Value 屬性;而 TBActiveXParamCollection 為 TBActiveParam 的集合類別,用來描述 ActiveX 參數集合。TBActiveXParamCollection 繼承 CollectionBase,加入操作集合的 Add、Insert、Remove、IndexOf、Contains 等方法,關於集合屬性的用法可以參閱筆者在部落格的「撰寫伺服器控制項的集合屬性 (CollectionBase)」一文中有詳細說明。

二、實作 ActiveX 伺服器控制項
step1. 新增繼承 WebControl 的 TBActiveX

step2. 覆寫 TagKey 屬性,傳回 object 的 Tag

        Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag
            Get
                Return HtmlTextWriterTag.Object
            End Get
        End Property

step3. 新增 ClassId 屬性,描述 ActiveX 的 ClassId
定義 ClassId 屬性,並覆寫 AddAttributesToRender 來輸出此屬性。

        ''' <summary>
        ''' 覆寫 AddAttributesToRender 方法。
        ''' </summary>
        Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)
            '加入 MediaPlayer ActiveX 元件的 classid
            writer.AddAttribute("classid", String.Format("clsid:{0}", Me.ClassId))
            MyBase.AddAttributesToRender(writer)
        End Sub

step4. 新增 Params 屬性,描述 ActiveX 的參數集合
定義 Params 屬性,型別為 TBActiveXParamCollection 類別,套用 EditorAttribute 設定 CollectionEditor 為集合編輯器。

        ''' <summary>
        ''' ActiveX 控制項參數集合。
        ''' </summary>
        < _
        Description("控制項參數集合。"), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
        Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _
        > _
        Public ReadOnly Property Params() As TBActiveXParamCollection
            Get
                If FParams Is Nothing Then
                    FParams = New TBActiveXParamCollection()
                End If
                Return FParams
            End Get
        End Property

當編輯 Params 時,會使用的 CollectionEditor 集合編輯器。

step5. 輸出 ActiveX 參數
覆寫 CreateChildControls 方法,在此方法依 Params 集合屬性定義依序來輸出 ActiveX 的參數集合。

        Private Sub AddParam(ByVal Name As String, ByVal Value As String)
            Dim oParam As HtmlControls.HtmlGenericControl

            oParam = New HtmlControls.HtmlGenericControl("param")
            oParam.Attributes.Add("name", Name)
            oParam.Attributes.Add("value", Value)
            Me.Controls.Add(oParam)
        End Sub

        ''' <summary>
        ''' 建立子控制項。
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            Dim oParam As TBActiveXParam

            '加入 ActiveX 參數集合
            For Each oParam In Me.Params
                AddParam(oParam.Name, oParam.Value)
            Next
            MyBase.CreateChildControls()
        End Sub

三、執行程式
上一篇我們使用 TBMediaPlayer 控制項來設定 Media Player,在此我們改用 TBActiveX 控制項來設定 Media Player,一樣可以呈現相同的結果。

        <bee:TBActiveX ID="TBActiveX1" runat="server" 
            ClassId="6BF52A52-394A-11D3-B153-00C04F79FAA6" Height="250px" Width="250px">
            <Params>
                <bee:TBActiveXParam Name="URL" Value="d:/Movie_01.wmv" />
                <bee:TBActiveXParam Name="autoStart" Value="true" />
            </Params>
        </bee:TBActiveX>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/12/5659.aspx

]]>
jeff377 2008-10-12 04:20:27
[ASP.NET 控制項實作 Day10] Media Player 控制項 https://ithelp.ithome.com.tw/articles/10012142?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012142?sc=rss.iron 我們在前面幾篇文章中,已經簡要的對伺服器控制項做了基本介紹,接下來的幾篇文章中我們要開始實作伺服器控制項。在網頁上常使用 Media Player 來撥放影片,在 ASP.NET 中沒有現成的控...]]> 我們在前面幾篇文章中,已經簡要的對伺服器控制項做了基本介紹,接下來的幾篇文章中我們要開始實作伺服器控制項。在網頁上常使用 Media Player 來撥放影片,在 ASP.NET 中沒有現成的控制項來處理 Media Player,只能在 aspx 中加入 Media Player 相關的程式碼;本文將示範如何製作一個 Media Player 控制項,讓我們在 ASP.NET 中更方便的使用 Media Player。
程式碼下載:ASP.NET Server Control - Day10.rar

一、Media Player 原始 HTML 碼
在製作 Media Player 控制項之前,你需要先了解 Media Player 原本的 HTML 碼,控制項的作用就是想辨法把這些寫在 aspx 中的 HTML 碼交由控制項來輸出而已,以下為網頁中加入 Media Player 的 HTML 碼範例。

<OBJECT id="VIDEO" width="320" height="240" 
	style="position:absolute; left:0;top:0;"
	CLASSID="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6"
	type="application/x-oleobject">
	
	<PARAM NAME="URL" VALUE="your file or url">
	<PARAM NAME="SendPlayStateChangeEvents" VALUE="True">
	<PARAM NAME="AutoStart" VALUE="True">
	<PARAM name="uiMode" value="none">
	<PARAM name="PlayCount" value="9999">
</OBJECT>

在下面的網頁有 Media Player 相關參數說明。
http://www.mioplanet.com/rsc/embed\_mediaplayer.htm

二、實作 Media Player 控制項
step1.首先新增由 WebControl 繼承下來的 TBMediaPlayer 類別。

    Public Class TBMediaPlayer
        Inherits WebControl

    End Class

step2.覆寫 TagKey 屬性,傳回 object 的 Tag。

        Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag
            Get
                Return HtmlTextWriterTag.Object
            End Get
        End Property

step3.輸出 HTML Tag 的 Attribute
在 object Tag 中包含 style、classid、type 二個 Attribute,WebControl 本身會處理 style,所以我們只需覆寫 AddAttributesToRender 方法,處理 classid 及 type 二個 Attribute,記得覆寫 AddAttributesToRender 方法時最後要加入 MyBase.AddAttributesToRender(writer),才會執行父類別的 AddAttributesToRender 方法。

        ''' <summary>
        ''' 覆寫 AddAttributesToRender 方法。
        ''' </summary>
        Protected Overrides Sub AddAttributesToRender(ByVal writer As System.Web.UI.HtmlTextWriter)
            '加入 MediaPlayer ActiveX 元件的 classid
            writer.AddAttribute("classid", "clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6")
            writer.AddAttribute("type", "application/x-oleobject")
            MyBase.AddAttributesToRender(writer)
        End Sub

step4.加入 Url 屬性
加入指定播放檔案來源的 Url 屬性,其中套用 EditorAttribute 設定 UrlEditor,使用 Url 專用的編輯器來設定屬性。

        ''' <summary>
        ''' 播放檔案來源。
        ''' </summary>
        < _
        Description("播放檔案來源"), _
        Bindable(True), _
        Category("Appearance"), _
        Editor(GetType(UrlEditor), GetType(UITypeEditor)), _
        UrlProperty(), _
        DefaultValue("") _
        > _
        Public Property Url() As String
            Get
                Return FUrl
            End Get
            Set(ByVal value As String)
                FUrl = value
            End Set
        End Property

step5.輸出 Url 參數
接下來覆寫 CreateChildControls 方法,輸出 Url 參數。

        ''' <summary>
        ''' 加入參數。
        ''' </summary>
        ''' <param name="Name">參數名稱。</param>
        ''' <param name="Value">參數值。</param>
        Private Sub AddParam(ByVal Name As String, ByVal Value As String)
            Dim oParam As HtmlControls.HtmlGenericControl

            oParam = New HtmlControls.HtmlGenericControl("param")
            oParam.Attributes.Add("name", Name)
            oParam.Attributes.Add("value", Value)
            Me.Controls.Add(oParam)
        End Sub

        Protected Overrides Sub CreateChildControls()
            '加入 Url 參數
            AddParam("url", Me.ResolveClientUrl(Me.Url))
            MyBase.CreateChildControls()
        End Sub

step6.輸出 Media Player 其他參數
你可以將 Media Player 的參數設定皆使用相對應的屬性來設定,然後使用 step5 的方式來輸出所有設定的參數值。

三、Media Player 控制項程式碼
Media Player 控制項的完整程式碼如下,此控制項只加入 URL、AutoStart、UIMode 三個參數,你可以視需求情形將使用到的參數定義為屬性來做設定即可。
因為此處有字元數限制,完整的程式碼請參閱筆者部落格相同文章
http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx

四、執行程式
把 TBMediaPlayer 控制項拖曳至頁面,設定好屬性後,執行程式就可以在頁面上看到呈現出來的 Media Player。

        <bee:TBMediaPlayer ID="TBMediaPlayer1" runat="server" Height="250px" 
            Width="250px" Url="~/test.wmv" />

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx

]]>
jeff377 2008-10-11 19:08:27
[ASP.NET 控制項實作 Day9] 控制項常用 Attribute 介紹(2) https://ithelp.ithome.com.tw/articles/10012060?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012060?sc=rss.iron 接續上篇 Attribute 的介紹,本文將再介紹一些伺服器控制項常用的 Attribute。
六、ToolboxDataAttribute 類別<...]]>
接續上篇 Attribute 的介紹,本文將再介紹一些伺服器控制項常用的 Attribute。
六、ToolboxDataAttribute 類別
作用:指定當自訂控制項從工具箱拖曳到頁面時,為此自訂控制項產生的預設標記。
當我們新增一個伺服器控制項,它就會預設在控制項類別套用 ToolboxDataAttribute,定義在控制項在 aspx 程式碼中的標記。

<ToolboxData("<{0}:TBButton runat=server ></{0}:TBButton>")> _
Public Class TBButton
    Inherits System.Web.UI.WebControls.Button
End Class

七、DefaultPropertyAttribute 類別
作用:指定類別的預設屬性。
下面的範例中,MyTextbox 類別套用 DefaultPropertyAttribute,設定 Text 屬性為預設屬性。

<DefaultProperty("Text")> _
Public Class MyTextbox
    Inherits WebControl

    Public Property Text() As String
        Get
            Return CType(Me.ViewState("Text"), String)
        End Get

        Set(ByVal value As String)
            Me.ViewState("Text") = value
        End Set
    End Property
End Class

八、DefaultEventAttribute 類別
作用:指定控制項的預設事件。
下面的範例中,MyTextbox 類別套用 DefaultEventAttribute,設定 TextChanged 為預設屬性。

<DefaultEvent("TextChanged")> _
Public Class MyTextbox
    Inherits WebControl

    ''' <summary>
    ''' TextChanged 事件。
    ''' </summary>
    Public Event TextChanged As EventHandler
End Class

當設計階段在頁面上的 MyTextbox 控制項點二下時,就會產生預設事件的處理函式。

    Protected Sub MyTextbox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextbox3.TextChanged

    End Sub

九、LocalizableAttribute 類別
作用:指定屬性是否應該當地語系化。
當屬性套用設為為 true 的 LocalizableAttribute 時,其屬性值會儲存在資源檔中,未來不需修改程式碼就可以將這些資源檔當地語系化。

        <Localizable(True)> _
        Public Property Text() As String
            Get
                Return CType(Me.ViewState("Text"), String)
            End Get

            Set(ByVal value As String)
                Me.ViewState("Text") = value
            End Set
        End Property

十、DesignerAttribute 類別
作用:設定控制項在設計階段服務的類別。
指定一個設計階段的服務類別,來管理控制項在設計階段的行為,例如控制項的設計階段外觀、智慧標籤內容。例如下面範例的 TBGridView 控制項就定義了 TBGridViewDesigner 來實作設計階段的行為,未來的章節中也會介紹如何實作控制項的 Designer。

    < Designer(GetType(TBGridViewDesigner)) > _
    Public Class TBGridView
        Inherits GridView
    End Class

十一、EditorAttribute 類別
作用:指定在屬性視窗中編輯屬性值的編輯器。
例如 ListBox 控制項的 Items 屬性,在屬性視窗編輯 Items 屬性時,會彈出 Items 集合屬性的編輯器。以下範例就是定義 Items 屬性的編輯器類別為 TBListItemsCollectionEditor,未來的章節中也會介紹如何實作屬性的 Editor。

        <Editor(GetType(TBListItemsCollectionEditor), GetType(System.Drawing.Design.UITypeEditor))> _
        Public Overrides ReadOnly Property Items() As ListItemCollection

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/10/5653.aspx

]]>
jeff377 2008-10-10 11:27:02
[ASP.NET 控制項實作 Day8] 控制項常用 Attribute 介紹(1) https://ithelp.ithome.com.tw/articles/10012016?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012016?sc=rss.iron Property 與 Attribute 二個術語一般都是翻譯成「屬性」,例如類別的屬性,是使用英文的 Property,而 HTML/XML 的元素屬性,使用的英文則是 Attribute。在...]]> Property 與 Attribute 二個術語一般都是翻譯成「屬性」,例如類別的屬性,是使用英文的 Property,而 HTML/XML 的元素屬性,使用的英文則是 Attribute。在 .NET 中 Property 與 Attribute 的意義及用法不同,不過微軟線上文件也將它翻譯為「屬性」,這可能讓人發生困擾及誤解;筆者比較喜歡的方式就是 Property 是屬性,Attribute 就維持原文。在 .NET 中類別或屬性上可以套用上不同的 Attribute,使類別或屬性具有不同的特性,本文將介紹一些在伺服器控制項常使用到的 Attribute。
一、DescriptionAttribute 類別
作用:指定控制項或屬性的描述。
當 DescriptionAttribute 套用至控制項的類別時,設定的描述內容就會出現在工具箱中控制項的提示。

<Description("按鈕控制項")> _
Public Class TBButton
    Inherits System.Web.UI.WebControls.Button
End Class


當 DescriptionAttribute 套用至控制項的屬性時,在屬性視窗下面就會出現設定的屬性描述內容。

<Description("詢問訊息")> _
Public Property ConfirmMessage() As String

二、DefaultValueAttribute 類別
作用:指定屬性的預設值。
使用 DefaultValueAttribute 設定屬性的預設值,若設定的屬性值與預設值相同時,此屬性值就不會出現在 aspx 程式碼中;筆者強烈建議屬性一定套用 DefaultValueAttribute,一來在 aspx 中的程式碼會比較少,另外一個重點是若因為某些因素需要修改屬性的預設值時,所有已開發頁面的控制項屬性值會一併變更;因為當初屬性值是預設值,沒有被寫入 aspx 程式碼中,所以一但控制項的屬性預設值變更,頁面已佈屬的控制項的屬性值就會全面適用。

        Private FConfirmMessage As String = String.Empty

        <DefaultValue("")> _
        Public Property ConfirmMessage() As String
            Get
                Return FConfirmMessage
            End Get
            Set(ByVal value As String)
                FConfirmMessage = value
            End Set
        End Property

三、CategoryAttribute 類別
作用:指定屬性或事件的分類名稱,當屬性視窗設定為 [分類] 模式時,以群組方式來顯示屬性或事件。
例如設定 ConfirmMessage 屬性在 "Behavior" 分類,則 ConfirmMessage 屬性會被歸類到「行為」分類。

        <Category("Behavior")> _
        Public Property ConfirmMessage() As String

四、BindableAttribute 類別
作用:指定成員是否通常使用於繫結。
在資料繫結設定視窗中中,指定屬性是否預設會出現在屬性清單中。

<Bindable(True)> _
Public Property ConfirmMessage() As String

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/09/5647.aspx

]]>
jeff377 2008-10-09 21:11:43
[ASP.NET 控制項實作 Day7] 設定工具箱的控制項圖示 https://ithelp.ithome.com.tw/articles/10011933?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011933?sc=rss.iron 當我們把自訂控制項加入到工具箱中時,你會發現所有的控制項預設都是同樣的圖示,雖然控制項的圖示不變更不會有什麼影響,不過我們還是希望為自訂控制項加上合適的外衣,本文將介紹如何設定工具箱控制項圖示。...]]> 當我們把自訂控制項加入到工具箱中時,你會發現所有的控制項預設都是同樣的圖示,雖然控制項的圖示不變更不會有什麼影響,不過我們還是希望為自訂控制項加上合適的外衣,本文將介紹如何設定工具箱控制項圖示。
一、加入控制項圖示檔
首先要準備一個 16 x 16 的點陣圖(bmp),如下所示。

將此圖檔加入至「伺服器控制項專案」中,可以如下圖所示,用一個特定的資料夾來儲存所有工具箱的圖示。

然後在圖檔的屬性視窗中,設定建置動作為「內嵌資源」。

二、設定控制項的圖示
首先定義一個 TBResource 類別,此為一個空的類別,其命名空間需與根命名空間相同,做為引用資源檔時使用。並加上控制項圖示的 WebResource 定義,因為根命名空間是 Bee.Web,而圖檔名稱為 TBButton.bmp,所以定義如下所示。

假設我們要設定 TBButton 的工具箱圖示,則在 TBButton 類別套用 ToolboxBitmapAttribute 如下,其中第一個參數為 GetType(TBResource),第二個參數為圖檔檔名。

重新編輯伺服器控制項專案,再將 Bee.Web.dll 組件的控制項加入工具箱中,你就可以發現 TBButton 的圖示已經變成設定的圖示了。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/08/5624.aspx

]]>
jeff377 2008-10-08 22:26:13
[ASP.NET 控制項實作 Day6] 事件與 PostBack https://ithelp.ithome.com.tw/articles/10011861?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011861?sc=rss.iron 一般類別的事件撰寫很單純,不過在 ASP.NET 中與前端使用者互動產生的事件就不是那麼簡單了;在以往的 ASP 年代是沒有事件這回事的,而在 ASP.NET 把網頁程式撰寫真正的物件導向化,用...]]> 一般類別的事件撰寫很單純,不過在 ASP.NET 中與前端使用者互動產生的事件就不是那麼簡單了;在以往的 ASP 年代是沒有事件這回事的,而在 ASP.NET 把網頁程式撰寫真正的物件導向化,用戶端使用者的操作透過 PostBack 來產生相對應的事件。例如前端使用者按鈕後會引發伺服端 Button 的 Click 事件,當前端使用者輸入文字框完畢後離開後會引發伺服端 TextBox 的 TextChanged 事件,在本文中就是要說明如何透過 PostBack 來產生與使用者互動的事件。
一、IPostBackEventHandler 與 IPostBackDataHandler 介面
控制項要處理 PostBack 產生的事件,必須實作 IPostBackEventHandler 或 IPostBackDataHandler 介面,這二個介面有什麼差別呢?例如 Button 是實作IPostBackEventHandler 介面,由控制項產生的 PostBack 直接引發事件,如 Button 的 Click 事件。例如 TextBox 是實作 IPostBackDataHandler 介面,當頁面產生 PostBack 時,會傳用戶端輸入的新值給控制項,由控制項本身去決定是否引發該事件;以 TextBox 舉例來說,它會判斷新值與舊值不同時才會引發 TextChanged 事件。

二、IPostBackEventHandler 介面實作
首先介紹 IPostBackEventHandler 介面,它包含 RaisePostBackEvent 方法,控制項在此方法中需處理要引發那些事件。我們繼承 WebControl 撰寫 MyButton 類別來說明 IPostBackEventHandler 介面,我們簡化控制項程式碼直接在 Render 方法輸入按鈕的 HTML 原始碼,並定義一個 Click 事件。實作 IPostBackEventHandler 介面的 RaisePostBackEvent 方法,在此方法中直接引發 Click 事件。

Public Class MyButton
    Inherits WebControl
    Implements IPostBackEventHandler

    ''' <summary>
    ''' Click 事件。
    ''' </summary>
    Public Event Click As EventHandler

    ''' <summary>
    ''' 引發 Click 事件。
    ''' </summary>
    Private Sub OnClick(ByVal e As EventArgs)
        RaiseEvent Click(Me, e)
    End Sub

    Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
        Dim e As New EventArgs()
        OnClick(e) '引發 Click 事件
    End Sub

    ''' <summary>
    ''' 覆寫 Render 方法。
    ''' </summary>
    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
        Dim sHTML As String

        sHTML = String.Format("<INPUT TYPE=Submit Name={0} Value = '按鈕'/>", Me.UniqueID)
        writer.Write(sHTML)
    End Sub

End Class

在頁面上拖曳 MyButton 控制項,在屬性視窗找到 Click 事件,點二下產生 Click 事件處理函式,並撰寫測試程式碼如下。

    Protected Sub MyButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton1.Click
        Me.Page.Response.Write("MyButton Click 事件")
    End Sub

執行程式,當按下 MyButton 按鈕時,就會執行它的 RaisePostBackEvent 方法,進而引發 Click 事件,也就會執行 Click 事件處理函式的程式碼。

三、IPostBackDataHandler 介面實作
IPostBackDataHandler 介面包含 LoadPostData 及 RaisePostDataChangedEvent 方法,當頁面 PostBack 時,會尋找具 IPostBackDataHandler 介面的控制項,先執行LoadPostData 方法,控制項在 LoadPostData 方法中會判斷用戶端傳入值決定是否引發事件,若 LoadPostData 傳回 True 表示要引發事件,此時會執行RaisePostDataChangedEvent 方法去決定要引發那些事件,反之傳回 False 表示不引發事件。

我們繼承 WebControl 撰寫 MyText 類別來說明 IPostBackDataHandler 介面,我們簡化控制項程式碼直接在 Render 方法輸入文字框的 HTML 原始碼,並定義一個 TextChanged 事件。在 LoadPostData 方法中我們會判斷用戶端傳入值與目前的值是否不相同,不相同時才會傳回 True,此時才會執行 RaisePostDataChangedEvent 方法,進而引發 TextChanged 事件。

Public Class MyTextbox
    Inherits WebControl
    Implements IPostBackDataHandler

    Public Property Text() As String
        Get
            Return CType(Me.ViewState("Text"), String)
        End Get
        Set(ByVal value As String)
            Me.ViewState("Text") = value
        End Set
    End Property

    ''' <summary>
    ''' TextChanged 事件。
    ''' </summary>
    Public Event TextChanged As EventHandler

    ''' <summary>
    ''' 引發 TextChanged 事件。
    ''' </summary>
    Private Sub OnTextChanged(ByVal e As EventArgs)
        RaiseEvent TextChanged(Me, e)
    End Sub

    Public Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As System.Collections.Specialized.NameValueCollection) As Boolean Implements System.Web.UI.IPostBackDataHandler.LoadPostData
        '前端使用者輸入值
        Dim oNewValue As String = postCollection.Item(postDataKey)
        If oNewValue <> Me.Text Then
            Me.Text = oNewValue
            Return True
        Else
            Return False
        End If
    End Function

    Public Sub RaisePostDataChangedEvent() Implements System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent
        Dim e As New EventArgs()
        '引發 TextChanged 事件
        OnTextChanged(e)
    End Sub

    ''' <summary>
    ''' 覆寫 Render 方法。
    ''' </summary>
    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
        Dim sHTML As String

        sHTML = String.Format("<INPUT Type=text Name={0} Value={1} >", Me.UniqueID, Me.Text)
        writer.Write(sHTML)
    End Sub

End Class

在頁面上拖曳 MyTextbox 及 MyButton 控制項,在 MyButton 的 Click 及 MyTextbox 的 TextChanged 事件撰寫如下測試程式碼。

    Protected Sub MyButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton1.Click
        Me.Page.Response.Write("MyButton Click 事件")
        Me.Page.Response.Write("<br/>")
    End Sub

    Protected Sub MyTextbox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextbox1.TextChanged
        Me.Page.Response.Write("MyTextbox TextChanged 事件")
        Me.Page.Response.Write("<br/>")
    End Sub

執行程式,在 MyTextbox 輸入 "A",再按下 MyButton,因為 MyTextbox 的值「空字串->"A"」,所以會引發 MyTextbox 的 TextChanged 事件及 MyButton 的 Click 事件。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格

]]>
jeff377 2008-10-07 23:30:19
[ASP.NET 控制項實作 Day5] 屬性與 ViewState https://ithelp.ithome.com.tw/articles/10011745?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011745?sc=rss.iron 在 ASP.NET 中,控制項的屬性與 ViewState 有著密不可分的關係,透過 ViewState 才有辨法維護控制項的屬性值。在本文中將介紹屬性與 ViewState 的關係,並說明屬性...]]> 在 ASP.NET 中,控制項的屬性與 ViewState 有著密不可分的關係,透過 ViewState 才有辨法維護控制項的屬性值。在本文中將介紹屬性與 ViewState 的關係,並說明屬性如何存取 ViewState 是比較有效率的方式。
當你加入一個「ASP.NET 伺服器控制項」時,類別中預設會有一個 Text 屬性寫法的範例如下所示,屬性的讀寫都是直接存取 ViewState,這是一般常見的控制項屬性寫法。可是這種屬性的寫法是沒有效率的,因為 ViewState 的成員是 Object 型別,每次讀取屬性時都是由 ViewState 取出指定鍵值的成員再轉型為屬性的型別,寫入屬性的動作也是直接寫入 ViewState 中。

    Property Text() As String
        Get
            Dim s As String = CStr(ViewState("Text"))
            If s Is Nothing Then
                Return String.Empty
            Else
                Return s
            End If
        End Get

        Set(ByVal Value As String)
            ViewState("Text") = Value
        End Set
    End Property

比較好的方式應該是讀取 ViewState 成員只做一次型別轉換的動作,而寫入 ViewState 的動作可以在 Render 前做批次寫入的動作即可。為了達到這樣的需求,我們可以覆寫 LoadViewState 及 SaveViewState 方法來處理屬性與 ViewState 的存取動作;當控制項初始化後會執行 LoadViewState 方法,來載入 ViewState 還原的控制項狀態,當控制項 Render 之前,會執行 SaveViewState 方法,將控制項的最終狀態存入 ViewState 中,也就是在此方法之後對控制項所做的任何變更都將會被忽略。

我們改寫屬性的寫法,不直接用 ViewState 來存取屬性,而是改用「屬性區域變數」來存取屬性,在 LoadViewState 時載入 ViewState 到屬性區域變數,而 SaveViewState 時再將屬性區域變數寫入 ViewState 中。我們依此方式將 Text 屬性改寫如下。

    Private FText As String

    Property Text() As String
        Get
            Return FText
        End Get
        Set(ByVal Value As String)
            FText = Value
        End Set
    End Property

    ''' <summary>
    ''' 載入 ViewState 還原控制項狀態。
    ''' </summary>
    Protected Overrides Sub LoadViewState(ByVal savedState As Object)
        If Not (savedState Is Nothing) Then
            ' Load State from the array of objects that was saved at vedViewState.
            Dim myState As Object() = CType(savedState, Object())

            If Not (myState(0) Is Nothing) Then
                MyBase.LoadViewState(myState(0))
            End If

            If Not (myState(1) Is Nothing) Then
                FText = CType(myState(1), String)
            End If
        End If
    End Sub

    ''' <summary>
    ''' 將控制項狀態寫入 ViewState 中。
    ''' </summary>
    Protected Overrides Function SaveViewState() As Object
        Dim baseState As Object = MyBase.SaveViewState()
        Dim myState(1) As Object
        myState(0) = baseState
        myState(1) = FText
        Return myState
    End Function

利用上述的方式,我們可以在 LoadViewState 批次載入所有屬性值,而在 SaveViewState 批次寫入屬性值,如此在讀取屬性就不用一直做型別轉換的動作以改善效率。

結語
雖然屬性一般都是儲存於 ViewState 中,不過若是一些無關緊要的屬性或是完全不會執行階段就變更的屬性,可以考慮不需要將這些屬性儲存於 ViewState 中;因為 ViewState 是個兩面刃,ViewState 可以很輕易幫我們維護屬性值,不過相對的也增加了面頁的傳輸量,所以可以視實際情形來決定屬性是否要儲存於 ViewState 中。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/07/5601.aspx

]]>
jeff377 2008-10-06 21:17:20
[ASP.NET 控制項實作 Day4] 複合控制項 https://ithelp.ithome.com.tw/articles/10011633?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011633?sc=rss.iron 複合控制項就是控制項可包含其他子控制項,複合控制項繼承至 System.Web.UI.WebControls.CompositeControl,例如 Login 及 Wizard 等控制項就是屬...]]> 複合控制項就是控制項可包含其他子控制項,複合控制項繼承至 System.Web.UI.WebControls.CompositeControl,例如 Login 及 Wizard 等控制項就是屬於複合控制項。我們常在網頁上常看到一種輸入日期的方式是年月日三個下拉清單,本文將利用複合控制項來實作這個年月日下拉清單控制項,示範如何實作複合控制項。
一、CompositeControl 類別的特性
CompositeControl 類別是抽象類別,它會實作 INamingContaner 介面,INamingContaner 介面會在子控制項的 ClinetID 加上父控制項的 ID,以確保頁面上控制項的 ClientID 是唯一的。繼承 CompositeControl 類別一般都是覆寫 CreateChildControls 方法,在此方法中將建立子控制項並加入 Controls 集合屬性中;當存取其子控制項時,若子控制項未建立,會執行 CreateChildControls 方法,以會確保所有子控制項皆已在存取 ControlCollection 之前建立。
二、日期下拉清單輸入器
我們繼承 CompositeControl 類別,命名為 TBDropDownDate。這個控制項會包含年月日三個下拉清單(DropDownList),所以我們只要在 CreateChildControls 方法中依序建立年月日的 DropDownList 子控制項,並加入 Controls 集合屬性中即可。

''' <summary>
''' 日期下拉清單輸入器。
''' </summary>
< _
ToolboxData("<{0}:TBDropDownDate runat=server></{0}:TBDropDownDate>") _
> _
Public Class TBDropDownDate
    Inherits System.Web.UI.WebControls.CompositeControl

    Protected Overrides Sub CreateChildControls()
        Dim oYear As DropDownList
        Dim oMonth As DropDownList
        Dim oDay As DropDownList
        Dim N1 As Integer

        '年下拉清單區間為 1950-2010 (年區間可以用屬性來設定)
        oYear = New DropDownList
        oYear.ID = "Year"
        For N1 = 1950 To 2010
            oYear.Items.Add(N1.ToString)
        Next
        Me.Controls.Add(oYear) '加入子控制項
        Me.Controls.Add(New LiteralControl("年"))

        '月下拉清單區間為 1-12
        oMonth = New DropDownList
        oMonth.ID = "Month"
        For N1 = 1 To 12
            oMonth.Items.Add(N1.ToString)
        Next
        Me.Controls.Add(oMonth) '加入子控制項
        Me.Controls.Add(New LiteralControl("月"))

        '日下拉清單區為為 1-31
        oDay = New DropDownList
        oDay.ID = "Day"
        For N1 = 1 To 12
            oDay.Items.Add(N1.ToString)
        Next
        Me.Controls.Add(oDay) '加入子控制項
        Me.Controls.Add(New LiteralControl("日"))

    End Sub
End Class

在設定階段拖曳 TBDropDownDate 到頁面上,就可以看到我們在 CreateChildControls 方法中所加入的子控制項。

執行程式,檢視它的 HTML 原始碼,會發現年月日的子控制項的 ClientID 都會在原 ID 前加上父控制項的 ID,這樣命名規則可以確保所有的控制項的 ClinetID 都是唯一值。

<span id="TBDropDownDate1">
<select name="TBDropDownDate1$Year" id="TBDropDownDate1_Year">

....省略

<select name="TBDropDownDate1$Month" id="TBDropDownDate1_Month">

....省略

<select name="TBDropDownDate1$Day" id="TBDropDownDate1_Day">
</span>

三、結語
我們已經看過三類伺服器控制項的簡單案例,不過這三個案例都只是簡單說明控制項 UI 的部分,一個完整的控制項需具備屬性、方法、事件、設計階段支援...等,在後面的文章中,我們將陸續針對這些部分做詳細的介紹。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/05/5583.aspx

]]>
jeff377 2008-10-05 22:22:25
[ASP.NET 控制項實作 Day3] 擴展現有伺服器控制項功能 https://ithelp.ithome.com.tw/articles/10011562?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011562?sc=rss.iron 相對於由無到有開發控制項,繼承現有現伺服器控制項是比較簡單且實用的方式;若希望在現有的控制項增加某些屬性或功能,直接繼承該控制項下來擴展功能是最快的方式,例如「按下 Button 會彈出詢問訊息...]]> 相對於由無到有開發控制項,繼承現有現伺服器控制項是比較簡單且實用的方式;若希望在現有的控制項增加某些屬性或功能,直接繼承該控制項下來擴展功能是最快的方式,例如「按下 Button 會彈出詢問訊息」、「TextBox 設為 ReadOnly 時,可以取得前端傳回的 Text 屬性」這類需求,都可以直接繼承原控制項下來,加上我們需要的功能即可。以下我們就以一個簡單的案例來說明如何繼承現有伺服器下來擴展功能。
一、擴展 Button 控制項:按鈕加上詢問訊息
按下按鈕執行某些動作前,有時會詢問使用者是否執行該動作;例如按下刪除鈕,會詢問使用者是否確定要執行刪除的動作。當然這只需要簡單的 JavaScript 就可以完成,不過相對於 .NET 的程式語言,JavaScript 是非常不易維護的用戶端指令碼,如果能讓開發人員完全用不到 JavaScript,那何樂不為呢? 那就由 Button 控制項本身提供加上詢問訊息的功能就可以,相關的 JavaScript 由控制項去處理。
一般要在 Button 加上詢問訊息,只要在 OnClientClick 屬性設定如下的 JavaScript 即可。我們的目的只是讓開發人員連設定 OnClientClick 屬性的 JavaScript 都省略,直接設定要詢問的訊息即可,接下來我們就要開始實作這個控制項。

<asp:Button ID="Button1" runat="server" Text="Button"  OnClientClick="if (confirm('確定執行嗎?')==false) {return false;}" />   

在 Bee.Web 專案中,加入「ASP.NET 伺服器控制項」,此控制項繼承 Button 下來命名為 TBButton (命名空間為 Bee.Web.WebControls)。在 TBButton 類別中加入 ConfirmMessage 屬性,用來設定詢問訊息的內容。然後在 Render 方法將詢問詢息的 JavaScript 設定到 OnClientClick 屬性即可。

Namespace WebControls
    < _
    Description("按鈕控制項"), _
    ToolboxData("<{0}:TBButton runat=server></{0}:TBButton>") _
    > _
    Public Class TBButton
        Inherits System.Web.UI.WebControls.Button

        <Description("詢問訊息")> _
        Public Property ConfirmMessage() As String
            Get
                Dim sConfirmMessage As String
                sConfirmMessage = CStr(ViewState("ConfirmMessage"))
                If sConfirmMessage Is Nothing Then
                    Return String.Empty
                Else
                    Return sConfirmMessage
                End If
            End Get
            Set(ByVal value As String)
                ViewState("ConfirmMessage") = value
            End Set
        End Property

        ''' <summary>
        ''' 覆寫 Render 方法。
        ''' </summary>
        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            Dim sScript As String
            Dim sConfirm As String

            '若有設定 ConfirmMessage 屬性,則在 OnClientClick 加入詢問訊息的 JavaScript
            If Me.ConfirmMessage <> String.Empty Then
                sScript = Me.OnClientClick
                '詢問訊息的 JavaScript
                sConfirm = String.Format("if (confirm('{0}')==false) {{return false;}}", Me.ConfirmMessage)
                If sScript = String.Empty Then
                    Me.OnClientClick = sConfirm
                Else
                    Me.OnClientClick = sConfirm & sScript
                End If
            End If
            MyBase.Render(writer)
        End Sub

    End Class
End Namespace

將 TBButton 拖曳到測試頁面,設定 ConfirmMessage 屬性。

<bee:TBButton ID="TBButton1" runat="server" ConfirmMessage="確定刪除此筆資料嗎?" Text="刪除" />

執行結果如下。

二、結語
筆者在開發 ASP.NET 的應用程式過程中,通常會習慣把所有現有控制項繼承下來,無論目前需不需要擴展控制項功能。這種方式對於開發大型系統是相當有幫助的,因為無法預期在系統開發的過程中會不會因為某些狀況,而臨時需要擴展控制項的功能,所以就先全部繼承下來以備不時之需,也為未來保留修改的彈性。

三、相關連結
擴展 CommandField 類別 - 刪除提示訊息
按鈕加上詢問訊息

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/04/5578.aspx

]]>
jeff377 2008-10-04 21:24:49
[ASP.NET 控制項實作 Day2] 建立第一個伺服器控制項 https://ithelp.ithome.com.tw/articles/10011523?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011523?sc=rss.iron 上一篇中已經建立「ASP.NET 伺服器控制項」專案,接下來我們將學習來撰寫第一個伺服器控制項。
撰寫伺服器控制項大致分為下列三種方式
1.由無到有建立全新的控制項,一般...]]>
上一篇中已經建立「ASP.NET 伺服器控制項」專案,接下來我們將學習來撰寫第一個伺服器控制項。
撰寫伺服器控制項大致分為下列三種方式
1.由無到有建立全新的控制項,一般會繼承至 System.Web.UI.Control 或 System.Web.UI.WebControls.WebControl 類別。
2.繼承現有控制項,擴展原有控制項的功能,如繼承原有 TextBox 來擴展功能。
3.複合式控制項,將多個現有的控制項組合成為一個新的控制項,例如 TextBox 右邊加個 Button 整合成一個控制項,一般會繼承至 System.Web.UI.WebControls.CompositeControl 類別。

本文將先介紹第1種方式,由無到有來建立控制項,後面的文章中會陸續介紹第2、3種方式的控制項。要建立全新的控制項會繼承至 Control 或 WebControl,沒有 UI 的控制項可由 Control 繼承下來 (如 SqlDataSource),具 UI 的控制項會由 WebControl 繼承下來。接下來的範例中,我們將繼承 WebControl 來建立第一個 MyTextBox 控制項。

一、新增 MyTextBox 控制項
在 Bee.Web 專案按右鍵選單,執行「加入\新增項目」,選擇「ASP.NET 伺服器控制項」,在名稱文字框中輸入 MyTextbox,按下「確定」鈕,就會在專案中加入 MyTextbox 控制項類別。

新加入的控制項預設有一個 Text 屬性,以及覆寫 Render 方法。Render 方法是「將控制項呈現在指定的 HTML 寫入器中」,簡單的說就是在 Render 方法會將控制項對應的 HTML 碼輸出,用來呈現在用戶端的瀏覽器上。假設我們要撰寫一個網頁上的文字框,那就先去看一下文字框在網頁中對應的 HTML 碼,然後在 Render 方法中想辨法輸出這些 HTML 碼即可。

二、輸出控制項的 HTML 碼
你可以使用 FrontPage 之類的 HTML 編輯器,先編輯出控制項的呈現方式,進而去觀查它的 HTML 碼,再回頭去思考如何去撰寫這個伺服器控制項。假設 MyTextbox 控制項包含一個文字框及一個按鈕,那最終輸出的 HTML 碼應該如下。

<input id="Text1" type="text" />
<input id="Button1" type="button" value="button" />

我們在 MyTextbox 的 RenderContents 方法中輸出上述的 HTML 碼。

    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
        Dim sHTML As String

        sHTML = "<input id=""Text1"" type=""text"" />" & _
                "<input id=""Button1"" type=""button"" value=""button"" />"
        writer.Write(sHTML)
    End Sub

建置控制項專案,然後拖曳 MyTextbox 在測試頁面上,設計階段就會呈現出我們期望的結果。

執行程式,在瀏覽器看一下 MyTextbox 控制項輸出的結果,是不是跟我們預期的一樣呢。

三、屬性套用到控制項 HTML 碼
控制項不可能單純這樣輸出 HTML 碼而已,控制項的相關屬性設定,一般都影響到輸出的 HTML 碼。假設 MyTextbox 有 Text 及 ButtonText 二個屬性,分別對應到 文字框的內容及按鈕的文字,MyTextbox 本來就有 Text 屬性,依像畫蘆葫新增 ButtonText 屬性。

    < _
    Bindable(True), _
    Category("Appearance"), _
    DefaultValue(""), _
    Localizable(True)> _
    Property ButtonText() As String
        Get
            Dim s As String = CStr(ViewState("ButtonText"))
            If s Is Nothing Then
                Return String.Empty
            Else
                Return s
            End If
        End Get

        Set(ByVal Value As String)
            ViewState("ButtonText") = Value
        End Set
    End Property

RenderContents 方法改寫如下。

    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
        Dim sHTML As String

        sHTML = "<input id=""Text1"" type=""text"" value=""{0}""/>" & _
                "<input id=""Button1"" type=""button"" value=""{1}"" />"
        sHTML = String.Format(sHTML, Me.Text, Me.ButtonText)
        writer.Write(sHTML)
    End Sub

重新建置控制項專案,在頁面上測試 MyTextbox 的 Text 及 ButtonText 屬性。

四、使 ClientID (HTML 原始碼控制項的 ID) 是唯一值
在頁面上放置二個 MyTextbox 控制項,執行程式,在瀏覽器中檢查 MyTextbox 的 HTML 原始碼。你會發現 MyTextbox 會以一個 span 包住控制項的內容,而每個控制項的輸出的 ClientID 是唯一的。不過 MyTextbox 內含的文字框及按鈕卻會重覆,所以一般子控制項的 ClientID 會在前面包含父控制項的 ID。

<span id="MyTextbox1">
<input id="Text1" type="text" value="這是文字"/>
<input id="Button1" type="button" value="這是按鈕" />
</span>

<br />

<span id="MyTextbox2">
<input id="Text1" type="text" value="這是文字"/>
<input id="Button1" type="button" value="這是按鈕" />
</span>

所以我們再次修改 RenderContents 方法的程式碼

    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
        Dim sHTML As String

        sHTML = "<input id=""{0}_Text"" type=""text"" value=""{1}""/>" & _
                "<input id=""{0}_Button"" type=""button"" value=""{2}"" />"
        sHTML = String.Format(sHTML, Me.ID, Me.Text, Me.ButtonText)
        writer.Write(sHTML)
    End Sub

執行程式,再次檢視 HTML 原始碼,所有的 ClinetID 都會是唯一的。

<span id="MyTextbox1">
<input id="MyTextbox1_Text" type="text" value="這是文字"/>
<input id="MyTextbox1_Button" type="button" value="這是按鈕" />
</span>

<br />

<span id="MyTextbox2">
<input id="MyTextbox2_Text" type="text" value="這是文字"/>
<input id="MyTextbox2_Button" type="button" value="這是按鈕" />
</span>

五、控制項前置詞
自訂控制項的預設前置詞是 cc1,不過這是可以修改的,在專案中的 AssemblyInfo.vb 檔案中,加入如下定義即可。詳細的作法請參考筆者部落格中的「自訂伺服器控制項前置詞」一本有詳細介紹,在此不再累述。

'設定控制項的標記前置詞
<Assembly: TagPrefix("Bee.Web.WebControls", "bee")>

六、結語
本文中是用土法鍊鋼的方法在撰寫伺服器控制項,一般在實作控制項時會有更好的方式、更易維護的寫法,後續的文章中會陸續介紹相關作法。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/03/5573.aspx

]]>
jeff377 2008-10-03 23:51:35
[ASP.NET 控制項實作 Day1] 建立 ASP.NET 伺服器控制項專案 https://ithelp.ithome.com.tw/articles/10011408?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011408?sc=rss.iron 在 ASP.NET 開發環境中,我們常使用現成的控制項直接拖曳至頁面中使用,有沒有想過我們也可以開發自用的控制項呢?本文將本文以 VS2008 為開發工具,VB.NET 為開發程式語言,來說明如...]]> 在 ASP.NET 開發環境中,我們常使用現成的控制項直接拖曳至頁面中使用,有沒有想過我們也可以開發自用的控制項呢?本文將本文以 VS2008 為開發工具,VB.NET 為開發程式語言,來說明如何建立「伺服器控制項」專案,以及如何測試開發階段的的伺服器控制項。
一、建立「ASP.NET 伺服器控制項」專案
首先執行功能表「檔案\新增專案」,在專案類型中選擇 Visual Basic -> Web,選取「ASP.NET 伺服器控制項」範本,在名稱文字框中輸入專案名稱,也就是組件的檔案名稱,我們輸入 Bee.Web 為專案名稱,組件檔案為 Bee.Web.dll,按下「確定」鈕即會建立新的「ASP.NET 伺服器控制項」專案。

在新建立「ASP.NET 伺服器控制項」專案中,會預設加入一個伺服器控制項類別(ServerControl1.vb),這個伺服器控制項已經事件幫我們加入一些控制項的程式碼。目前暫不做任何修改,直接使用此控制項來做測試說明。

接下來執行功能表「專案\Bee.Web 屬性」,設定此組件的根命名空間,一般慣用的根命名空間都會與組件名稱相同,以方便加入參考時可以快速找到相關組件。

我們先儲存這個「ASP.NET 伺服器控制項」專案,指定儲存位置,按下「儲存」鈕。整個專案相關檔案,會儲存在以專案名稱的資料夾中。

二、加入測試網站
不要關閉目前「ASP.NET 伺服器控制項」專案,執行功能表「檔案\加入\新網站」,選擇「ASP.NET 網站」,會在方案中加入一個網站,來測試開發階段的伺服器控制項使用。

在測試網站加入參考,選擇「專案」頁籤,此頁籤中會列出該方案中其他可加入參考的專案,選取 Bee.Web 專案,按下「確定」鈕。

先在 Bee.Web 專案中執行「建置」動作,然後切換到測試網站的頁面設計,工具箱中就會出現 ServerControl1 伺服器控制項。這個控制項就可以直接拖曳至頁面中使用,這個控制項只是單純 Render 出 Text 屬性值,你可以在控制項屬性視窗中,更改 Text 屬性值為 "測試文字",就會看到這個控制項顯示 "測試文字"。將測試網站設為啟動專案,按下「F5」執行程式,就會看到該控制在執行階段的結果。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/02/5562.aspx

]]>
jeff377 2008-10-02 23:16:29


https://matters.news/@twcctz500/undefined-健康是最好的禮物蛋黃油https-www-facebook-com-eggsoil-bafyreifu3vznb6tdpc5axcoyjkxkyoqxzodprbhhg67b4pofzvsilba5be

<?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"     xmlns:media="http://search.yahoo.com/mrss/">    <channel>        <title>ASP.NET 伺服器控制項開發 :: 2008 iT 邦幫忙鐵人賽</title>        <link>https://ithelp.ithome.com.tw/users/20007956/ironman</link>        <description><![CDATA[ASP.NET 是目前相當熱門的網站開發程式語言,市面上也有一大卡車的書籍在介紹 ASP.NET,不過卻非常少介紹「ASP.NET 伺服器控制項」方面的書籍。在此將透過一系列...]]></description>        <atom:link href="https://ithelp.ithome.com.tw/users/20007956/ironman" rel="self"></atom:link>                <language>zh-TW</language>        <lastBuildDate>Mon, 06 Jun 2022 20:19:06 +0800</lastBuildDate>                    <item>                <title>[ASP.NET 控制項實作 Day29] 解決 DropDownList 成員 Value 值相同產生的問題(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10013458?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013458?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> 接下來還要覆寫 LoadPostData 方法,取得 __EVENTARGUMENT 這個 HiddenField 的值,並判斷與原 SelectedIndex 屬性值是...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> 接下來還要覆寫 LoadPostData 方法,取得 __EVENTARGUMENT 這個 HiddenField 的值,並判斷與原 SelectedIndex 屬性值是否不同,不同的話傳回 True,使其產生 SelectedIndexChanged 事件。</p> <pre><code>        Protected Overrides Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean            Dim values As String()            Dim iSelectedIndex As Integer            Me.EnsureDataBound()            values = postCollection.GetValues(postDataKey)            If (Not values Is Nothing) Then                iSelectedIndex = CInt(Me.Page.Request.Form(&quot;__EVENTARGUMENT&quot;))                If (Me.SelectedIndex &lt;&gt; iSelectedIndex) Then                    MyBase.SetPostDataSelection(iSelectedIndex)                    Return True                End If            End If            Return False        End Function </code></pre> <p><strong>四、測試程式</strong><br /> 在 TBDropDownList 的 SelectedIndexChanged 事件撰寫如下測試程式碼。</p> <pre><code>    Protected Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged        Dim sText As String        sText = String.Format(&quot;TBDropDownList: Index={0} Value={1}&quot;, DropDownList2.SelectedIndex, DropDownList2.SelectedValue)        Me.Response.Write(sText)    End Sub </code></pre> <p>執行程式,在 TBDropDownList 選取 &quot;王五&quot; 這個選項時,會正常顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb_10.png" alt="" /></p> <p>接下選取 Value 值相同的 &quot;陳六&quot; 這個選項,也會正常引發 SelectedIndexChanged ,並顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb_11.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/30/5830.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/30/5830.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-30 21:23:12</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day29] 解決 DropDownList 成員 Value 值相同產生的問題</title>                <link>https://ithelp.ithome.com.tw/articles/10013457?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013457?sc=rss.iron</guid>                <description><![CDATA[<p>DropDownList 控制頁的成員清單中,若有 ListItem 的 Value 值是相同的情形時,會造成 DropDownList 無法取得正確的 SelectedIndex 屬性值、且無...]]></description>                                    <content:encoded><![CDATA[<p>DropDownList 控制頁的成員清單中,若有 ListItem 的 Value 值是相同的情形時,會造成 DropDownList 無法取得正確的 SelectedIndex 屬性值、且無法正確引發 SelectedIndexChanged 事件的問題;今天剛好在網路上看到有人在詢問此問題,所以本文將說明這個問題的源由,並修改 DropDownList 控制項來解決這個問題。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008103021737321.rar" target="_blank">ASP.NET Server Control - Day29.rar</a></p> <p><strong>一、DropDownList 的成員 Value 值相同產生的問題</strong><br /> 我們先寫個測試程式來描述問題,在頁面上放置一個 DropDownList 控制項,設定 AutoPostBack=True,並加入四個 ListItem,其中 &quot;王五&quot; 及 &quot;陳六&quot; 二個 ListItem 的 Value 值相同。</p> <pre><code>    &lt;asp:DropDownList ID=&quot;DropDownList1&quot; runat=&quot;server&quot; AutoPostBack=&quot;True&quot;&gt;            &lt;asp:ListItem Value=&quot;0&quot;&gt;張三&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;1&quot;&gt;李四&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;2&quot;&gt;王五&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;2&quot;&gt;陳六&lt;/asp:ListItem&gt;    &lt;/asp:DropDownList&gt; </code></pre> <p>在 DropDownList 的 SelectedIndexChanged 事件,輸出 DropDownList 的 SelectedIndex 及 SelectedValue 屬性值。</p> <pre><code>    Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged        Dim sText As String        sText = String.Format(&quot;DropDownList: Index={0} Value={1}&quot;, DropDownList1.SelectedIndex, DropDownList1.SelectedValue)        Me.Response.Write(sText)    End Sub </code></pre> <p>執行程式,在 DropDownList 選取 &quot;李四&quot; 這個選項時,會正常顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb.png" alt="" /></p> <p>接下來選取 &quot;陳六&quot; 這個選項時,竟然發生奇怪的現象,DorpDownList 竟然顯示相同 Value 值的 &quot;王五&quot; 這個成員的 SelectedIndex 及 SelectedValue 屬性值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb_6.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb_7.png" alt="" /></p> <p><strong>二、問題發生的原因</strong><br /> 我們先看一下 DropDownList 輸出到用戶端的 HTML 原始碼。</p> <pre><code>&lt;select name=&quot;DropDownList1&quot; onchange=&quot;javascript:setTimeout('__doPostBack(\'DropDownList1\',\'\')', 0)&quot; id=&quot;DropDownList1&quot;&gt; &lt;option selected=&quot;selected&quot; value=&quot;0&quot;&gt;張三&lt;/option&gt; &lt;option value=&quot;1&quot;&gt;李四&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;王五&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;陳六&lt;/option&gt; &lt;/select&gt; </code></pre> <p>DropDownList 是呼叫 __doPostBack 函式,只傳入 eventTarget參數 (對應到 __EVENTTARGET 這個 HiddenField) 為 DropDownList 的 ClientID;當 PostBack 回伺服端時,在 DropDownList 的 LoadPostData 方法中,會取得用戶端選取的 SelectedValue 值,並去尋找對應的成員的 SelectedIndex 值。可是問題來了,因為 &quot;王五&quot; 與 &quot;陳六&quot; 的 Value 是相同的值,當在尋找符合 Value 值的成員時,前面的選項 &quot;王五&quot; 會先符合條件而傳回該 Index 值,所以先造成取得錯誤的 SelectedIndex 。</p> <pre><code>Protected Overridable Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean    Dim values As String() = postCollection.GetValues(postDataKey)    Me.EnsureDataBound    If (Not values Is Nothing) Then        MyBase.ValidateEvent(postDataKey, values(0))        Dim selectedIndex As Integer = Me.Items.FindByValueInternal(values(0), False)        If (Me.SelectedIndex &lt;&gt; selectedIndex) Then            MyBase.SetPostDataSelection(selectedIndex)            Return True        End If    End If    Return False End Function </code></pre> <p><strong>三、修改 DropDownList 控制項來解決問題</strong><br /> 要解決這個問題最好的方式就是直接修改 DropDownList 控制項,自行處理前端呼叫 __doPostBack 的動作,將用戶端 DropDownList 選擇 SelectedIndex 一併傳回伺服端。所以我們繼承 DropDownList 命名為 TBDropDownList,覆寫 AddAttributesToRender 來自行輸出 PostBack 的用戶端指令碼,我們會用一個變數記錄 AutoPostBack 屬性,並強制將 AutoPostBack 屬性值設為 False,這是為了不要 MyBase 產生 PostBack 的指令碼;然後再自行輸出 AutoPostBack 用戶端指令碼,其中 __doPostBack 的 eventArgument 參數 (對應到 __EVENTARGUMENT 這個 HiddenField) 傳入 this.selectedIndex。</p> <pre><code>        Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)            Dim bAutoPostBack As Boolean            Dim sScript As String            '記錄 AutoPostBack 值,並將 AutoPostBack 設為 False,不要讓 MyBase 產生 PostBack 的指令碼            bAutoPostBack = Me.AutoPostBack            Me.AutoPostBack = False            MyBase.AddAttributesToRender(writer)            If bAutoPostBack Then                MyBase.Attributes.Remove(&quot;onchange&quot;)                sScript = String.Format(&quot;__doPostBack('{0}',{1})&quot;, Me.ClientID, &quot;this.selectedIndex&quot;)                writer.AddAttribute(HtmlTextWriterAttribute.Onchange, sScript)                Me.AutoPostBack = True            End If        End Sub </code></pre> <p>在頁面上放置一個 TBDropDownList 控制項,設定與上述案例相同的成員清單。</p> <pre><code>        &lt;bee:TBDropDownList ID=&quot;DropDownList2&quot; runat=&quot;server&quot; AutoPostBack=&quot;True&quot;&gt;            &lt;asp:ListItem Value=&quot;0&quot;&gt;張三&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;1&quot;&gt;李四&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;2&quot;&gt;王五&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;2&quot;&gt;陳六&lt;/asp:ListItem&gt;        &lt;/bee:TBDropDownList&gt; </code></pre> <p>執行程式查看 TBDropDownList 控制項的 HTML 原始碼,呼叫 __doPostBack 函式的參數已經被修改,eventArgument 參數會傳入該控制項的 selectedIndex。</p> <pre><code>&lt;select name=&quot;DropDownList2&quot; id=&quot;DropDownList2&quot; onchange=&quot;__doPostBack('DropDownList2',this.selectedIndex)&quot;&gt; &lt;option selected=&quot;selected&quot; value=&quot;0&quot;&gt;張三&lt;/option&gt; &lt;option value=&quot;1&quot;&gt;李四&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;王五&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;陳六&lt;/option&gt; &lt;/select&gt; </code></pre> <p>[超過字數限制,下一篇接續本文]</p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-30 21:15:23</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day28] 圖形驗證碼控制項(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10013365?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013365?sc=rss.iron</guid>                <description><![CDATA[<p>接續一上文<br /> <strong>二、實作圖形驗證碼控制項</strong><br /> 雖然我們可以使用 Image 控制項來呈現 ValidateCode.aspx 頁面產生的驗證碼圖...]]></description>                                    <content:encoded><![CDATA[<p>接續一上文<br /> <strong>二、實作圖形驗證碼控制項</strong><br /> 雖然我們可以使用 Image 控制項來呈現 ValidateCode.aspx 頁面產生的驗證碼圖形,可是這樣只處理一半的動作,因為沒有處理「使用者輸入的驗證碼」是否與「圖形驗證碼」相符,所以我們將實作一個圖形驗證碼控制項,來處理掉所有相關動作。<br /> 即然上面的示範使用 Image 控制項來呈現驗證碼,所以圖形驗證碼控制項就繼承 Image 命名為 TBValidateCode。</p> <pre><code>    &lt; _    Description(&quot;圖形驗證碼控制項&quot;), _    ToolboxData(&quot;&lt;{0}:TBValidateCode runat=server&gt;&lt;/{0}:TBValidateCode&gt;&quot;) _    &gt; _    Public Class TBValidateCode        Inherits System.Web.UI.WebControls.Image        End </code></pre> <p>新增 ValidateCodeUrl 屬性,設定圖形驗證碼產生頁面的網址。</p> <pre><code>        ''' &lt;summary&gt;        ''' 圖形驗證碼產生頁面網址。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;圖形驗證碼產生頁面網址&quot;), _        DefaultValue(&quot;&quot;) _        &gt; _        Public Property ValidateCodeUrl() As String            Get                Return FValidateCodeUrl            End Get            Set(ByVal value As String)                FValidateCodeUrl = value            End Set        End Property </code></pre> <p>覆寫 Render 方法,若未設定 ValidateCodeUrl 屬性,則預設為 ~/Page/ValidateCode.aspx 這個頁面。另外我們在圖形的 ondbclick 加上一段用戶端指令碼,其作用是讓用戶可以滑鼠二下來重新產生一個驗證碼圖形。</p> <pre><code>        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)            Dim sUrl As String            Dim sScript As String            sUrl = Me.ValidateCodeUrl            If String.IsNullOrEmpty(sUrl) Then                sUrl = &quot;~/Page/ValidateCode.aspx&quot;            End If            If Me.BorderWidth = Unit.Empty Then                Me.BorderWidth = Unit.Pixel(1)            End If            If Me.AlternateText = String.Empty Then                Me.AlternateText = &quot;圖形驗證碼&quot;            End If            Me.ToolTip = &quot;滑鼠點二下可重新產生驗證碼&quot;            Me.ImageUrl = sUrl            If Not Me.DesignMode Then                sScript = String.Format(&quot;this.src='{0}?flag='+Math.random();&quot;, Me.Page.ResolveClientUrl(sUrl))                Me.Attributes(&quot;ondblclick&quot;) = sScript            End If            Me.Style(HtmlTextWriterStyle.Cursor) = &quot;pointer&quot;            MyBase.Render(writer)        End Sub </code></pre> <p>另外新增一個 ValidateCode 方法,用來檢查輸入驗證碼是否正確。還記得我們在產生驗證碼圖形時,同時把該驗證碼的值寫入 Session(&quot;_ValidateCode&quot;) 中吧,所以這個方法只是把用戶輸入的值與 Seesion 中的值做比對。</p> <pre><code>        ''' &lt;summary&gt;        ''' 檢查輸入驗證碼是否正確。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Code&quot;&gt;輸入驗證碼。&lt;/param&gt;        ''' &lt;returns&gt;驗證成功傳回 True,反之傳回 False。&lt;/returns&gt;        Public Function ValidateCode(ByVal Code As String) As Boolean            If Me.Page.Session(SessionKey) Is Nothing Then Return False            If SameText(CCStr(Me.Page.Session(SessionKey)), Code) Then                Return True            Else                Return False            End If        End Function </code></pre> <p><strong>三、測試程式</strong><br /> 在頁面放置一個 TBValidateCode 控制項,另外加一個文字框及按鈕,供使用者輸入驗證碼後按下「確定」鈕後到伺服端做輸入值比對的動作。</p> <pre><code>        &lt;bee:TBValidateCode ID=&quot;TBValidateCode1&quot; runat=&quot;server&quot; /&gt;        &lt;bee:TBTextBox ID=&quot;txtCode&quot; runat=&quot;server&quot;&gt;&lt;/bee:TBTextBox&gt;        &lt;bee:TBButton ID=&quot;TBButton1&quot; runat=&quot;server&quot; Text=&quot;確定&quot; /&gt; </code></pre> <p>在「確定」鈕的 Click 事件中,我們使用 TBValidateCode 控制項的 ValidateCode 方法判斷驗證碼輸入的正確性。</p> <pre><code>    Protected Sub TBButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TBButton1.Click        If TBValidateCode1.ValidateCode(txtCode.Text) Then            Me.Response.Write(&quot;驗證碼輸入正確&quot;)        Else            Me.Response.Write(&quot;驗證碼輸入錯誤!&quot;)        End If    End Sub </code></pre> <p>執行程式,頁面就會隨機產生一個驗證碼圖形。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay28_1B7F/image_thumb.png" alt="" /></p> <p>輸入正確的值按「確定」鈕,就會顯示「驗證碼輸入正確」的訊息。因為我們在同一頁面測試的關係,你會發現 PostBack 後驗證碼圖形又會重新產生,一般正常的做法是驗證正確後就導向另一個頁面。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay28_1B7F/image_thumb_1.png" alt="" /></p> <p>當我們輸入錯誤的值,就會顯示「驗證碼輸入錯誤!」的訊息。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay28_1B7F/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/29/5818.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/29/5818.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-29 20:34:22</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day28] 圖形驗證碼控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10013361?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013361?sc=rss.iron</guid>                <description><![CDATA[<p>在網頁上常把圖形驗證碼應用在登入或貼文的頁面中,因為圖形驗證碼具有機器不易識別的特性,可以防止機器人程式惡意的存取網頁。在本文中將實作一個圖形驗證碼的伺服器控制項,透過簡單的屬性設定就可以輕易地...]]></description>                                    <content:encoded><![CDATA[<p>在網頁上常把圖形驗證碼應用在登入或貼文的頁面中,因為圖形驗證碼具有機器不易識別的特性,可以防止機器人程式惡意的存取網頁。在本文中將實作一個圖形驗證碼的伺服器控制項,透過簡單的屬性設定就可以輕易地在網頁上套用圖形驗證碼。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081029202355938.rar" target="_blank">ASP.NET Server Control - Day28.rar</a></p> <p><strong>一、產生圖形驗證碼</strong><br /> 我們先準備一個產生圖形驗證碼的頁面 (ValidateCode.aspx),這個頁面主要是繪製驗證碼圖形,並將其寫入記憶體資料流,最後使用 Response.BinaryWrite 將圖形輸出傳遞到用戶端。當我們輸出此驗證碼圖形的同時,會使用 Session(&quot;_ValidateCode&quot;) 來記錄驗證碼的值,以便後續與使用者輸入驗證碼做比對之用。</p> <pre><code>Partial Class ValidateCode    Inherits System.Web.UI.Page    ''' &lt;summary&gt;    ''' 產生圖形驗證碼。    ''' &lt;/summary&gt;    Public Function CreateValidateCodeImage(ByRef Code As String, ByVal CodeLength As Integer, _        ByVal Width As Integer, ByVal Height As Integer, ByVal FontSize As Integer) As Bitmap        Dim sCode As String = String.Empty        '顏色列表,用於驗證碼、噪線、噪點        Dim oColors As Color() = { _            Drawing.Color.Black, Drawing.Color.Red, Drawing.Color.Blue, Drawing.Color.Green, _            Drawing.Color.Orange, Drawing.Color.Brown, Drawing.Color.Brown, Drawing.Color.DarkBlue}        '字體列表,用於驗證碼        Dim oFontNames As String() = {&quot;Times New Roman&quot;, &quot;MS Mincho&quot;, &quot;Book Antiqua&quot;, _                                      &quot;Gungsuh&quot;, &quot;PMingLiU&quot;, &quot;Impact&quot;}        '驗證碼的字元集,去掉了一些容易混淆的字元        Dim oCharacter As Char() = {&quot;2&quot;c, &quot;3&quot;c, &quot;4&quot;c, &quot;5&quot;c, &quot;6&quot;c, &quot;8&quot;c, _                                    &quot;9&quot;c, &quot;A&quot;c, &quot;B&quot;c, &quot;C&quot;c, &quot;D&quot;c, &quot;E&quot;c, _                                    &quot;F&quot;c, &quot;G&quot;c, &quot;H&quot;c, &quot;J&quot;c, &quot;K&quot;c, &quot;L&quot;c, _                                    &quot;M&quot;c, &quot;N&quot;c, &quot;P&quot;c, &quot;R&quot;c, &quot;S&quot;c, &quot;T&quot;c, _                                    &quot;W&quot;c, &quot;X&quot;c, &quot;Y&quot;c}        Dim oRnd As New Random()        Dim oBmp As Bitmap        Dim oGraphics As Graphics        Dim N1 As Integer        Dim oPoint1 As Drawing.Point        Dim oPoint2 As Drawing.Point        Dim sFontName As String        Dim oFont As Font        Dim oColor As Color        '生成驗證碼字串        For N1 = 0 To CodeLength - 1            sCode += oCharacter(oRnd.Next(oCharacter.Length))        Next        oBmp = New Bitmap(Width, Height)        oGraphics = Graphics.FromImage(oBmp)        oGraphics.Clear(Drawing.Color.White)        Try            For N1 = 0 To 4                '畫噪線                oPoint1.X = oRnd.Next(Width)                oPoint1.Y = oRnd.Next(Height)                oPoint2.X = oRnd.Next(Width)                oPoint2.Y = oRnd.Next(Height)                oColor = oColors(oRnd.Next(oColors.Length))                oGraphics.DrawLine(New Pen(oColor), oPoint1, oPoint2)            Next            For N1 = 0 To sCode.Length - 1                '畫驗證碼字串                sFontName = oFontNames(oRnd.Next(oFontNames.Length))                oFont = New Font(sFontName, FontSize, FontStyle.Italic)                oColor = oColors(oRnd.Next(oColors.Length))                oGraphics.DrawString(sCode(N1).ToString(), oFont, New SolidBrush(oColor), CSng(N1) * FontSize + 10, CSng(8))            Next            For i As Integer = 0 To 30                '畫噪點                Dim x As Integer = oRnd.Next(oBmp.Width)                Dim y As Integer = oRnd.Next(oBmp.Height)                Dim clr As Color = oColors(oRnd.Next(oColors.Length))                oBmp.SetPixel(x, y, clr)            Next            Code = sCode            Return oBmp        Finally            oGraphics.Dispose()        End Try    End Function    ''' &lt;summary&gt;    ''' 產生圖形驗證碼。    ''' &lt;/summary&gt;    Public Sub CreateValidateCodeImage(ByRef MemoryStream As MemoryStream, _        ByRef Code As String, ByVal CodeLength As Integer, _        ByVal Width As Integer, ByVal Height As Integer, ByVal FontSize As Integer)        Dim oBmp As Bitmap        oBmp = CreateValidateCodeImage(Code, CodeLength, Width, Height, FontSize)        Try            oBmp.Save(MemoryStream, ImageFormat.Png)        Finally            oBmp.Dispose()        End Try    End Sub    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load        Dim sCode As String = String.Empty        '清除該頁輸出緩存,設置該頁無緩存        Response.Buffer = True        Response.ExpiresAbsolute = System.DateTime.Now.AddMilliseconds(0)        Response.Expires = 0        Response.CacheControl = &quot;no-cache&quot;        Response.AppendHeader(&quot;Pragma&quot;, &quot;No-Cache&quot;)        '將驗證碼圖片寫入記憶體流,並將其以 &quot;image/Png&quot; 格式輸出        Dim oStream As New MemoryStream()        Try            CreateValidateCodeImage(oStream, sCode, 4, 100, 40, 18)            Me.Session(&quot;_ValidateCode&quot;) = sCode            Response.ClearContent()            Response.ContentType = &quot;image/Png&quot;            Response.BinaryWrite(oStream.ToArray())        Finally            '釋放資源            oStream.Dispose()        End Try    End Sub End Class </code></pre> <p>我們將此頁面置於 ~/Page/ValidateCode.aspx,當要使用此頁面的圖形驗證碼,只需要在使用 Image 控制項,設定 ImageUrl 為此頁面即可。</p> <pre><code>&lt;asp:Image ID=&quot;imgValidateCode&quot; runat=&quot;server&quot; ImageUrl=&quot;~/Page/ValidateCode.aspx&quot; /&gt; </code></pre> <p>[超過字數限制,下一篇接續本文]</p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-29 20:31:45</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態(續2)</title>                <link>https://ithelp.ithome.com.tw/articles/10013241?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013241?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> 接下來設定做為新增、編輯使用的 TBFormView 控制項,我們只使用 EditItemTemplate 來同時處理新增、刪除,所以 EditItemTemplate ...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> 接下來設定做為新增、編輯使用的 TBFormView 控制項,我們只使用 EditItemTemplate 來同時處理新增、刪除,所以 EditItemTemplate 需要同時具有「新增」、「更新」、「取消」三個按鈕。其中 ProductID 為主索引欄位,所以我們使用 TBTextBox 來繫結 ProductID 欄位,設定 FormViewModeState.InsertMode=&quot;Enable&quot; 使控制項在新增模式時為可編輯,設定 FormViewModeState.EditMode=&quot;Disable&quot; 使控制項在修改模式是唯讀的。</p> <pre><code>        &lt;bee:TBFormView ID=&quot;TBFormView1&quot; runat=&quot;server&quot; DataKeyNames=&quot;ProductID&quot; DataSourceID=&quot;SqlDataSource1&quot;            DefaultMode=&quot;Edit&quot; SingleTemplate=&quot;EditItemTemplate&quot; BackColor=&quot;White&quot; BorderColor=&quot;#CCCCCC&quot;            BorderStyle=&quot;None&quot; BorderWidth=&quot;1px&quot; CellPadding=&quot;3&quot; GridLines=&quot;Both&quot; Visible=&quot;False&quot;&gt;            &lt;FooterStyle BackColor=&quot;White&quot; ForeColor=&quot;#000066&quot; /&gt;            &lt;RowStyle ForeColor=&quot;#000066&quot; /&gt;            &lt;EditItemTemplate&gt;                ProductID:                &lt;bee:TBTextBox ID=&quot;TextBox1&quot; runat=&quot;server&quot; Text='&lt;%# Bind(&quot;ProductID&quot;) %&gt;'&gt;                  &lt;FormViewModeState EditMode=&quot;Disable&quot; InsertMode=&quot;Enable&quot;&gt;                  &lt;/FormViewModeState&gt;                &lt;/bee:TBTextBox&gt;                '省略                &lt;asp:LinkButton ID=&quot;LinkButton1&quot; runat=&quot;server&quot; CausesValidation=&quot;True&quot; CommandName=&quot;Insert&quot;                    Text=&quot;新增&quot; /&gt;                 &lt;asp:LinkButton ID=&quot;UpdateButton&quot; runat=&quot;server&quot; CausesValidation=&quot;True&quot; CommandName=&quot;Update&quot;                    Text=&quot;更新&quot; /&gt;                 &lt;asp:LinkButton ID=&quot;UpdateCancelButton&quot; runat=&quot;server&quot; CausesValidation=&quot;False&quot;                    CommandName=&quot;Cancel&quot; Text=&quot;取消&quot; /&gt;            &lt;/EditItemTemplate&gt;        &lt;/bee:TBFormView&gt; </code></pre> <p><strong>2. 測試新增模式</strong><br /> 接下來執行程式,一開始為瀏覽模式,以 TBGridView 來呈現資料。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_2.png" alt="" /></p> <p>按下 Header 的「新增」鈕,就會隱藏 TBGridView,而切換到 TBFormView 的新增模式。其中繫結 ProductID 欄位的 TBTextBox 為可編輯模式,而下方的按鈕只會顯示「新增」及「取消」鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_3.png" alt="" /></p> <p>在新增模式輸入完畢後,按下「新增」鈕,資料錄就會被寫入資料庫。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_4.png" alt="" /></p> <p><strong>3. 測試修改模式</strong><br /> 接下來測試修改模式,按下「編輯」鈕,就會隱藏 TBGridView,而切換到 TBFormView 的修改模式。其中繫結 ProductID 欄位的 TBTextBox 為唯讀模式,而下方的按鈕只會顯示「更新」及「取消」鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_5.png" alt="" /></p> <p>在修改模式輸入完畢後,按下「更新」鈕,資料錄就會被寫入資料庫。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_6.png" alt="" /></p> <p><strong>4. 頁面程式碼</strong><br /> 示範了上述的操作後,接下來我們回頭看一下頁面的程式碼。你沒看錯,筆者也沒貼錯,真的是一行程式碼都沒有,因為所有相關動作都由控制項處理掉了。</p> <pre><code>Partial Class Day27    Inherits System.Web.UI.Page End Class </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-28 13:57:23</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態(續1)</title>                <link>https://ithelp.ithome.com.tw/articles/10013239?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013239?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>二、讓 TextBox 控制項可自行維護狀態</strong><br /> 接下來擴展 TextBox 控制項,繼承 TextBox 命名為 TBText...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>二、讓 TextBox 控制項可自行維護狀態</strong><br /> 接下來擴展 TextBox 控制項,繼承 TextBox 命名為 TBTextBox。新增 FormViewModeState 屬性 (TBFormViewModeState 型別),依 FormView Mode 來設定控制項狀。並覆寫 PreRender 方法,在此方法中呼叫 DoFormViewModeStatus 私有方法,依 FormView 的模式來處理控制項狀態。</p> <pre><code>    ''' &lt;summary&gt;    ''' 文字框控制項。    ''' &lt;/summary&gt;    &lt; _    Description(&quot;文字框控制項。&quot;), _    ToolboxData(&quot;&lt;{0}:TBTextBox runat=server&gt;&lt;/{0}:TBTextBox&gt;&quot;) _    &gt; _    Public Class TBTextBox        Inherits TextBox        Private FFormViewModeState As TBFormViewModeState        ''' &lt;summary&gt;        ''' 依 FormViewMode 來設定控制項狀態。        ''' &lt;/summary&gt;        &lt; _        Category(WebCommon.Category.Behavior), _        NotifyParentProperty(True), _        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _        PersistenceMode(PersistenceMode.InnerProperty), _        DefaultValue(&quot;&quot;) _        &gt; _        Public ReadOnly Property FormViewModeState() As TBFormViewModeState            Get                If FFormViewModeState Is Nothing Then                    FFormViewModeState = New TBFormViewModeState                End If                Return FFormViewModeState            End Get        End Property        ''' &lt;summary&gt;        ''' 處理控制項狀態。        ''' &lt;/summary&gt;        Private Sub DoControlStatus(ByVal ControlStatus As EControlState)            Select Case ControlStatus                Case EControlState.Enable                    Me.Enabled = True                Case EControlState.Disable                    Me.Enabled = False                Case EControlState.Hide                    Me.Visible = False            End Select        End Sub        ''' &lt;summary&gt;        ''' 依 FormView 的模式來處理控制項狀態。        ''' &lt;/summary&gt;        Private Sub DoFormViewModeStatus()            Dim oFormView As FormView            '若控制項置於 FormView 中,則依 FormView 的模式來處理控制項狀態            If TypeOf Me.BindingContainer Is FormView Then                oFormView = DirectCast(Me.BindingContainer, FormView)                Select Case oFormView.CurrentMode                    Case FormViewMode.Insert                        DoControlStatus(Me.FormViewModeState.InsertMode)                    Case FormViewMode.Edit                        DoControlStatus(Me.FormViewModeState.EditMode)                    Case FormViewMode.ReadOnly                        DoControlStatus(Me.FormViewModeState.BrowseMode)                End Select            End If        End Sub        ''' &lt;summary&gt;        ''' 覆寫。引發 PreRender 事件。        ''' &lt;/summary&gt;        Protected Overrides Sub OnPreRender(ByVal e As EventArgs)            MyBase.OnPreRender(e)            '依 FormView 的模式來處理控制項狀態            DoFormViewModeStatus()        End Sub    End Class </code></pre> <p><strong>三、測試程式</strong><br /> <strong>1. 設定控制項相關屬性</strong><br /> 我們使用 Northwnd 資料庫的 Products資料表為例,以 GridView+FormView 示範資料「新增/修改/刪除」的操作。在頁面拖曳 SqlDataSource 控制項後,在頁面上的使用 TBGridView 來顯示瀏覽資料。TBGridView 的 FormViewID 設為關連的 TBFormVIew 控制項;另外有使用到 TBCommandField,設定 ShowHeaderNewButton=True,讓命令列具有「新增」鈕。</p> <pre><code>        &lt;bee:TBGridView ID=&quot;TBGridView1&quot; runat=&quot;server&quot; AutoGenerateColumns=&quot;False&quot; DataKeyNames=&quot;ProductID&quot;            DataSourceID=&quot;SqlDataSource1&quot; FormViewID=&quot;TBFormView1&quot;&gt;            &lt;Columns&gt;                &lt;bee:TBCommandField ShowDeleteButton=&quot;True&quot; ShowEditButton=&quot;True&quot;                    ShowHeaderNewButton=&quot;True&quot; &gt;                &lt;/bee:TBCommandField&gt;                                '省略                            &lt;/Columns&gt;        &lt;/bee:TBGridView&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-28 13:53:32</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態</title>                <link>https://ithelp.ithome.com.tw/articles/10013233?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013233?sc=rss.iron</guid>                <description><![CDATA[<p>在 <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/12/3936.aspx" target="_blank">GridV...]]></description>                                    <content:encoded><![CDATA[<p>在 <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/12/3936.aspx" target="_blank">GridView+FormView 示範資料 新增/修改/刪除(進階篇:伺服器控制項)</a> 一文中,示範了擴展 GridView 及 FormView 控制項,讓 GridView 可以透過屬性與 FormView 做關連來處理資料的「新增/修改/刪除」的動作。因為在該案例中,只使用 FormView 的 EditTemplate 同時處理「新增」及「修改」的動作,所以還需要自行撰寫部分程式碼去判斷控制項在新增或修改的啟用狀態,例如編號欄位在新增時為啟用,修改時就不啟用。在該文最後也提及其實有辨法讓這個案例達到零程式碼的目標,那就是讓控制項 (如 TextBox) 自行判斷所在的 FormView 的 CurrentMode,自行決定本身是否要「啟用/不啟用」、「顯示/隱藏」等狀態。本文以 TextBox 為例,說明如何修改 TextBox 讓它可以達到上述的需求。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081028133154164.rar" target="_blank">ASP.NET Server Control - Day27.rar</a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、TBFormViewModeState 類別</strong><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb.png" alt="" /><br /> 我們先定義 EControlState (控制項狀態) 列舉,描述控制項在特定模式的狀態為何。</p> <pre><code>    ''' &lt;summary&gt;    ''' 控制項狀態列舉。    ''' &lt;/summary&gt;    Public Enum EControlState        ''' &lt;summary&gt;        ''' 不設定。        ''' &lt;/summary&gt;        NotSet = 0        ''' &lt;summary&gt;        ''' 啟用。        ''' &lt;/summary&gt;        Enable = 1        ''' &lt;summary&gt;        ''' 不啟用。        ''' &lt;/summary&gt;        Disable = 2        ''' &lt;summary&gt;        ''' 隱藏。        ''' &lt;/summary&gt;        Hide = 3    End Enum </code></pre> <p>再來定義 TBFormViewModeState 類別,用來設定控制項在各種 FormView 模式 (瀏覽、新增、修改) 中的控制項狀態。</p> <pre><code>''' &lt;summary&gt; ''' 依 FormViewMode 來設定控制項狀態。 ''' &lt;/summary&gt; &lt; _ Serializable(), _ TypeConverter(GetType(ExpandableObjectConverter)) _ &gt; _ Public Class TBFormViewModeState    Private FInsertMode As EControlState = EControlState.NotSet    Private FEditMode As EControlState = EControlState.NotSet    Private FBrowseMode As EControlState = EControlState.NotSet    ''' &lt;summary&gt;    ''' 在新增模式(FormViewMode=Insert)的控制項狀態。    ''' &lt;/summary&gt;    &lt; _    NotifyParentProperty(True), _    DefaultValue(GetType(EControlState), &quot;NotSet&quot;) _    &gt; _    Public Property InsertMode() As EControlState        Get            Return FInsertMode        End Get        Set(ByVal value As EControlState)            FInsertMode = value        End Set    End Property    ''' &lt;summary&gt;    ''' 在編輯模式(FormViewMode=Edit)的控制項狀態。    ''' &lt;/summary&gt;    &lt; _    NotifyParentProperty(True), _    DefaultValue(GetType(EControlState), &quot;NotSet&quot;) _    &gt; _    Public Property EditMode() As EControlState        Get            Return FEditMode        End Get        Set(ByVal value As EControlState)            FEditMode = value        End Set    End Property    ''' &lt;summary&gt;    ''' 在瀏覽模式(FormViewMode=ReadOnly)的控制項狀態。    ''' &lt;/summary&gt;    &lt; _    NotifyParentProperty(True), _    DefaultValue(GetType(EControlState), &quot;NotSet&quot;) _    &gt; _    Public Property BrowseMode() As EControlState        Get            Return FBrowseMode        End Get        Set(ByVal value As EControlState)            FBrowseMode = value        End Set    End Property End Class </code></pre> <p>定義為 TBFormViewModeState 型別的屬性是屬於複雜屬性,要套用 TypeConverter(GetType(ExpandableObjectConverter)),讓該屬性可在屬性視窗 (PropertyGrid) 擴展以便設定屬性值,如下圖所示。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_1.png" alt="" /></p> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-28 13:45:43</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day26] 讓你的 GridView 與眾不同</title>                <link>https://ithelp.ithome.com.tw/articles/10013209?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013209?sc=rss.iron</guid>                <description><![CDATA[<p>在網路上可以找到相當多擴展 GridView 控制項功能的文章,在筆者的部落格中也有多篇提及擴展 GridView、DataControlField、BoundFIeld 功能的相關文章,在本文...]]></description>                                    <content:encoded><![CDATA[<p>在網路上可以找到相當多擴展 GridView 控制項功能的文章,在筆者的部落格中也有多篇提及擴展 GridView、DataControlField、BoundFIeld 功能的相關文章,在本文將這些關於擴展 GridView 控制項功能及欄位類別的相關文章做一整理簡介,若需要擴展 GridView 相關功能時可以做為參考。<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/22/4105.aspx" target="_blank"><strong>1. 擴展 GridView 控制項 - 無資料時顯示標題列</strong></a><br /> 摘要:當 GridView 繫結的 DataSource 資料筆數為 0 時,會依 EmptyDataTemplate 及 EmptyDataText 的設定來顯示無資料的狀態。若我們希望 GridView 在無資料時,可以顯示欄位標題,有一種作法是在 EmptyDataTemplate 中手動在設定一個標題列,不過這種作法很麻煩。本文擴展 GridView 控制項,直接透過屬性設定就可以在無資料顯示欄位標題。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/GridView_DAA6/image_thumb.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/17/4028.aspx" target="_blank"><strong>2. 擴展 GridView 控制項 - 支援 Excel 及 Word 匯出</strong></a><br /> 摘要:GridView 匯出 Excel 及 Word 文件是蠻常使用的需求,此篇文章將擴展 GridView 控制項提供匯出 Excel 及 Word 文件的方法。一般在 GridView 匯出的常見下列問題也會在此一併被解決。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/GridViewExcelWord_14C22/image_thumb_1.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/GridViewExcelWord_14C22/image_thumb_2.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/12/3936.aspx" target="_blank"><strong>3. GridView+FormView 示範資料 新增/修改/刪除(進階篇:伺服器控制項)</strong></a><br /> 摘要:擴展 GridView 及 FormView 控制項,在 GridView 控制項中新增 FormViewID 屬性,關連至指定的 FormView 控制項 ID,就可以讓 GridView 結合 FormView 來做資料異動的動作。</p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1766.aspx" target="_blank"><strong>4. 擴展 CommandField 類別 - 刪除提示訊息</strong></a><br /> 摘要:新增 DeleteConfirmMessage 屬性,設定刪除提示確認訊息。<br /> <img src="http://blog.blueshop.com.tw/images/blog_blueshop_com_tw/jeff377/1525/r_Ex19.1.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1767.aspx" target="_blank"><strong>5. 擴展 CommandField 類別 - 刪除提示訊息含欄位值</strong></a><br /> 摘要:設定刪除提示確認訊息中可包含指定 DataField 欄位值,明確提示要刪除的資料列。<br /> <img src="http://blog.blueshop.com.tw/images/blog_blueshop_com_tw/jeff377/1525/r_Ex20.1.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1710.aspx" target="_blank"><strong>6. 讓 CheckBoxField 繫結非布林值(0 或 1)欄位</strong></a><br /> 摘要:CheckBoxField 若繫結的欄位值為 0 或 1 時 (非布林值) 會發生錯誤,本文擴展 CheckBoxField 類別,讓 CheckBoxField 有辨法繫結 0 或 1 的欄位值。</p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/21/4093.aspx" target="_blank"><strong>7. 擴展 CheckBoxField 類別 - 支援非布林值的雙向繫結</strong></a><br /> 摘要:CheckBoxField 繫結的欄位值並無法直接使用 CBool 轉型為布林值,例如 &quot;T/F&quot;、&quot;是/否&quot; 之類的資料,若希望使用 CheckBoxField 來顯示就比較麻煩,一般的作法都是轉為 TemplateField,自行撰寫資料繫結的函式,而且只能支援單向繫結。在本文直接改寫 CheckBoxField 類別,讓 CheckBoxField 可以直接雙向繫結 &quot;T/F&quot; 或 &quot;是/否&quot; 之類的資料。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/CheckBoxField_1471C/image_thumb.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/13/3969.aspx" target="_blank"><strong>8. 擴展 CommandField 類別 - Header 加入新增鈕</strong></a><br /> 摘要:支援在 CommandField 的 Header 的部分加入「新增」鈕,執行新增鈕會引發 RowCommand 事件。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/CommandFieldHeader_13B3E/image_4.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/29/4169.aspx" target="_blank"><strong>9. GridView 自動編號欄位 - TBSerialNumberField</strong></a><br /> 摘要:繼承 DataControlField 來撰寫自動編號欄位,若 GridView 需要自動編號欄位時只需加入欄位即可。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/GridViewTBSerialNumberField_13347/image_2.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank"><strong>10. 自訂 GridVie 欄位類別 - 實作 TBDropDownField 欄位類別</strong></a><br /> 摘要:支援在 GridView 中顯示下拉清單的欄位類別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb_1.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/26/5777.aspx" target="_blank"><strong>11. 自訂 GridView 欄位 - 日期欄位</strong></a><br /> 摘要:支援在 GridView 中顯示日期下拉選單編輯的欄位類別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay25TBDropDownFieldItems_6B94/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/27/5793.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/27/5793.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-27 22:37:14</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day25] 自訂 GridView 欄位 - 日期欄位(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10013091?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013091?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>四、覆寫 ExtractValuesFromCell 方法 - 擷取儲存格的欄位值</strong><br /> 當用戶端使用 GridView 編輯後執...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>四、覆寫 ExtractValuesFromCell 方法 - 擷取儲存格的欄位值</strong><br /> 當用戶端使用 GridView 編輯後執行更新動作時,會呼叫 ExtractValuesFromCell 方法,來取得儲存格的欄位值,以便寫入資料來源。所以我們要覆寫 ExtractValuesFromCell 方法,將 Cell 或 TDateEdit 控制項的值取出填入具 IOrderedDictionary 介面的物件。</p> <pre><code>        ''' &lt;summary&gt;        ''' 使用指定 DataControlFieldCell 的值填入指定的 IDictionary 物件。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Dictionary&quot;&gt;用於儲存指定儲存格的值。&lt;/param&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;包含要擷取值的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列的狀態。&lt;/param&gt;        ''' &lt;param name=&quot;IncludeReadOnly&quot;&gt;true 表示包含唯讀欄位的值,否則為 false。&lt;/param&gt;        Public Overrides Sub ExtractValuesFromCell( _            ByVal Dictionary As IOrderedDictionary, _            ByVal Cell As DataControlFieldCell, _            ByVal RowState As DataControlRowState, _            ByVal IncludeReadOnly As Boolean)            Dim oControl As Control = Nothing            Dim sDataField As String = Me.DataField            Dim oValue As Object = Nothing            Dim sNullDisplayText As String = Me.NullDisplayText            Dim oDateEdit As TBDateEdit            If (((RowState And DataControlRowState.Insert) = DataControlRowState.Normal) OrElse Me.InsertVisible) Then                If (Cell.Controls.Count &gt; 0) Then                    oControl = Cell.Controls.Item(0)                    oDateEdit = TryCast(oControl, TBDateEdit)                    If (Not oDateEdit Is Nothing) Then                        oValue = oDateEdit.Text                    End If                ElseIf IncludeReadOnly Then                    Dim s As String = Cell.Text                    If (s = &quot; &quot;) Then                        oValue = String.Empty                    ElseIf (Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) Then                        oValue = HttpUtility.HtmlDecode(s)                    Else                        oValue = s                    End If                End If                If (Not oValue Is Nothing) Then                    If TypeOf oValue Is String Then                        If (CStr(oValue).Length = 0) AndAlso Me.ConvertEmptyStringToNull Then                            oValue = Nothing                        ElseIf (CStr(oValue) = sNullDisplayText) AndAlso (sNullDisplayText.Length &gt; 0) Then                            oValue = Nothing                        End If                    End If                    If Dictionary.Contains(sDataField) Then                        Dictionary.Item(sDataField) = oValue                    Else                        Dictionary.Add(sDataField, oValue)                    End If                End If            End If        End Sub </code></pre> <p><strong>五、測試程式</strong><br /> 我們使用 Northwnd 資料庫的 Employees 資料表為例,在 GridView 加入自訂的 TBDateField 欄位繫結 BirthDate 欄位,另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 BirthDate 欄位來做比較。</p> <pre><code>            &lt;bee:TBDateField DataField=&quot;BirthDate&quot; HeaderText=&quot;BirthDate&quot;                SortExpression=&quot;BirthDate&quot; DataFormatString=&quot;{0:d}&quot;                ApplyFormatInEditMode=&quot;True&quot; CalendarStyle=&quot;Winter&quot; /&gt;                        &lt;asp:BoundField DataField=&quot;BirthDate&quot; HeaderText=&quot;BirthDate&quot;                SortExpression=&quot;BirthDate&quot; DataFormatString=&quot;{0:d}&quot;                ApplyFormatInEditMode=&quot;True&quot; ReadOnly=&quot;true&quot; /&gt; </code></pre> <p>執行程式,在編輯資料列時,TBDateField 就會以 TDateEdit 控制項來進行編輯。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay25TBDropDownFieldItems_6B94/image_thumb_2.png" alt="" /></p> <p>使用 TDateEdit 編輯欄位值後,按「更新」鈕,資料就會被寫回資料庫。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay25TBDropDownFieldItems_6B94/image_thumb_3.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/26/5777.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/26/5777.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-26 17:04:50</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day25] 自訂 GridView 欄位 - 日期欄位</title>                <link>https://ithelp.ithome.com.tw/articles/10013083?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013083?sc=rss.iron</guid>                <description><![CDATA[<p>前二篇文章介紹了自訂 GridView 使用的下拉清單欄位 (TBDropDownField),對如何繼承 BoundField 類別下來改寫自訂欄位應該有進一步的了解。在 GridView 中...]]></description>                                    <content:encoded><![CDATA[<p>前二篇文章介紹了自訂 GridView 使用的下拉清單欄位 (TBDropDownField),對如何繼承 BoundField 類別下來改寫自訂欄位應該有進一步的了解。在 GridView 中輸入日期也常蠻常見的需求,在本文將再實作一個 GridView 使用的日期欄位,在欄位儲存格使用 TBDateEdit 控制項來編輯資料。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081026164615771.rar" target="_blank">ASP.NET Server Control - Day25.rar</a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、繼承 TBBaseBoundField 實作 TDateField</strong><br /> GridView 的日期欄位需要繫結資料,一般的作法是由 BoundField 繼承下來改寫;不過我們之前已經有繼承 BoundField 製作一個 TBBaseBoundField 的自訂欄位基底類別 (詳見「 <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">[ASP.NET 控制項實作 Day23] 自訂 GridVie 欄位類別 - 實作 TBDropDownField 欄位類別</a>」 一文),所以我們要實作的日期欄位直接繼承 TBBaseBoundField 命名為 TDateField,並覆寫 CreateField 方法,傳回 TDateField 物件。</p> <pre><code>    ''' &lt;summary&gt;    ''' 日期欄位。    ''' &lt;/summary&gt;    Public Class TBDateField        Inherits TBBaseBoundField        Protected Overrides Function CreateField() As DataControlField            Return New TBDateField()        End Function    End Class </code></pre> <p>自訂欄位類別主要是要覆寫 InitializeDataCell 方法做資料儲存格初始化、覆寫 OnDataBindField 方法將欄位值繫結至 BoundField 物件、覆寫 ExtractValuesFromCell 方法來擷取儲存格的欄位值,下面我們將針對這幾個需要覆寫的方法做一說明。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay25TBDropDownFieldItems_6B94/image_thumb.png" alt="" /></p> <p><strong>二、覆寫 InitializeDataCell 方法 - 資料儲存格初始化</strong><br /> 首先覆寫 InitializeDataCell 方法處理資料儲存格初始化,當唯讀狀態時使用 Cell 來呈現資料;若為編輯狀態時,則在 Cell 中加入 TBDateEdit 控制項,並將 TBDateField 的屬性設定給 TBDateEdit 控制項的相關屬性。然後將儲存格 (DataControlFieldCell) 或日期控制項 (TDateEdit) 的 DataBinding 事件導向 OnDataBindField 事件處理方法。</p> <pre><code>        ''' &lt;summary&gt;        ''' 資料儲存格初始化。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;要初始化的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Protected Overrides Sub InitializeDataCell(ByVal Cell As DataControlFieldCell, ByVal RowState As DataControlRowState)            Dim oDateEdit As TBDateEdit            Dim oControl As Control            If Me.CellIsEdit(RowState) Then                '編輯狀態在儲存格加入 TBDateEdit 控制項                oDateEdit = New TBDateEdit()                oDateEdit.FirstDayOfWeek = Me.FirstDayOfWeek                oDateEdit.ShowWeekNumbers = Me.ShowWeekNumbers                oDateEdit.CalendarStyle = Me.CalendarStyle                oDateEdit.Lang = Me.Lang                oDateEdit.ShowTime = Me.ShowTime                oControl = oDateEdit                Cell.Controls.Add(oControl)            Else                oControl = Cell            End If            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)            End If        End Sub </code></pre> <p>TDateEdit 控制項為筆者自行撰寫的日期控制項,TDateEdit 控制項的相關細節可以參考筆者部落格下面幾篇文章有進一步說明。<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1742.aspx" target="_blank">日期控制項實作教學(1) - 結合 JavaScript</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1743.aspx" target="_blank">日期控制項實作教學(2) - PostBack 與 事件</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1746.aspx" target="_blank">TBDateEdit 日期控制項 - 1.0.0.0 版 (Open Source)</a><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay21_6429/image_thumb_1.png" alt="" /></p> <p><strong>三、覆寫 OnDataBindField 方法 - 將欄位值繫結至 BoundField 物件</strong><br /> 當 GridView 執行 DataBind 時,每個儲存格的 DataBinding 事件都會被導向 OnDataBindField 方法,此方法中我們會由資料來源取得指定欄位值,處理此欄位值的格式化時,將欄位值呈現在 Cell 或 TDateEdit 控制項上。</p> <pre><code>        ''' &lt;summary&gt;        ''' 將欄位值繫結至 BoundField 物件。        ''' &lt;/summary&gt;        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)            Dim oControl As Control            Dim oDateEdit As TBDateEdit            Dim oNamingContainer As Control            Dim oDataValue As Object            '欄位值            Dim bEncode As Boolean              '是否編碼            Dim sText As String                 '格式化字串            oControl = DirectCast(sender, Control)            oNamingContainer = oControl.NamingContainer            oDataValue = Me.GetValue(oNamingContainer)            bEncode = ((Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) AndAlso TypeOf oControl Is TableCell)            sText = Me.FormatDataValue(oDataValue, bEncode)            If TypeOf oControl Is TableCell Then                If (sText.Length = 0) Then                    sText = &quot; &quot;                End If                DirectCast(oControl, TableCell).Text = sText            Else                If Not TypeOf oControl Is TBDateEdit Then                    Throw New HttpException(String.Format(&quot;{0}: Wrong Control Type&quot;, Me.DataField))                End If                oDateEdit = DirectCast(oControl, TBDateEdit)                If Me.ApplyFormatInEditMode Then                    oDateEdit.Text = sText                ElseIf (Not oDataValue Is Nothing) Then                    oDateEdit.Text = oDataValue.ToString                End If            End If        End Sub </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-26 16:56:36</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day24] TBDropDownField 的 Items 屬性的資料繫結(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10013047?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013047?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>三、由關連的資料來源擷取資料</strong><br /> 再來就是重點就是要處理 PerformSelecrt 私有方法,來取得 Items 屬性的成員...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>三、由關連的資料來源擷取資料</strong><br /> 再來就是重點就是要處理 PerformSelecrt 私有方法,來取得 Items 屬性的成員清單內容。PerformSelect 方法的作用是去尋找頁面上的具 IDataSource 介面的控制項,並執行此資料來源的 Select 方法,以取得資料來設定 Items 的清單內容。<br /> <strong>step1. 尋找資料來源控制項</strong><br /> PerformSelect 方法中有使用 FindControlEx 方法,它是自訂援尋控制項的多載方法,是取代 FindControl 進階方法。程式碼中使用 FindControlEx 去是頁面中以遞迴方式尋找具有 IDataSource 介面的控制項,且 ID 屬性值為 TBDropDownList.ID 的屬性值。<br /> <strong>step2. 執行資料來源控制項的 Select 方法</strong><br /> 當找到資料來源控制項後 (如 SqlDataSource、ObjectDataSource ...等等),執行其 DataSourceView.Select 方法,此方法需入一個 DataSourceViewSelectCallback 函式當作參數,當資料來源控制項取得資料後回呼我們指定的 OnDataSourceViewSelectCallback 函式中做後序處理。<br /> <strong>step3. 將取得的資料來設定生 Items 的清單內容</strong><br /> 在 OnDataSourceViewSelectCallback 函式中接到回傳的具 IEnumerable 介面的資料,有可能是 DataView、DataTable ...等型別的資料。利用 DataBinder.GetPropertyValue 來取得 DataTextField 及 DataValueField 設定的欄位值,逐一建立 ListItem 項目,並加入 Items 集合屬性中。</p> <pre><code>        ''' &lt;summary&gt;        ''' 從關聯的資料來源擷取資料。        ''' &lt;/summary&gt;        Private Sub PerformSelect()            Dim oControl As Control            Dim oDataSource As IDataSource            Dim oDataSourceView As DataSourceView            '若未設定 DataSourceID 屬性則離開            If StrIsEmpty(Me.DataSourceID) Then Exit Sub            '找到具 IDataSource 介面的控制項            oControl = FindControlEx(Me.Control.Page, GetType(IDataSource), &quot;ID&quot;, Me.DataSourceID)            If oControl Is Nothing Then Exit Sub            oDataSource = DirectCast(oControl, IDataSource)            oDataSourceView = oDataSource.GetView(String.Empty)            oDataSourceView.Select(DataSourceSelectArguments.Empty, _                        New DataSourceViewSelectCallback(AddressOf Me.OnDataSourceViewSelectCallback))        End Sub        ''' &lt;summary&gt;        ''' 擷取資料的回呼函式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;data&quot;&gt;取得的資料。&lt;/param&gt;        Private Sub OnDataSourceViewSelectCallback(ByVal data As IEnumerable)            Dim oCollection As ICollection            Dim oValue As Object            Dim oItem As ListItem            Me.Items.Clear()            If data Is Nothing Then Exit Sub            oCollection = TryCast(data, ICollection)            Me.Items.Capacity = oCollection.Count            For Each oValue In data                oItem = New ListItem()                If StrIsNotEmpty(Me.DataTextField) Then                    oItem.Text = DataBinder.GetPropertyValue(oValue, DataTextField, Nothing)                End If                If StrIsNotEmpty(Me.DataValueField) Then                    oItem.Value = DataBinder.GetPropertyValue(oValue, DataValueField, Nothing)                End If                Me.Items.Add(oItem)            Next        End Sub </code></pre> <p><strong>四、測試程式</strong><br /> 使用上篇中同一個案例做測試,同樣以 Northwnd 資料庫的 Products 資料表為例。在 GridView 加入自訂的 TBDropDownField 欄位繫結 CategoryID 欄位,並設定 DataSourceID、DataTextField、DataValueField 屬性;另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 CategoryID 欄位來做比較。</p> <pre><code>                &lt;bee:TBDropDownField  HeaderText=&quot;CategoryID&quot;                      SortExpression=&quot;CategoryID&quot; DataField=&quot;CategoryID&quot;                    DataTextField=&quot;CategoryName&quot; DataValueField=&quot;CategoryID&quot; DataSourceID=&quot;SqlDataSource2&quot;&gt;                &lt;/bee:TBDropDownField&gt;                &lt;asp:BoundField DataField=&quot;CategoryID&quot; HeaderText=&quot;CategoryID&quot;                    SortExpression=&quot;CategoryID&quot;  ReadOnly=&quot;true&quot; /&gt; </code></pre> <p>執行程式,在 GridView 瀏覽的模式時,TBDropDownField 的儲存格已經會呈現 Items 對應成員的顯示文字。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay24TBDropDownField_73B8/image_thumb_1.png" alt="" /></p> <p>執行資料列編輯時,也可以正常顯示下拉清單的內容。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay24TBDropDownField_73B8/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-25 18:11:28</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day24] TBDropDownField 的 Items 屬性的資料繫結</title>                <link>https://ithelp.ithome.com.tw/articles/10013041?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013041?sc=rss.iron</guid>                <description><![CDATA[<p>上篇中我們實作了 GridView 的 TBDropDownField 欄位類別,不過眼尖的讀者不知有沒有發覺我們並處理 Items 屬性取得成員清單的動作,而是直接設定儲存格內含的 TBDro...]]></description>                                    <content:encoded><![CDATA[<p>上篇中我們實作了 GridView 的 TBDropDownField 欄位類別,不過眼尖的讀者不知有沒有發覺我們並處理 Items 屬性取得成員清單的動作,而是直接設定儲存格內含的 TBDropDownList 控制項相關屬性 (DataSourceID、DataTextField、DataValueField 屬性) 後,就由 TDropDownList 控制項自行處理 Items 屬性的資料繫結。當 GridView 的資料列是編輯狀態時,下拉清單會顯示出 Items 的文字內容;可是瀏覽狀態的資料列,卻是顯示欄位原始值,無法呈現 Items 的文字內容。本文將說明如何自行處理 TBDropDownField 的 Items 屬性的資料繫結動作,並使唯讀狀態的資料列也可以呈現 Items 的文字內容。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay24TBDropDownField_73B8/image_thumb.png" alt="" /><br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081025163920497.rar" target="_blank">ASP.NET Server Control - Day24.rar</a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、Items 屬性的問題</strong><br /> 我們重新看一次原本 TBDropDownField 類別在處理 Items 屬性的資料繫結取得清單內容的程式碼,在覆寫 InitializeDataCell 方法中,當儲存格為編輯模式時,會呈現 TBDropDownList 控制項並設定取得 Items 清單內容的相關屬性,讓 TBDropDownList 自行去處理它的 Items 屬性的清單內容。</p> <pre><code>'由資料來源控制項取得清單項目 oDropDownList.DataSourceID = Me.DataSourceID oDropDownList.DataTextField = Me.DataTextField oDropDownList.DataValueField = Me.DataValueField </code></pre> <p>不知你有沒有發覺,我們無論在 InitializeDataCell 及 OnDataBindField 方法中,都沒有針對 TBDropDownList 控制項做任何 DataBind 動作,那它是怎麼從 DataSourceID 關聯的資料來源擷取資料呢?因為 GridView 在執行 DataBind 時,就會要求所有的子控制項做 DataBind,所以我們只要設定好 BDropDownList 控制項相關屬性後,當 TBDropDownList 自動被要求資料繫結時就會取得 Items 的清單內容。<br /> 當然使用 TBDropDownList 控制項去處理 Items 的資料繫結動作最簡單,可是這樣唯讀的儲存格只能顯示原始欄位值,無法呈現 Items 中對應成員的文字;除非無論唯讀或編輯狀態,都要建立 TBDropDownList 控制項去取得 Items 清單內容,而唯讀欄位使用 TBDropDownList.Items 去找到對應成員的顯示文字,不過這樣的作法會怪怪的,而且沒有執行效能率。所以比較好的辨法,就是由 TBDropDownField 類別自行處理 Items 的資料繫結,同時提供給唯讀狀態的<br /> DataControlFieldCell 及編輯狀態的 TBDropDownList 使用。</p> <p><strong>二、由 TBDropDownField 類別處理 Items 屬性的資料繫結</strong><br /> 我們要自行處理 Items 屬性來取得成員清單,在 InitializeDataCell 方法中無須處理 Items 屬性,只需產生儲存格需要的子控制項,未來在執行子控制項的 DataBinding 時的 OnDataBindField 方法中再來處理 Items 屬性。</p> <pre><code>        Protected Overrides Sub InitializeDataCell( _            ByVal Cell As DataControlFieldCell, _            ByVal RowState As DataControlRowState)            Dim oDropDownList As TBDropDownList            Dim oControl As Control            If Me.CellIsEdit(RowState) Then                oDropDownList = New TBDropDownList()                oControl = oDropDownList                Cell.Controls.Add(oControl)            Else                oControl = Cell            End If            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)            End If        End Sub </code></pre> <p>在 OnDataBindField 方法中,我們加上一段處理 Items 屬性的程式碼如下,會利用 PerformSelecrt 私有方法,由關聯的資料來源 (即 DataSrouceID 指定的資料來源控制項) 擷取資料並產生 Items 的成員清單,在後面會詳細講解 PerformSelecrt 方法處理擷取資料的細節。因為 TBDropDownField 每個資料儲存格都會執行 OnDataBindField 方法,但 Items 取得成員清單的動作只需做一次即可,所以會以 FIsPerformSelect 區域變數來判斷是否已取得 Items 的成員清單,若已取過就不重新取得,這樣比較有執行效能。</p> <pre><code>            If Not Me.DesignMode Then                If Not FIsPerformSelect Then                    '從關聯的資料來源擷取資料                    PerformSelect()                    FIsPerformSelect = True                End If            End If </code></pre> <p>當取得儲存儲的對應的欄位值時,依此欄位值由 Items 集合去取得對應的 ListItem 成員,並以此 ListItem.Text 的文字內容來做顯示。</p> <pre><code>            '由 Items 去取得對應成員的顯示內容            oListItem = Me.Items.FindByValue(CCStr(sText))            If oListItem IsNot Nothing Then                sText = oListItem.Text            End If </code></pre> <p>若是由 TBDropDownList 所引發的 OnDataBindField 方法時,使用 SetItems 私有方法將 TBDropDownField.Items 屬性複製給 TBDropDownList.Item 屬性。</p> <pre><code>                ODropDownList = DirectCast(oControl, TBDropDownList)                SetItems(ODropDownList) </code></pre> <p>SetItems 私有方法的程式碼如下。</p> <pre><code>        Private Sub SetItems(ByVal DropDownList As TBDropDownList)            Dim oItems() As ListItem            If Not Me.DesignMode Then                ReDim oItems(Me.Items.Count - 1)                Me.Items.CopyTo(oItems, 0)                DropDownList.Items.AddRange(oItems)            End If        End Sub </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-25 18:09:12</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續3)</title>                <link>https://ithelp.ithome.com.tw/articles/10012977?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012977?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>四、測試程式</strong><br /> 辛苦寫好 TBDropDownField 欄位類別時,接下來就是驗收成果的時候。我們以 Northwnd 資料...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>四、測試程式</strong><br /> 辛苦寫好 TBDropDownField 欄位類別時,接下來就是驗收成果的時候。我們以 Northwnd 資料庫的 Products 資料表為例,將 TBDropDownList .DataField 設為 CategoryID 欄位來做測試。首先我們測試沒有 DataSoruceID 的情況,在 GridView 加入自訂的 TBDropDownField 欄位繫結 CategoryID 欄位,另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 CategoryID 欄位來做比較。</p> <pre><code>                &lt;bee:TBDropDownField  HeaderText=&quot;CategoryID&quot;                      SortExpression=&quot;CategoryID&quot; DataField=&quot;CategoryID&quot; &gt;                    &lt;Items&gt;                    &lt;asp:ListItem Value=&quot;&quot;&gt;未對應&lt;/asp:ListItem&gt;                    &lt;asp:ListItem Value=&quot;2&quot;&gt;Condiments&lt;/asp:ListItem&gt;                    &lt;asp:ListItem Value=&quot;3&quot;&gt;Confections&lt;/asp:ListItem&gt;                    &lt;/Items&gt;                &lt;/bee:TBDropDownField&gt;                &lt;asp:BoundField DataField=&quot;CategoryID&quot; HeaderText=&quot;CategoryID&quot;                    SortExpression=&quot;CategoryID&quot;  ReadOnly=&quot;true&quot; /&gt; </code></pre> <p>執行程式,在 GridView 在唯讀模式,TBDropDownFIeld 可以正確的繫結 CategoryID 欄位值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb.png" alt="" /></p> <p>編輯某筆資料列進入編輯狀態,就會顯示 TBDropDownList 控制項,清單成員為我們在 Items 設定的內容。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb_1.png" alt="" /></p> <p>使用 TBDropDownList 來做編輯欄位值,按下更新鈕,這時會執行 TBDropDownField.ExtractValuesFromCell 方法,取得儲存格中的值;最後由資料來源控制項將欄位值寫回資料庫。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb_2.png" alt="" /></p> <p>接下來測試設定 TBDropDownField.DataSourceID 的情況,把 DataSourcID 指向含 Categories 資料表內容的 SqlDataSoruce 控制項。</p> <pre><code>                &lt;bee:TBDropDownField  HeaderText=&quot;CategoryID&quot;                      SortExpression=&quot;CategoryID&quot; DataField=&quot;CategoryID&quot;                    DataTextField=&quot;CategoryName&quot; DataValueField=&quot;CategoryID&quot; DataSourceID=&quot;SqlDataSource2&quot;&gt;                &lt;/bee:TBDropDownField&gt; </code></pre> <p>執行程式查看結果,可以發現 TBDropDownList 控制項的清單內容也可以正常顯示 SqlDataSoruce 控制項取得資料。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb_3.png" alt="" /></p> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-24 00:32:30</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續2)</title>                <link>https://ithelp.ithome.com.tw/articles/10012973?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012973?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>step4. 處理資料繫結</strong><br /> 當 GridView 控制項在執行資料繫結時,儲存格的控制項就會引發 DataBinding 事...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>step4. 處理資料繫結</strong><br /> 當 GridView 控制項在執行資料繫結時,儲存格的控制項就會引發 DataBinding 事件,而這些事件會被導向 OnDataBindField 方法來統一處理儲存格中控制項的繫結動作。</p> <pre><code>       ''' &lt;summary&gt;        ''' 將欄位值繫結至 BoundField 物件。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;sender&quot;&gt;控制項。&lt;/param&gt;        ''' &lt;param name=&quot;e&quot;&gt;事件引數。&lt;/param&gt;        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)            Dim oControl As Control            Dim ODropDownList As TBDropDownList            Dim oNamingContainer As Control            Dim oDataValue As Object            '欄位值            Dim bEncode As Boolean              '是否編碼            Dim sText As String                 '格式化字串            oControl = DirectCast(sender, Control)            oNamingContainer = oControl.NamingContainer            oDataValue = Me.GetValue(oNamingContainer)            bEncode = ((Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) AndAlso TypeOf oControl Is TableCell)            sText = Me.FormatDataValue(oDataValue, bEncode)            If TypeOf oControl Is TableCell Then                If (sText.Length = 0) Then                    sText = &quot; &quot;                End If                DirectCast(oControl, TableCell).Text = sText            Else                If Not TypeOf oControl Is TBDropDownList Then                    Throw New HttpException(String.Format(&quot;{0}: Wrong Control Type&quot;, Me.DataField))                End If                ODropDownList = DirectCast(oControl, TBDropDownList)                If Me.ApplyFormatInEditMode Then                    ODropDownList.Text = sText                ElseIf (Not oDataValue Is Nothing) Then                    ODropDownList.Text = oDataValue.ToString                End If            End If        End Sub </code></pre> <p><strong>step5. 取得儲存格中的值</strong><br /> 另外我們還需要覆寫 ExtractValuesFromCell 方法,取得儲存格中的值。這個方法是當 GridView 的編輯資料要準備寫入資料庫時,會經由 ExtractValuesFromCell 方法此來取得每個儲存格的值,並將這些欄位值加入 Dictionary 參數中,這個準備寫入的欄位值集合,可以在 DataSource 控制項的寫入資料庫的相關方法中取得使用。</p> <pre><code>        ''' &lt;summary&gt;        ''' 使用指定 DataControlFieldCell 物件的值填入指定的 System.Collections.IDictionary 物件。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Dictionary&quot;&gt;用於儲存指定儲存格的值。&lt;/param&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;包含要擷取值的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列的狀態。&lt;/param&gt;        ''' &lt;param name=&quot;IncludeReadOnly&quot;&gt;true 表示包含唯讀欄位的值,否則為 false。&lt;/param&gt;        Public Overrides Sub ExtractValuesFromCell( _            ByVal Dictionary As IOrderedDictionary, _            ByVal Cell As DataControlFieldCell, _            ByVal RowState As DataControlRowState, _            ByVal IncludeReadOnly As Boolean)            Dim oControl As Control = Nothing            Dim sDataField As String = Me.DataField            Dim oValue As Object = Nothing            Dim sNullDisplayText As String = Me.NullDisplayText            Dim oDropDownList As TBDropDownList            If (((RowState And DataControlRowState.Insert) = DataControlRowState.Normal) OrElse Me.InsertVisible) Then                If (Cell.Controls.Count &gt; 0) Then                    oControl = Cell.Controls.Item(0)                    oDropDownList = TryCast(oControl, TBDropDownList)                    If (Not oDropDownList Is Nothing) Then                        oValue = oDropDownList.Text                    End If                ElseIf IncludeReadOnly Then                    Dim s As String = Cell.Text                    If (s = &quot; &quot;) Then                        oValue = String.Empty                    ElseIf (Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) Then                        oValue = HttpUtility.HtmlDecode(s)                    Else                        oValue = s                    End If                End If                If (Not oValue Is Nothing) Then                    If TypeOf oValue Is String Then                        If (CStr(oValue).Length = 0) AndAlso Me.ConvertEmptyStringToNull Then                            oValue = Nothing                        ElseIf (CStr(oValue) = sNullDisplayText) AndAlso (sNullDisplayText.Length &gt; 0) Then                            oValue = Nothing                        End If                    End If                    If Dictionary.Contains(sDataField) Then                        Dictionary.Item(sDataField) = oValue                    Else                        Dictionary.Add(sDataField, oValue)                    End If                End If            End If        End Sub </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-24 00:31:32</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續1)</title>                <link>https://ithelp.ithome.com.tw/articles/10012971?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012971?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>step2. 加入 TBBaseBoundField 的屬性</strong><br /> TBBaseBoundField 類別會內含 DropDown...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>step2. 加入 TBBaseBoundField 的屬性</strong><br /> TBBaseBoundField 類別會內含 DropDownList 控制項,所以加入設定 DropDownList 控制項的對應屬性;我們在 TBBaseBoundField 類別加入了 Items 、DataSourceID、DataTextField、DataValueField 屬性。其中 Items 屬性的型別與 DropDownList.Items 屬性相同,都是 ListItemCollection 集合類別,且 Items 屬性會儲存於 ViewState 中。</p> <pre><code>        ''' &lt;summary&gt;        ''' 清單項目集合。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;清單項目集合。&quot;), _        DefaultValue(CStr(Nothing)), _        PersistenceMode(PersistenceMode.InnerProperty), _        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _        Editor(GetType(ListItemsCollectionEditor), GetType(UITypeEditor)), _        MergableProperty(False), _        Category(&quot;Default&quot;)&gt; _        Public Overridable ReadOnly Property Items() As ListItemCollection            Get                If (FItems Is Nothing) Then                    FItems = New ListItemCollection()                    If MyBase.IsTrackingViewState Then                        CType(FItems, IStateManager).TrackViewState()                    End If                End If                Return FItems            End Get        End Property        ''' &lt;summary&gt;        ''' 資料來源控制項的 ID 屬性。        ''' &lt;/summary&gt;        Public Property DataSourceID() As String            Get                Return FDataSourceID            End Get            Set(ByVal value As String)                FDataSourceID = value            End Set        End Property        ''' &lt;summary&gt;        ''' 提供清單項目文字內容的資料來源的欄位。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;提供清單項目文字內容的資料來源的欄位。&quot;), _        DefaultValue(&quot;&quot;) _        &gt; _        Public Property DataTextField() As String            Get                Return FDataTextField            End Get            Set(ByVal value As String)                FDataTextField = value            End Set        End Property        ''' &lt;summary&gt;        ''' 提供清單項目值的資料來源的欄位。        ''' &lt;/summary&gt;        Public Property DataValueField() As String            Get                Return FDataValueField            End Get            Set(ByVal value As String)                FDataValueField = value            End Set        End Property </code></pre> <p><strong>step3.建立儲存格內含的控制項</strong><br /> GridView 是以儲存格 (DataControlFieldCell) 為單位,我們要覆寫 InitializeDataCell 方法來建立儲存格中的控制項;當儲存格為可編輯狀態時,就建立 DropDownList 控制項並加入儲存格中,在此使用上篇文章提及的 TBDropDownList 控制項來取代,以解決清單成員不存在造成錯誤的問題。若未設定 DataSourceID 屬性時,則由 Items 屬性取得自訂的清單項目;若有設定 DataSourceID 屬性,則由資料來源控制項 (如 SqlDataSource、ObjectDataSource 控制項) 來取得清單項目。<br /> 當建立儲存格中的控制項後,需要以 AddHeadler 的方法,將此控制項的 DataBinding 事件導向 OnDataBindField 這個事件處理方法,我們要在 OnDataBindField 處理資料繫結的動作。</p> <pre><code>        ''' &lt;summary&gt;        ''' 資料儲存格初始化。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;要初始化的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Protected Overrides Sub InitializeDataCell( _            ByVal Cell As DataControlFieldCell, _            ByVal RowState As DataControlRowState)            Dim oDropDownList As TBDropDownList            Dim oItems() As ListItem            Dim oControl As Control            If Me.CellIsEdit(RowState) Then                oDropDownList = New TBDropDownList()                oControl = oDropDownList                Cell.Controls.Add(oControl)                If Not Me.DesignMode Then                    If StrIsEmpty(Me.DataSourceID) Then                        '自訂清單項目                        ReDim oItems(Me.Items.Count - 1)                        Me.Items.CopyTo(oItems, 0)                        oDropDownList.Items.AddRange(oItems)                    Else                        '由資料來源控制項取得清單項目                        oDropDownList.DataSourceID = Me.DataSourceID                        oDropDownList.DataTextField = Me.DataTextField                        oDropDownList.DataValueField = Me.DataValueField                    End If                End If            Else                oControl = Cell            End If            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)            End If        End Sub </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-24 00:25:02</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位</title>                <link>https://ithelp.ithome.com.tw/articles/10012965?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012965?sc=rss.iron</guid>                <description><![CDATA[<p>GridView 是 ASP.NET 中一個相當常用的控制項,在 GridView 可加入 BoundField、CheckBoxField、CommandField、TemplateField...]]></description>                                    <content:encoded><![CDATA[<p>GridView 是 ASP.NET 中一個相當常用的控制項,在 GridView 可加入 BoundField、CheckBoxField、CommandField、TemplateField ... 等不同型別的欄位,可是偏偏沒有提供在 GridView 中可呈現 DropDownList 的欄位型別;遇到這類需求時,一般的作法都是使用 TemplateField 來處理。雖然 TemplateField 具有相當好的設計彈性。可是在當 GridView 需要動態產生欄位的需求時,TemplateField 就相當麻煩,要寫一堆程式碼自行去處理資料繫結的動作。相互比較起來,BoundField、CheckBoxField ...等這類事先定義類型的欄位,在 GridView 要動態產生這些欄位就相當方便。如果我們可以把一些常用的 GridView 的欄位,都做成類似 BoundField 一樣,只要設定欄位的屬性就好,這樣使用上就會方便許多,所以在本文將以實作 DropDownList 欄位為例,讓大家了解如何去自訂 GridView 的欄位類別。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102321355642.rar" target="_blank">ASP.NET Server Control - Day23.rar </a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、選擇合適的父類別</strong><br /> 一般自訂 GridView 的欄位類別時,大都是由 DataControlField 或 BoundField 繼承下來改寫。若是欄位不需繫結資料(如 CommandFIeld),可以由 DataControlFIeld 繼承下來,若是欄位需要做資料繫結時(如 CheckBoxFIld,可以直接由 BoundField 繼承下來改寫比較方便。<br /> DataControlField 類別是所有類型欄位的基底類別,BoundField 類別也是由 DataControlField 類別繼承下來擴展了資料繫結部分的功能,所以我們要實作含 DropDownList 的欄位,也是由 BoundField 繼承下來改寫。</p> <p><strong>二、自訂欄位基底類別</strong><br /> 在此我們不直接繼承 BoundFIeld,而是先撰寫一個繼承 BoundField 命名為 TBBaseBoundField 的基底類別,此類別提供一些通用的屬性及方法,使我們更方便去撰寫自訂的欄位類別。</p> <pre><code>    ''' &lt;summary&gt;    ''' 資料欄位基礎類別。    ''' &lt;/summary&gt;    Public MustInherit Class TBBaseBoundField        Inherits BoundField        Private FRowIndex As Integer = 0        ''' &lt;summary&gt;        ''' 資料列是否為編輯模式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Public Function RowStateIsEdit(ByVal RowState As DataControlRowState) As Boolean            Return (RowState And DataControlRowState.Edit) &lt;&gt; DataControlRowState.Normal        End Function        ''' &lt;summary&gt;        ''' 資料列是否為新增模式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Public Function RowStateIsInsert(ByVal RowState As DataControlRowState) As Boolean            Return (RowState And DataControlRowState.Insert) &lt;&gt; DataControlRowState.Normal        End Function        ''' &lt;summary&gt;        ''' 資料列是否為編輯或新增模式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Public Function RowStateIsEditOrInsert(ByVal RowState As DataControlRowState) As Boolean            Return RowStateIsEdit(RowState) OrElse RowStateIsInsert(RowState)        End Function        ''' &lt;summary&gt;        ''' 判斷儲存格是否可編輯(新增/修改)。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Friend Function CellIsEdit(ByVal RowState As DataControlRowState) As Boolean            Return (Not Me.ReadOnly) AndAlso RowStateIsEditOrInsert(RowState)        End Function        ''' &lt;summary&gt;        ''' 資料列索引。        ''' &lt;/summary&gt;        Friend ReadOnly Property RowIndex() As Integer            Get                Return FRowIndex            End Get        End Property        ''' &lt;summary&gt;        ''' 儲存格初始化。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;要初始化的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;CellType&quot;&gt;儲存格類型。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        ''' &lt;param name=&quot;RowIndex&quot;&gt;資料列之以零起始的索引。&lt;/param&gt;        Public Overrides Sub InitializeCell(ByVal Cell As DataControlFieldCell, ByVal CellType As DataControlCellType, _            ByVal RowState As DataControlRowState, ByVal RowIndex As Integer)            FRowIndex = RowIndex            MyBase.InitializeCell(Cell, CellType, RowState, RowIndex)        End Sub        ''' &lt;summary&gt;        ''' 是否需要執行資料繫結。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Friend Function RequiresDataBinding(ByVal RowState As DataControlRowState) As Boolean            If MyBase.Visible AndAlso StrIsNotEmpty(MyBase.DataField) AndAlso RowStateIsEdit(RowState) Then                Return True            Else                Return False            End If        End Function    End Class </code></pre> <p><strong>三、實作 TBDropDownField 欄位類別</strong><br /> <strong>step1. 繼承 TBBaseBoundField 類別</strong><br /> 首先新增一個類別,繼承 TBBaseBoundField 命名為 TBDropDownFIeld 類別,覆寫 CreateField 方法,傳回 TBDropDownFIeld 物件。</p> <pre><code>    Public Class TBDropDownField        Inherits TBBaseBoundField        Protected Overrides Function CreateField() As System.Web.UI.WebControls.DataControlField            Return New TBDropDownField()        End Function    End Class </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-24 00:19:12</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day22] 讓 DropDownList 不再因項目清單不存在而造成錯誤(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10012915?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012915?sc=rss.iron</guid>                <description><![CDATA[<p>接續上篇文章內容<br /> <strong>三、解決 TBDropDownList 設定 DataSourceID 造成資料無法繫結的問題</strong><br /> 要解決上述 TBDro...]]></description>                                    <content:encoded><![CDATA[<p>接續上篇文章內容<br /> <strong>三、解決 TBDropDownList 設定 DataSourceID 造成資料無法繫結的問題</strong><br /> 要解決上述 TBDropDownList 設定 DataSourceID 問題,需在設定 SelectedValue 屬性時,若 Items.Count=0 先用一個 FCachedSelectedValue 變數將正確的值先暫存下來,然後覆寫 PerformDataBinding 方法,當 DorpDownList 取得 DataSoruceID 所對應的項目清單內容後,因為這時 Items 的內容才會完整取回,再去設定一次 SelectedValue 屬性就可以正確的繫結資料。</p> <pre><code>    Public Class TBDropDownList        Inherits DropDownList        Private FCachedSelectedValue As String        ''' &lt;summary&gt;        ''' 覆寫 SelectedValue 屬性。        ''' &lt;/summary&gt;        Public Overrides Property SelectedValue() As String            Get                Return MyBase.SelectedValue            End Get            Set(ByVal value As String)                If Me.Items.Count &lt;&gt; 0 Then                    Dim oItem As ListItem = Me.Items.FindByValue(value)                    If (oItem Is Nothing) Then                        Me.SelectedIndex = -1 '當 Items 不存在時                    Else                        MyBase.SelectedValue = value                    End If                Else                    FCachedSelectedValue = value                End If            End Set        End Property        Protected Overrides Sub PerformDataBinding(ByVal data As System.Collections.IEnumerable)            MyBase.PerformDataBinding(data)            'DataSoruceID 資料繫結後再設定 SelectedValue 屬性值            If (Not FCachedSelectedValue Is Nothing) Then                Me.SelectedValue = FCachedSelectedValue            End If        End Sub    End Class </code></pre> <p>重新執行程式,切換到編輯模式時,TBDropDownList 就可以正確的繫結欄位值了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_5.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-23 07:03:54</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day22] 讓 DropDownList 不再因項目清單不存在而造成錯誤</title>                <link>https://ithelp.ithome.com.tw/articles/10012909?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012909?sc=rss.iron</guid>                <description><![CDATA[<p>DropDownList 控制項常常會因為項目清單中不存在繫結的欄位,而發生以下的錯誤訊息。因為繫結資料的不完整或異常就會造成這樣的異常錯誤,在設計上實在是相當困擾,而且最麻煩的是這個錯誤在頁面...]]></description>                                    <content:encoded><![CDATA[<p>DropDownList 控制項常常會因為項目清單中不存在繫結的欄位,而發生以下的錯誤訊息。因為繫結資料的不完整或異常就會造成這樣的異常錯誤,在設計上實在是相當困擾,而且最麻煩的是這個錯誤在頁面的程式碼也無法使用 Try ... Catch 方式來略過錯誤。其實最簡單的方式就去直接去修改 DropDownList 控制項,讓 DropDownList 控制項繫結資料時,就算欄位值不存在清單項目中也不要釋出錯誤,本文就要說明如何繼承 DorpDownList 下來修改,來有效解決這個問題。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb.png" alt="" /><br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102365119295.rar" target="_blank">ASP.NET Server Control - Day22.rar</a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、覆寫 SelectedValue 屬性解決資料繫結的問題</strong><br /> DropDownList 控制項繫結錯誤的原因,可以由上圖的錯誤訊息可以大概得知是寫入 SelectedValue 屬性時發生的錯誤;所以我們繼承 DorpDownList 下來命名為 TBDropDownList,並覆寫 SelectedValue 屬性來解決這個問題。解決方式是在寫入 SelectedValue 屬性時,先判斷準備寫入的值是否存在項目清單中,存在的話才寫入 SelectedValue 屬性,若不存在則直接設定 SelectedIndex 屬性為 -1。</p> <pre><code>    Public Class TBDropDownList        Inherits DropDownList        ''' &lt;summary&gt;        ''' 覆寫 SelectedValue 屬性。        ''' &lt;/summary&gt;        Public Overrides Property SelectedValue() As String            Get                Return MyBase.SelectedValue            End Get            Set(ByVal value As String)                Dim oItem As ListItem = Me.Items.FindByValue(value)                If (oItem Is Nothing) Then                    Me.SelectedIndex = -1 '當 Items 不存在時                    Exit Property                Else                    MyBase.SelectedValue = value                End If            End Set        End Property    End Class </code></pre> <p>我們以 Northwnd 資料庫的 Products 資料表做為測試資料,事先定義 DropDownList 的 Items 內容,其中第一個加入 &quot;未對應&quot; 的項目,將 SelectedValue 屬性繫結至 CategoryID 欄位。</p> <pre><code>                &lt;bee:TBDropDownList ID=&quot;DropDownList1&quot; runat=&quot;server&quot;                    SelectedValue='&lt;%# Bind(&quot;CategoryID&quot;) %&gt;'&gt;                    &lt;asp:ListItem Value=&quot;&quot;&gt;未對應&lt;/asp:ListItem&gt;                    &lt;asp:ListItem Value=&quot;2&quot;&gt;Condiments&lt;/asp:ListItem&gt;                    &lt;asp:ListItem Value=&quot;3&quot;&gt;Confections&lt;/asp:ListItem&gt;                &lt;/bee:TBDropDownList&gt; </code></pre> <p>當資料的 CategoryID 欄位值不存在於 DropDownList 的 Items 集合屬性中時,就會顯示第一個 &quot;未對應&quot; 的項目。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_1.png" alt="" /></p> <p><strong>二、TBDropDownList 設定 DataSoruceID 產生的問題</strong><br /> 上述的解決方法在筆者的「讓 DropDownList DataBind 不再發生錯誤」一文中已經有提及,不過有讀者發現另一個問題,就是當 DropDownList 設定 DataSourceID 時卻會發生資料無法正常繫結,以下就來解決這個問題。<br /> 我們設定 TBDropDownList 的 DataSoruceID 來取得項目清單的內容,將 DataSourceID 設定為另一個取得 Categories 資料表內容的 SqlDataSource 控制項。</p> <pre><code>                &lt;bee:TBDropDownList ID=&quot;DropDownList1&quot; runat=&quot;server&quot;                    SelectedValue='&lt;%# Bind(&quot;CategoryID&quot;) %&gt;' DataSourceID=&quot;SqlDataSource2&quot;                    DataTextField=&quot;CategoryName&quot; DataValueField=&quot;CategoryID&quot;&gt;                &lt;/bee:TBDropDownList&gt;                &lt;asp:SqlDataSource ID=&quot;SqlDataSource2&quot; runat=&quot;server&quot;                    ConnectionString=&quot;&lt;%$ ConnectionStrings:Northwnd %&gt;&quot;                    SelectCommand=&quot;SELECT CategoryID, CategoryName, Description, Picture FROM Categories&quot;                    ProviderName=&quot;&lt;%$ ConnectionStrings:Northwnd.ProviderName %&gt;&quot; &gt;                &lt;/asp:SqlDataSource&gt; </code></pre> <p>當執行程式時,FormView 原本在瀏覽模式時的 CategoryID 欄位值為 7 (CategoryName 應為 Product)。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_3.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_2.png" alt="" /></p> <p>當按下「編輯」時切換到 EditItemTemplate 時,改用 TBDropDownList 繫結 CategoryID 欄位值,可以這時欲無法繫結正確的值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_4.png" alt="" /></p> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-23 06:59:56</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day21] 實作控制項智慧標籤(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10012897?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012897?sc=rss.iron</guid>                <description><![CDATA[<p>接續 [ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 一文<br /> <strong>step2. 在智慧標籤面板加入屬性項目</strong><br /> DesignerA...]]></description>                                    <content:encoded><![CDATA[<p>接續 [ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 一文<br /> <strong>step2. 在智慧標籤面板加入屬性項目</strong><br /> DesignerActionPropertyItem 類別是設定智慧標籤面上的屬性項目,DesignerActionPropertyItem 建構函式的第一個參數(memberName) 為屬性名稱,這個屬性指的是 TBDateEditActionList 類別中的屬性,所以要在 TBDateEditActionList 新增一個對應的屬性。<br /> 例如在智慧標籤中加入 AutoPostBack 屬性項目,則在 TBDateEditActionList 類別需有一個對應 AutoPostBack 屬性。</p> <pre><code>            oItems.Add(New DesignerActionPropertyItem(&quot;AutoPostBack&quot;, _                &quot;AutoPostBack&quot;, &quot;Behavior&quot;, &quot;是否引發 PostBack 動作。&quot;)) </code></pre> <p>TBDateEditActionList.AutoPostBack 屬性如下,其中 Me.Component 指的是目前的 TDateEdit 控制項,透過 GetPropertyValue 及 SetPropertyValue 方法來存取控制項的指定屬性。</p> <pre><code>        ''' &lt;summary&gt;        ''' 是否引發 PostBack 動作。        ''' &lt;/summary&gt;        Public Property AutoPostBack() As Boolean            Get                Return CType(GetPropertyValue(Me.Component, &quot;AutoPostBack&quot;), Boolean)            End Get            Set(ByVal value As Boolean)                SetPropertyValue(Me.Component, &quot;AutoPostBack&quot;, value)            End Set        End Property    ''' &lt;summary&gt;    ''' 設定物件的屬性值。    ''' &lt;/summary&gt;    ''' &lt;param name=&quot;Component&quot;&gt;屬性值將要設定的物件。&lt;/param&gt;    ''' &lt;param name=&quot;PropertyName&quot;&gt;屬性名稱。&lt;/param&gt;    ''' &lt;param name=&quot;Value&quot;&gt;新值。&lt;/param&gt;    Public Shared Sub SetPropertyValue(ByVal Component As Object, ByVal PropertyName As String, ByVal Value As Object)        Dim Prop As PropertyDescriptor = TypeDescriptor.GetProperties(Component).Item(PropertyName)        Prop.SetValue(Component, Value)    End Sub    ''' &lt;summary&gt;    ''' 取得物件的屬性值。    ''' &lt;/summary&gt;    ''' &lt;param name=&quot;Component&quot;&gt;具有要擷取屬性的物件。&lt;/param&gt;    ''' &lt;param name=&quot;PropertyName&quot;&gt;屬性名稱。&lt;/param&gt;    Public Shared Function GetPropertyValue(ByVal Component As Object, ByVal PropertyName As String) As Object        Dim Prop As PropertyDescriptor = TypeDescriptor.GetProperties(Component).Item(PropertyName)        Return Prop.GetValue(Component)    End Function </code></pre> <p><strong>step3. 在智慧標籤面板加入方法項目</strong><br /> DesignerActionMethodItem 類別是設定智慧標籤面上的方法項目,DesignerActionPropertyItem 建構函式的第二個參數(memberName) 為方法名稱,這個方法指的是 TBDateEditActionList 類別中的方法,所以要在 TBDateEditActionList 新增一個對應的方法。<br /> 例如在智慧標籤中加入 About 方法項目,則在 TBDateEditActionList 類別需有一個對應 About 方法。</p> <pre><code>            oItems.Add(New DesignerActionMethodItem(Me, &quot;About&quot;, _                &quot;關於 TDateEdit 控制項&quot;, &quot;About&quot;, _                &quot;關於 TDateEdit 控制項。&quot;, True)) </code></pre> <p>TBDateEditActionList 的 About 方法只是單純顯示一個訊息視窗,一般你可以在這方法加入任何想在設計階段處理的動作。例如自動產生 GridView 的欄位、在 FormView 加入控制項並自動排版,這些都可以在此實現的。</p> <pre><code>        Public Sub About()            MsgBox(&quot;TDateEdit 是結合 The Coolest DHTML Calendar 日期選擇器實作的控制項&quot;)        End Sub </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-22 18:02:28</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day21] 實作控制項智慧標籤</title>                <link>https://ithelp.ithome.com.tw/articles/10012896?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012896?sc=rss.iron</guid>                <description><![CDATA[<p>控制項通常會把常用屬性或功能顯示在智慧標籤中,提供使用者更簡便的快速設定,例如下圖為 GridView 的智慧。若要製作控制項的智慧標籤,需實作控制項的 ActionList 加入智慧標籤中要顯...]]></description>                                    <content:encoded><![CDATA[<p>控制項通常會把常用屬性或功能顯示在智慧標籤中,提供使用者更簡便的快速設定,例如下圖為 GridView 的智慧。若要製作控制項的智慧標籤,需實作控制項的 ActionList 加入智慧標籤中要顯示的項目,在本文將以 TDateEdit 控制項為例,進一步說明控制項的智慧標籤的實作方式。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay21_6429/image_thumb.png" alt="" /><br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102217453969.rar" target="_blank">ASP.NET Server Control - Day21.rar</a></p> <p><strong>一、TDateEdit 控制項介紹</strong><br /> TDateEdit 控制項是筆者之前在部落格中實作的一個日期控制項,如下圖所示。它是結合 JavaScript 的 The Coolest DHTML Calendar 日期選擇器實作的控制項,我已將 TDateEdit 控制項的相關程式碼含入 Bee.Web.dll 組件中。TDateEdit 控制項的相關細節可以參考筆者部落格下面幾篇文章有進一步說明,本文將以 TDateEdit 控制項為例,只針對實作智慧標籤的部分做進一步說明。<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1742.aspx" target="_blank">日期控制項實作教學(1) - 結合 JavaScript</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1743.aspx" target="_blank">日期控制項實作教學(2) - PostBack 與 事件</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1746.aspx" target="_blank">TBDateEdit 日期控制項 - 1.0.0.0 版 (Open Source)</a><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay21_6429/image_thumb_1.png" alt="" /></p> <p><strong>二、控制項加入智慧標籤</strong><br /> 控制項要加入智慧標籤要實作控制項的 Designer,我們繼承 ControlDesigner 命名為 TBDateEditDesigner,然後覆寫 ActionLists 屬性,此屬性即是傳回智慧標籤中所包含的項目清單集合。在 ActionLists 屬性中一般會先加入父類別的 ActionLists 屬性,再加入自訂的 ActionList 類別,這樣才可以保留原父類別中智慧標籤的項目清單。</p> <pre><code>    ''' &lt;summary&gt;    ''' TBDateEdit 控制項的設計模式行為。    ''' &lt;/summary&gt;    Public Class TBDateEditDesigner        Inherits System.Web.UI.Design.ControlDesigner        ''' &lt;summary&gt;        ''' 取得控制項設計工具的動作清單集合。        ''' &lt;/summary&gt;        Public Overrides ReadOnly Property ActionLists() As DesignerActionListCollection            Get                Dim oActionLists As New DesignerActionListCollection()                oActionLists.AddRange(MyBase.ActionLists)                oActionLists.Add(New TBDateEditActionList(Me))                Return oActionLists            End Get        End Property    End Class </code></pre> <p>我們自訂的 ActionList 為 TBDateEditActionList 類別,它在智慧標籤呈現的項目清單如下圖所示,接下去我們會說明 TBDateEditActionList 類別的內容。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay21_6429/image_thumb_2.png" alt="" /></p> <p><strong>三、自訂智慧標籤面板的項目清單集合</strong><br /> DesignerActionList 類別定義用於建立智慧標籤面板的項目清單的基底類別,所以我們首先繼承 DesignerActionList 命名為 TBDateEditActionList。</p> <pre><code>    ''' &lt;summary&gt;    ''' 定義 TBDateEdit 控制項智慧標籤面板的項目清單集合。    ''' &lt;/summary&gt;    Public Class TBDateEditActionList        Inherits DesignerActionList        ''' &lt;summary&gt;        ''' 建構函式。        ''' &lt;/summary&gt;        Public Sub New(ByVal owner As ControlDesigner)            MyBase.New(owner.Component)        End Sub    End Class </code></pre> <p>接下來要覆寫 GetSortedActionItems 方法,它會回傳 DesignerActionItemCollection 集合型別,此集合中會傳回要顯示在智慧標籤面板的項目清單集合,所以我們要在 DesignerActionItemCollection 集合中加入我們要呈現的項目清單內容。</p> <pre><code>        ''' &lt;summary&gt;        ''' 傳回要顯示在智慧標籤面板的項目清單集合。        ''' &lt;/summary&gt;        Public Overrides Function GetSortedActionItems() As System.ComponentModel.Design.DesignerActionItemCollection            Dim oItems As New DesignerActionItemCollection()            '在此加入智慧標籤面板的項目清單                      Return oItems        End Function </code></pre> <p><strong>step1. 在智慧標籤面板加入靜態標題項目</strong><br /> 首先介紹 DesignerActionHeaderItem 類別,它是設定靜態標題項目,例如我們在 TDateEdit 的智慧標籤中加入「行為」、「外觀」二個標題項目,其中 DesignerActionHeaderItem 建構函式的 category 參數是群組名稱,我們可以將相關的項目歸類到同一個群組。</p> <pre><code>Dim oItems As New DesignerActionItemCollection() oItems.Add(New DesignerActionHeaderItem(&quot;行為&quot;, &quot;Behavior&quot;)) oItems.Add(New DesignerActionHeaderItem(&quot;外觀&quot;, &quot;Appearance&quot;)) </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-22 18:01:29</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day20] 偵錯設計階段的程式碼</title>                <link>https://ithelp.ithome.com.tw/articles/10012807?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012807?sc=rss.iron</guid>                <description><![CDATA[<p>上篇我們介紹了自訂 Designer 來輸出控制項設計階段的 HTML 碼,可是若你去對針 Designer 的程式碼下中斷點,你會發覺根本無法偵錯。因為程式在執行階段時期,根本不會執行 Des...]]></description>                                    <content:encoded><![CDATA[<p>上篇我們介紹了自訂 Designer 來輸出控制項設計階段的 HTML 碼,可是若你去對針 Designer 的程式碼下中斷點,你會發覺根本無法偵錯。因為程式在執行階段時期,根本不會執行 Designer 相關類別,所以你在 Designer 類別中下的中斷點完全無效;當然不可能這樣寫程式碼而用感覺去偵錯,本文將告訴你如何去偵錯設計階段的程式碼。<br /> <strong>一、設計階段程式碼的錯誤</strong><br /> 如果撰寫 Designer、Editor、ActionList 等設計階段的程式碼,當這些設計階段的程式碼發生錯誤,可能會發生設計頁面中控制項的錯誤情形,如下圖所示。因為控制項專案本身非啟動專案,在測試網站的設計頁面若控制項發生異常時會直接釋出錯誤,無法偵錯設計階段的程式碼;若真得要偵錯誤設計階段的問題,就要使用另一個 VS2008 來偵錯。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_1.png" alt="" /></p> <p><strong>二、設定起始外部程式</strong><br /> 要偵錯控制項設計階段的程式碼,要先將控制項專案(Bee.Web)設定為啟時專案。然後設定控制項專案的「屬性」,在「偵錯」頁籤中的起始動作選擇「起始外部程式」,選擇 VS2008 的執行檔位置,預設為 C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb.png" alt="" /></p> <p><strong>三、開始偵錯設計階段程式碼</strong><br /> <strong>step1. 控制項專案開始偵錯</strong><br /> 在設計階要偵錯的程式碼下中斷點,在控制項專案按下 F5 開始偵錯,這時會啟動另一個新的 VS2008 執行檔。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_2.png" alt="" /></p> <p><strong>step2. 在新的 VS2008 的工具箱加入控制項</strong><br /> 在新的 VS2008 中新增一個測試網站,在工具箱按右鍵執行「選擇項目」開啟「選擇工具箱項目」視窗,然後按「瀏覽」鈕按選擇控制項組件(Bee.Web.dll),將要偵錯的控制項加入工具箱中。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_4.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_5.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_6.png" alt="" /></p> <p><strong>step3. 將控制項拖曳至頁面做設計動作</strong><br /> 在新的 VS2008 中,將控制項拖曳至頁面,就會開始執行設計階段的程式碼,特定的設計動作就會執行到相對的設計階段程式碼,當執行到之前下的中斷點時就可以開始偵錯了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_7.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/21/5741.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/21/5741.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-21 00:28:45</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day19] 控制項設計階段的外觀</title>                <link>https://ithelp.ithome.com.tw/articles/10012682?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012682?sc=rss.iron</guid>                <description><![CDATA[<p>有一些控制項在執行階段是不會呈現,也就是說控制項本身在執行階段不會 Render 出 HTML 碼,例如 SqlDataSoruce、ScriptManager 這類控制項;那它們在設計階段的頁...]]></description>                                    <content:encoded><![CDATA[<p>有一些控制項在執行階段是不會呈現,也就是說控制項本身在執行階段不會 Render 出 HTML 碼,例如 SqlDataSoruce、ScriptManager 這類控制項;那它們在設計階段的頁面是如何呈現出來呢?本文將針對控制項設計階段的外觀做進一步的說明。<br /> 程式碼下載:ASP.NET Server Control - Day19.rar<br /> <strong>一、控制項設計階段的 HTML 碼</strong><br /> Web 伺服器控制項的設計模式行為都是透過 ControlDesigner 來處理,連設計階段時控制項的外觀也是如此;控制項在設計階段與執行執行時呈現的外觀不一定相同,當然大部分會儘量一致,使其能所見即所得。<br /> 控制項在設計階段的 HTML 碼是透 ControlDesigner.GetDesignTimeHtml 方法來處理,在 ControlDesigner.GetDesignTimeHtml 預設會執行控制項的 RenderControl 方法,所以大部分的情況下設計階段與執行階段輸出的 HTML 碼會相同。當控制項的 Visible=False 時,執行階段是完全不會輸出 HTML 碼,可是在設計階段時會特別將控制項設定 Visible=True,使控制項能完整呈現。</p> <p>ControlDesigner.GetDesignTimeHtml 方法</p> <pre><code>Public Overridable Function GetDesignTimeHtml() As String    Dim writer As New StringWriter(CultureInfo.InvariantCulture)    Dim writer2 As New DesignTimeHtmlTextWriter(writer)    Dim errorDesignTimeHtml As String = Nothing    Dim flag As Boolean = False    Dim visible As Boolean = True    Dim viewControl As Control = Nothing    Try        viewControl = Me.ViewControl        visible = viewControl.Visible        If Not visible Then            viewControl.Visible = True            flag = Not Me.UsePreviewControl        End If        viewControl.RenderControl(writer2)        errorDesignTimeHtml = writer.ToString    Catch exception As Exception        errorDesignTimeHtml = Me.GetErrorDesignTimeHtml(exception)    Finally        If flag Then            viewControl.Visible = visible        End If    End Try    If ((Not errorDesignTimeHtml Is Nothing) AndAlso (errorDesignTimeHtml.Length &lt;&gt; 0)) Then        Return errorDesignTimeHtml    End If    Return Me.GetEmptyDesignTimeHtml End Function </code></pre> <p><strong>二、自訂控制項的 Designer</strong><br /> 以 TBToolbar 為例,若我們在 RenderContents 方法未針對 Items.Count=0 做輸出 HTML 的處理,會發現未設定 Items 屬性時,在設計頁面上完全看不到 TBToolbar 控制項;像這種控制項設計階段的 HTML 碼,就可以自訂控制項的 Designer 來處理。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay19_1ACC/image_thumb.png" alt="" /></p> <p>繼承 ControlDesigner 命名為 TBToolbarDesigner,這個類別是用來擴充 TBToolbar 控制項的設計模式行為。我們可以覆寫 GetDesignTimeHtml 方法,處理設計階段表示控制項的 HTML 標記,此方法回傳的 HTML 原始碼就是控制項呈現在設計頁面的外觀。所以我們可以在 TBToolbar.Items.Count=0 時,輸出一段提示的 HTML 碼,這樣當 TBToolbar 未設定 Items 屬性時一樣可以在設計頁面上呈現控制項。</p> <pre><code>    ''' &lt;summary&gt;    ''' 擴充 TBToolbar 控制項的設計模式行為。    ''' &lt;/summary&gt;    Public Class TBToolbarDesigner        Inherits System.Web.UI.Design.ControlDesigner        ''' &lt;summary&gt;        ''' 用來在設計階段表示控制項的 HTML 標記。        ''' &lt;/summary&gt;        Public Overrides Function GetDesignTimeHtml() As String            Dim sHTML As String            Dim oControl As TBToolbar            oControl = CType(ViewControl, TBToolbar)            If oControl.Items.Count = 0 Then                sHTML = &quot;&lt;div style=&quot;&quot;background-color: #C0C0C0; border:solid 1px; width:200px&quot;&quot;&gt;請設定 Items 屬性&lt;/div&gt;&quot;            Else                sHTML = MyBase.GetDesignTimeHtml()            End If            Return sHTML        End Function    End Class </code></pre> <p>在 TBToolbar 控制項套用 DesignerAttribute 設定自訂的 TBToolbarDesigner 類別。</p> <pre><code>    &lt;Designer(GetType(TBToolbarDesigner))&gt; _    Public Class TBToolbar        Inherits WebControl    End Class </code></pre> <p>重建控制項組件,切換到設計頁面上的看 TBToolbar 控制項未設定 Items 屬性時的外觀,就是我們在 TBToolbarDesigner.GetDesignTimeHtml 方法回傳的 HTML 碼。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay19_1ACC/image_thumb_1.png" alt="" /></p> <p>如果你覺得上述設計階段的控制項有點太陽春,我們也可以輸出類似 SqlDataSource 控制項的外觀,將未設定 Items 屬性時輸出 HTML 改呼叫 CreatePlaceHolderDesignTimeHtml 方法。</p> <pre><code>            If oControl.Items.Count = 0 Then                sHTML = MyBase.CreatePlaceHolderDesignTimeHtml(&quot;請設定 Items 屬性&quot;)            Else                sHTML = MyBase.GetDesignTimeHtml()            End If </code></pre> <p>來看一下這樣修改後的結果,是不是比較專業一點了呢。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay19_1ACC/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/20/5726.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/20/5726.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-20 02:25:29</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day18] 修改集合屬性編輯器</title>                <link>https://ithelp.ithome.com.tw/articles/10012636?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012636?sc=rss.iron</guid>                <description><![CDATA[<p>上篇我們實作了「集合屬性包含不同型別的成員」,不過若有去使用屬性視窗編輯 TBToolbar 的 Items 屬性,你會發覺這個集合屬性編輯器無法加入我們定義不同型別的成員,只能加入最原始的集合...]]></description>                                    <content:encoded><![CDATA[<p>上篇我們實作了「集合屬性包含不同型別的成員」,不過若有去使用屬性視窗編輯 TBToolbar 的 Items 屬性,你會發覺這個集合屬性編輯器無法加入我們定義不同型別的成員,只能加入最原始的集合成員。是不是只能在 aspx 程式碼中手動去輸入呢?當然不需要這樣人工作業,只要改掉集合屬性編輯器就可以達到我們的需求,本文將介紹修改集合屬性編輯器的相關作法。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/200810190138285.rar" target="_blank">ASP.NET Server Control - Day18.rar</a></p> <p><strong>一、自訂集合屬性編輯器</strong><br /> 我們先看一下 TBToolbar.Items 屬性套用的 EditorAttribute,它是使用 CollectionEditor 類別來當作屬性編輯器,所以我們就是要繼承 CollectionEditor 類別下來修改成自訂的屬性編輯器。</p> <pre><code>&lt; _ Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _ &gt; _ Public ReadOnly Property Items() As TBToolbarItemCollection </code></pre> <p>新增一個繼承 CollectionEditor 的 TBToolbarItemCollectionEditor 類別,並加入建構函式。此類別屬於 Bee.WebControls.Design 命名空間,通常我們會把設計階段使用的類別歸類到特別的命名空間便於管理及使用。</p> <pre><code>Namespace WebControls.Design    Public Class TBToolbarItemCollectionEditor        Inherits CollectionEditor        ''' &lt;summary&gt;        ''' 建構函式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Type&quot;&gt;型別。&lt;/param&gt;        Public Sub New(ByVal Type As Type)            MyBase.New(Type)        End Sub    End Class End Namespace </code></pre> <p>我們可以先修改 Items 屬性的 EditorAttribute,看看我們自訂的 TBToolbarItemCollectionEditor 是否能正常運作。不過這個屬性編輯器跟原本的沒什麼差異,因為我們只是單純繼承下來沒做任何異動,接下去我們就要開始來修改這個屬性編輯器。</p> <pre><code>&lt; _ Editor(GetType(TBToolbarItemCollectionEditor), GetType(UITypeEditor)) _ &gt; _ Public ReadOnly Property Items() As TBToolbarItemCollection </code></pre> <p><strong>二、加入不同型別的集合成員</strong><br /> 再來我們就要著手修改集合屬性編輯器,讓它可以加入不同型別的集合成員。覆寫 CollectionEditor 的 CanSelectMultipleInstances 方法傳回 True,這個方法是設定 CollectionEditor 是否允許加入多種不同型別的集合成員。</p> <pre><code>        Protected Overrides Function CanSelectMultipleInstances() As Boolean            Return True        End Function </code></pre> <p>再來覆寫 CreateNewItemTypes 方法,這個方法是取得這個集合編輯器可包含的資料型別,將集合可包含的資料型別以陣列傳回。</p> <pre><code>        ''' &lt;summary&gt;        ''' 取得這個集合編輯器可包含的資料型別。        ''' &lt;/summary&gt;        ''' &lt;returns&gt;這個集合可包含的資料型別陣列。&lt;/returns&gt;        Protected Overrides Function CreateNewItemTypes() As System.Type()            Dim ItemTypes(2) As System.Type            ItemTypes(0) = GetType(TBToolbarButton)            ItemTypes(1) = GetType(TBToolbarTextbox)            ItemTypes(2) = GetType(TBToolbarLabel)            Return ItemTypes        End Function </code></pre> <p>重建控制項組件,使用 Items 的集合屬性編輯器,就可以發現「加入」鈕的下拉清單就會出現我們所定義的三種型別的集合成員,如此可以加入不同型別的成員了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay18_6E6C/image_2.png" alt="" /></p> <p><strong>三、設定清單項目的顯示文字</strong><br /> 在成員清單項目中預設會顯示成員含命名空間的型別,若我們要修改成比較有識別的顯示文字,例如 TBToolbarButton(Key=Add) 可以顯示「按鈕-Add」,這時可以覆寫 GetDisplayText 方法來設定清單項目的顯示文字。</p> <pre><code>        ''' &lt;summary&gt;        ''' 取出指定清單項目的顯示文字。        ''' &lt;/summary&gt;        Protected Overrides Function GetDisplayText(ByVal value As Object) As String            If TypeOf value Is TBToolbarButton Then                Return String.Format(&quot;按鈕 - {0}&quot;, CType(value, TBToolbarButton).Key)            ElseIf TypeOf value Is TBToolbarTextbox Then                Return &quot;文字框&quot;            ElseIf TypeOf value Is TBToolbarLabel Then                Return String.Format(&quot;標籤 - {0}&quot;, CType(value, TBToolbarLabel).Text)            Else                Return value.GetType.Name            End If        End Function </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay18_6E6C/image_4.png" alt="" /></p> <p><strong>四、集合編輯器的屬性視窗的屬性描述</strong><br /> 一般屬性視窗下面都會有屬性描述,可以集合屬性編輯器中的屬性視窗下面竟沒有屬性描述。若我們要讓它的屬性描述可以顯示,可以覆寫 CreateCollectionForm 方法,取得集合屬性編輯表單,再去設定表單上的 PropertyGrid.HelpVisible<br /> = True 即可。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay18_6E6C/image_6.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/19/5721.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/19/5721.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-19 00:13:21</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day17] 集合屬性包含不同型別的成員</title>                <link>https://ithelp.ithome.com.tw/articles/10012600?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012600?sc=rss.iron</guid>                <description><![CDATA[<p>我們知道在 GridView 的 Columns 集合屬性中,可以包含不同型別的欄位,如 BoundFIeld、CheckBoxField、HyperLinkField ...等不同型別的欄位。...]]></description>                                    <content:encoded><![CDATA[<p>我們知道在 GridView 的 Columns 集合屬性中,可以包含不同型別的欄位,如 BoundFIeld、CheckBoxField、HyperLinkField ...等不同型別的欄位。如果我們希望工具列中不只包含按鈕,可以包含其他不同類型的子控制項,那該怎麼做呢?本文就以上篇中的 TBToolbar 控制項為案例,讓 Items 集合屬性可以加入 Button、TextBox、Label ...等不同的子控制項。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081017235034673.rar" target="_blank">ASP.NET Server Control - Day17.rar</a><br /> <strong>一、不同型別的集合成員</strong><br /> 我們的需求是讓工具列可以加入 Button、TextBox、Label 三種子控制項,所以繼承原來的 TBToolbarItem (只保留 Enabled 屬性),新增了 TBToolbarButton、TBToolbarTextbox、TBToolbarLabel 三個類別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay17_1430A/image_6.png" alt="" /></p> <p>這些新增的成員類別都是繼承至 TBToolbarItem,所以在 aspx 程式碼中,手動輸入 Items 的成員時,就會列出這幾種定義的成員型別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay17_1430A/image_2.png" alt="" /></p> <p><strong>二、建立不同型別集合成員的子控制項</strong><br /> 因為 Items 屬性的成員具不同型別,所以我們要改寫 RenderContents 方法,判斷成員型別來建立對應類型的子控制項。若為 TBToolbarButton 型別建立 Button 控制項、若為 TBToolbarTextbox 型別則建立 TextBox 控制項、若為 TBToolbarLabel 型別則建立 Label 控制項。其中 TBToolbarButton 建立的控制項為 TBButton,這個控制項是我們在「 <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/04/5578.aspx" target="_blank">[ASP.NET 控制項實作 Day3] 擴展現有伺服器控制項功能</a>」一文中實作的具詢問訊息的按鈕控制項。</p> <pre><code>        ''' &lt;summary&gt;        ''' 覆寫 RenderContents 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)            Dim oItem As TBToolbarItem            Dim oControl As Control            For Each oItem In Me.Items                If TypeOf oItem Is TBToolbarButton Then                    '建立 Button 控制項                    oControl = CreateToolbarButton(CType(oItem, TBToolbarButton))                ElseIf TypeOf oItem Is TBToolbarTextbox Then                    '建立 Textbox 控制項                    oControl = CreateToolbarTextbox(CType(oItem, TBToolbarTextbox))                Else                    '建立 Label 控制項                    oControl = CreateToolbarLabel(CType(oItem, TBToolbarLabel))                End If                Me.Controls.Add(oControl)            Next            MyBase.RenderContents(writer)        End Sub        ''' &lt;summary&gt;        ''' 建立工具列按鈕。        ''' &lt;/summary&gt;        Private Function CreateToolbarButton(ByVal Item As TBToolbarButton) As Control            Dim oButton As TBButton            Dim sScript As String            oButton = New TBButton()            oButton.Text = Item.Text            oButton.Enabled = Item.Enabled            oButton.ID = Item.Key            oButton.ConfirmMessage = Item.ConfirmMessage            sScript = Me.Page.ClientScript.GetPostBackEventReference(Me, Item.Key)            oButton.OnClientClick = sScript            Return oButton        End Function        ''' &lt;summary&gt;        ''' 建立工具列文字框。        ''' &lt;/summary&gt;        Private Function CreateToolbarTextbox(ByVal Item As TBToolbarTextbox) As Control            Dim oTextBox As TextBox            oTextBox = New TextBox            Return oTextBox        End Function        ''' &lt;summary&gt;        ''' 建立工具列標籤。        ''' &lt;/summary&gt;        Private Function CreateToolbarLabel(ByVal Item As TBToolbarLabel) As Control            Dim oLabel As Label            oLabel = New Label()            oLabel.Text = Item.Text            Return oLabel        End Function </code></pre> <p>我們手動在 aspx 程式碼中輸入不同型別的成員,TBToolbar 控制項就會呈現對應的子控制項。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay17_1430A/image_8.png" alt="" /></p> <p><strong>三、執行程式</strong><br /> 執行程式,就可以在瀏覽器看到呈現的工具列,當按下「刪除」時也會出現我們定義的詢問訊息。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay17_1430A/image_4.png" alt="" /></p> <p>輸出的 HTML 碼如下</p> <pre><code>&lt;span id=&quot;TBToolbar1&quot;&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Add&quot; value=&quot;新增&quot; onclick=&quot;__doPostBack('TBToolbar1','Add');&quot; id=&quot;TBToolbar1_Add&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Edit&quot; value=&quot;修改&quot; onclick=&quot;__doPostBack('TBToolbar1','Edit');&quot; id=&quot;TBToolbar1_Edit&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Delete&quot; value=&quot;刪除&quot; onclick=&quot;if (confirm('確定刪除嗎?')==false) {return false;}__doPostBack('TBToolbar1','Delete');&quot; id=&quot;TBToolbar1_Delete&quot; /&gt; &lt;span&gt;關鍵字&lt;/span&gt; &lt;input name=&quot;TBToolbar1$ctl01&quot; type=&quot;text&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Search&quot; value=&quot;搜尋&quot; onclick=&quot;__doPostBack('TBToolbar1','Search');&quot; id=&quot;TBToolbar1_Search&quot; /&gt; &lt;/span&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/18/5718.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/18/5718.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-18 00:05:57</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day16] 繼承 WebControl 實作 Toolbar 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012507?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012507?sc=rss.iron</guid>                <description><![CDATA[<p>前面我們討論過「繼承 CompositeControl 實作 Toolbar 控制項」,本文將繼承 WebControl 來實作同樣功能的 Toolbar 控制項,用不同的方式來實作同一個控制項...]]></description>                                    <content:encoded><![CDATA[<p>前面我們討論過「繼承 CompositeControl 實作 Toolbar 控制項」,本文將繼承 WebControl 來實作同樣功能的 Toolbar 控制項,用不同的方式來實作同一個控制項,進而比較二者之間的差異。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081016225639603.rar" target="_blank">ASP.NET Server Control - Day16.rar</a></p> <p><strong>一、繼承 WebControl 實作 TBToolbar 控制項</strong><br /> <strong>step1. 新增繼承 WebControl 的 TBToolbar 控制項</strong><br /> 新增繼承 WebControl 的 TBToolbar 控制項,你也可以直接原修改原 TBToolbar 控制項,繼承對象由 CompositeControl 更改為 WebControl即可。跟之前一樣在 TBToolbar 控制項加入 Items 屬性及 Click 事件。<br /> 另外 TBToolbar 控制項需實作 INamingContainer 界面,此界面很特殊沒有任何屬性或方法,INamingContainer 界面的作用是子控制項的 ClientID 會在前面加上父控制項的 ClickID,使每個子控制項有唯一的 ClientID。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15WebControlToolbar_12C6D/image_thumb_3.png" alt="" /></p> <p><strong>step2. 建立工具列按鈕集合</strong><br /> 覆寫 RenderContents 方法,將原本 TBToolbar (複合控制項) 的 CreateChildControls 方法中建立工具列按鈕程式碼,搬移至 RenderContents 方法即可。</p> <pre><code>        Private Sub ButtonClickEventHandler(ByVal sender As Object, ByVal e As EventArgs)            Dim oButton As Button            Dim oEventArgs As ClickEventArgs            oButton = CType(sender, Button)            oEventArgs = New ClickEventArgs()            oEventArgs.Key = oButton.ID            OnClick(oEventArgs)        End Sub        ''' &lt;summary&gt;        ''' 覆寫 RenderContents 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)            Dim oItem As TBToolbarItem            Dim oButton As Button            For Each oItem In Me.Items                oButton = New Button()                oButton.Text = oItem.Text                oButton.Enabled = oItem.Enabled                oButton.ID = oItem.Key                AddHandler oButton.Click, AddressOf ButtonClickEventHandler                Me.Controls.Add(oButton)            Next            If Me.Items.Count = 0 AndAlso Me.DesignMode Then                oButton = New Button()                oButton.Text = &quot;請設定 Items 屬性。&quot;                Me.Controls.Add(oButton)            End If            MyBase.RenderContents(writer)        End Sub </code></pre> <p>上述的直接搬移過來的程式碼還有個問題,就是原來的使用 AddHandler 來處理按鈕事件的方式變成沒有作用了?因為現在不是複合式控制項,當前端的按鈕 PostBack 傳回伺服端時,TBToolbar 不會事先建立子控制槓,所以機制會找不到原來產生的按鈕,也就無法使用 AddHandler 來處理事件了。</p> <pre><code>AddHandler oButton.Click, AddressOf ButtonClickEventHandler </code></pre> <p><strong>step3. 處理 Click 事件</strong><br /> 因為不能使用 AddHandler 來處理按鈕事件,所以我們就自行使用 Page.ClientScript.GetPostBackEventReference 方法來產生 PostBack 動作的用戶端指令碼,按鈕的 OnClientClick 去執行 PostBack 的動作。</p> <pre><code>            For Each oItem In Me.Items                oButton = New Button()                oButton.Text = oItem.Text                oButton.Enabled = oItem.Enabled                oButton.ID = oItem.Key                sScript = Me.Page.ClientScript.GetPostBackEventReference(Me, oItem.Key)                oButton.OnClientClick = sScript                Me.Controls.Add(oButton)            Next </code></pre> <p>TBToolar 控制項輸出的 HTML 碼如下</p> <pre><code>&lt;span id=&quot;TBToolbar1&quot;&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Add&quot; value=&quot;新增&quot; onclick=&quot;__doPostBack('TBToolbar1','Add');&quot; id=&quot;TBToolbar1_Add&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Edit&quot; value=&quot;修改&quot; onclick=&quot;__doPostBack('TBToolbar1','Edit');&quot; id=&quot;TBToolbar1_Edit&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Delete&quot; value=&quot;刪除&quot; onclick=&quot;__doPostBack('TBToolbar1','Delete');&quot; id=&quot;TBToolbar1_Delete&quot; /&gt; &lt;/span&gt; </code></pre> <p>要自行處理 PostBack 的事件,需實作 IPostBackEventHandler 介面,在 RaisePostBackEvent 方法來引發 TBToolbar 的 Click 事件。</p> <pre><code>    Public Class TBToolbar        Inherits WebControl        Implements INamingContainer        Implements IPostBackEventHandler        Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent            Dim oEventArgs As ClickEventArgs            oEventArgs = New ClickEventArgs()            oEventArgs.Key = eventArgument            Me.OnClick(oEventArgs)        End Sub    End Class </code></pre> <p><strong>二、測試程式</strong><br /> 在測試頁面上放置 TBToolbar 控制項,在 Click 事件撰寫測試程式碼。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15WebControlToolbar_12C6D/image_thumb.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/17/5706.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/17/5706.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-17 00:05:40</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day15] 複合控制項隱藏的問題</title>                <link>https://ithelp.ithome.com.tw/articles/10012425?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012425?sc=rss.iron</guid>                <description><![CDATA[<p>上一篇我們使用複合控制項(繼承 CompositeControl)的方式來實作 TBToolbar 控制項,本文將針對複合控制項做一些測試,說明在使用複合控制項要注意的一些問題。<br /> 程...]]></description>                                    <content:encoded><![CDATA[<p>上一篇我們使用複合控制項(繼承 CompositeControl)的方式來實作 TBToolbar 控制項,本文將針對複合控制項做一些測試,說明在使用複合控制項要注意的一些問題。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008101612954661.rar" target="_blank">ASP.NET Server Control - Day15.rar</a><br /> <strong>一、複合控制項建立子控制項的時機</strong><br /> 還記得我們之前介紹複合控制項時有談到 CompositeControl 類別會確保我們存取子控制項時,它的子控制項一定會事先建立;也就是當我們使用 Controls 屬性去存取子控制項時,一定會執行 CreateChildControls 方法,以確保子控制項事先被建立。我們看一下 CompositeControl 類別的 Controls 屬性的寫法就可以了解其中的原由,在存取 CompositeControl.Controls 屬性時,它會先執行 Control.EnsureChildControls 方法;而 EnsureChildControls 方法會去判斷子控制項是否已建立,若未建立會去執行 CreateChildControls 方法,這也就是為什麼 CompositeControl 有辨法確保子控制項事先被建立的原因。</p> <p>CompositeControl.Controls 屬性如下</p> <pre><code>Public Overrides ReadOnly Property Controls As ControlCollection    Get        Me.EnsureChildControls        Return MyBase.Controls    End Get End Property </code></pre> <p>Control.EnsureChildControls 方法如下</p> <pre><code>Protected Overridable Sub EnsureChildControls()    If (Not Me.ChildControlsCreated AndAlso Not Me.flags.Item(&amp;H100)) Then        Me.flags.Set(&amp;H100)        Try            Me.ResolveAdapter            If (Not Me._adapter Is Nothing) Then                Me._adapter.CreateChildControls            Else                Me.CreateChildControls            End If            Me.ChildControlsCreated = True        Finally            Me.flags.Clear(&amp;H100)        End Try    End If End Sub </code></pre> <p><strong>二、複合控制項隱藏的問題</strong><br /> 我們以上篇的 TBToolbar 控制項為例,撰寫一些測試案例來說明複合控制項的問題。在撰寫測試案例之前,我們先修改一下 TBToolbar 控制項,覆寫 LoadViewState 及 SaveViewState 方法,將 Items 屬性儲存於 ViewState 中以維持狀態。</p> <p>在測試頁面上放置「測試一」、「測試二」、「PostBack」三個按鈕,這三個按鈕的動作如下。<br /> 「測試一」按鈕:在工具列直接新增一個按鈕。<br /> 「測試二」按鈕:先使用 FindControl 取得工具列的按鈕,然後在在工具列再新增一個按鈕。<br /> 「PostBack」按鈕:單純執行 PostBack,不撰寫程式碼。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb.png" alt="" /></p> <p>三個按鈕的程式碼如下所示。</p> <pre><code>    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click        Dim oItem As TBToolbarItem        '加入新按鈕        oItem = New TBToolbarItem()        oItem.Text = &quot;新按鈕&quot;        oItem.Key = &quot;NewButton&quot;        TBToolbar1.Items.Add(oItem)        Me.Response.Write(&quot;「測試一」按鈕&quot;)    End Sub    Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click        Dim oItem As TBToolbarItem        Dim oButton As Button        '先執行 FindControl 去取得 ID=&quot;Add&quot; 的按鈕        oButton = TBToolbar1.FindControl(&quot;Add&quot;)        '再加入新按鈕        oItem = New TBToolbarItem()        oItem.Text = &quot;新按鈕&quot;        oItem.Key = &quot;NewButton&quot;        TBToolbar1.Items.Add(oItem)        Me.Response.Write(&quot;「測試二」按鈕&quot;)    End Sub    Protected Sub Button3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button3.Click        '單純 PostBack,無程式碼        Me.Response.Write(&quot;「PostBack」按鈕&quot;)    End Sub </code></pre> <p>案例一:執行「測試一」按鈕,在工具列直接新增一個按鈕。<br /> 當按下「測試一」按鈕時,工具列可以正常加入我們新增的按鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb_1.png" alt="" /></p> <p>案例二:執行「測試二」按鈕,先使用 FindControl 取得工具列的按鈕,然後在在工具列再新增一個按鈕。<br /> 重新執行程式,當按下「測試二」按鈕時,你會發現奇怪的現象,工具列竟然沒有加入我們新增的按鈕?<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb_2.png" alt="" /></p> <p>此時再按下「PostBack」按鈕,工具列才會出現我們剛剛加入的按鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb_3.png" alt="" /></p> <p>為什麼會發生這種怪現象呢?其實原因很簡單,因為 FindControl 時會去存取 Controls 屬性,而這時子控制項已經被建立了;而之前再用 Items 屬性加入新按鈕,它已經不會在重建子控制項,導致第一時間沒有加入新按鈕。不過 Items 屬性會被存在 ViewState 中,所以當執行「PostBack」按鈕時,就會出現我們剛剛新增的按鈕。</p> <p><strong>三、解決方式</strong><br /> 要解決上述「測試二」的問題,只要覆寫 TBToolbar 控制項的 Render 方法,在 Render 前執行 RecreateChildControls 方法,強制重建子控制項。</p> <pre><code>        ''' &lt;summary&gt;        ''' 覆寫 Render 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)            Me.RecreateChildControls()            MyBase.Render(writer)        End Sub </code></pre> <p>再一次執行「測試二」的動作,就會發現執行結果就會正常了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb_5.png" alt="" /></p> <p><strong>四、結語</strong><br /> 在複合控制項的 Render 前執行 RecreateChildControls 方法可以強制重建子控制項,可是這樣又會引發另一個問題,那就是當直接存取子控制項去修改子控制項的屬性後,一旦在 Render 又重建子控制項,那之前設定子控制項狀態又被全部重建了,所以需特別注意有這樣的情形。另外複合控制項有可能重覆執行建立子控制的動作,在執行效能上也比較不佳。</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/16/5695.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/16/5695.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-16 00:14:12</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day14] 繼承 CompositeControl 實作 Toolbar 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012339?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012339?sc=rss.iron</guid>                <description><![CDATA[<p>之前我們簡單介紹過繼承 CompositeControl 來實作複合控制項,在本文我們將以 Toolbar 控制項為例,以複合控制項的作法(繼承 CompositeControl )來實作 To...]]></description>                                    <content:encoded><![CDATA[<p>之前我們簡單介紹過繼承 CompositeControl 來實作複合控制項,在本文我們將以 Toolbar 控制項為例,以複合控制項的作法(繼承 CompositeControl )來實作 Toolbar 控制項,此工具列控制項包含 Items 屬性來描述工具列項目集合,依 Items 屬性的設定來建立工具列按鈕,另外包含 Click 事件可以得知使用按了那個按鈕。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081014234924792.rar" target="_blank">ASP.NET Server Control - Day14.rar</a><br /> <strong>一、工具列項目集合類別</strong><br /> 工具列包含多個按鈕,新增 TBToolbarItem 類別來描述工具列項目,TBToolbarItem 類別包含 Key、Text、Enabled 三個屬性;而 TBToolbarItemCollection 為 TBToolbarItem 的集合類別來描述工具列按鈕集合。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay14CompositeControlToolbar_12481/image_thumb.png" alt="" /></p> <p><strong>二、實作 TBToolbar 控制項</strong><br /> <strong>step1. 新增繼承 CompositeControl 的 TBToolbar 控制項</strong></p> <pre><code>    &lt; _    Description(&quot;工具列控制項。&quot;), _    ParseChildren(True, &quot;Items&quot;), _    ToolboxData(&quot;&lt;{0}:TBToolbar runat=server &gt;&lt;/{0}:TBToolbar&gt;&quot;) _    &gt; _    Public Class TBToolbar        Inherits CompositeControl    End Class </code></pre> <p><strong>step2. 新增 Items 屬性,描述工具列項目集合</strong></p> <pre><code>        ''' &lt;summary&gt;        ''' 工具列項目集合。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;工具列項目集合。&quot;), _        PersistenceMode(PersistenceMode.InnerProperty), _        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _        Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _        &gt; _        Public ReadOnly Property Items() As TBToolbarItemCollection            Get                If FItems Is Nothing Then                    FItems = New TBToolbarItemCollection()                End If                Return FItems            End Get        End Property </code></pre> <p><strong>step3. 新增 Click 事件</strong><br /> TBToolbar 類別新增 Click 事件,當按下按鈕時會引發 Click 事件,由 Click 的事件引數 e.Key 可以得知使用者按了那個按鈕。</p> <pre><code>        ''' &lt;summary&gt;        ''' Click 事件引數。        ''' &lt;/summary&gt;        Public Class ClickEventArgs            Inherits System.EventArgs            Private FKey As String = String.Empty            ''' &lt;summary&gt;            ''' 項目鍵值。            ''' &lt;/summary&gt;            Public Property Key() As String                Get                    Return FKey                End Get                Set(ByVal value As String)                    FKey = value                End Set            End Property        End Class        ''' &lt;summary&gt;        ''' 按下工具列按鈕所引發的事件。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;按下工具列按鈕所引發的事件。&quot;) _        &gt; _        Public Event Click(ByVal sender As Object, ByVal e As ClickEventArgs)        ''' &lt;summary&gt;        ''' 引發 Click 事件。        ''' &lt;/summary&gt;        Protected Overridable Sub OnClick(ByVal e As ClickEventArgs)            RaiseEvent Click(Me, e)        End Sub </code></pre> <p><strong>step4. 建立工具列按鈕集合</strong><br /> 覆寫 CreateChildControls 方法,依 Items 屬性的設定,來建立工具列中的按鈕集合。每個按鈕的 Click 事件都導向 ButtonClickEventHandler 方法,來處理所有按鈕的 Click 動作,並引發 TBToolbar 的 Click 事件。</p> <pre><code>        Private Sub ButtonClickEventHandler(ByVal sender As Object, ByVal e As EventArgs)            Dim oButton As Button            Dim oEventArgs As ClickEventArgs            oButton = CType(sender, Button)            oEventArgs = New ClickEventArgs()            oEventArgs.Key = oButton.ID            OnClick(oEventArgs)        End Sub        ''' &lt;summary&gt;        ''' 建立子控制項。        ''' &lt;/summary&gt;        Protected Overrides Sub CreateChildControls()            Dim oItem As TBToolbarItem            Dim oButton As Button            For Each oItem In Me.Items                oButton = New Button()                oButton.Text = oItem.Text                oButton.Enabled = oItem.Enabled                oButton.ID = oItem.Key                AddHandler oButton.Click, AddressOf ButtonClickEventHandler                Me.Controls.Add(oButton)            Next            MyBase.CreateChildControls()        End Sub </code></pre> <p><strong>三、測試程式</strong><br /> 在頁面拖曳 TBToolbar 控制項,並設定 Items 屬性,如入新增、修改、刪除三個按鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay14CompositeControlToolbar_12481/image_thumb_1.png" alt="" /></p> <p>在 TBToolbar 控制項的 Click 事件加入測試程式碼,輸出引發 Click 事件的 e.Key。</p> <pre><code>    Protected Sub TBToolbar1_Click(ByVal sender As Object, ByVal e As Bee.Web.WebControls.TBToolbar.ClickEventArgs) Handles TBToolbar1.Click        Me.Response.Write(String.Format(&quot;您按了 {0}&quot;, e.Key))    End Sub </code></pre> <p>執行程式,當按了工具列上的按鈕時,就會引發 Click 事件,並輸出該按鈕對應的 Key。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay14CompositeControlToolbar_12481/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/15/5687.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/15/5687.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-15 00:13:50</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day13] Flash 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012267?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012267?sc=rss.iron</guid>                <description><![CDATA[<p>Flash 也是網頁常用的 ActiveX 插件,在本文中將繼承 TBActiveX 下來撰寫 TBFlash 控制項,用來輸出網頁套用 Flash 的相關 HTML 碼。<br /> 程式碼下...]]></description>                                    <content:encoded><![CDATA[<p>Flash 也是網頁常用的 ActiveX 插件,在本文中將繼承 TBActiveX 下來撰寫 TBFlash 控制項,用來輸出網頁套用 Flash 的相關 HTML 碼。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008101323519608.rar" target="_blank">ASP.NET Server Control - Day13.rar</a></p> <p><strong>一、網頁 Flash 的原始 HTML 碼</strong><br /> 我們先觀查在網頁中套用 Flash 插件的原始 HTML 碼,以點部落首頁抬頭的 Flash 原始碼為例如下,其中 &lt;object&gt; tag 的 codebase attribute 是指 Flash 插件的下載位置及版本。</p> <pre><code>&lt;object id=&quot;ShockwaveFlash2&quot; height=&quot;90&quot; width=&quot;728&quot;  codebase=&quot;http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0&quot;  classid=&quot;clsid:D27CDB6E-AE6D-11cf-96B8-444553540000&quot;&gt; &lt;param value=&quot;http://files.dotblogs.com.tw/dotjum/ad/debug.swf&quot; name=&quot;movie&quot;/&gt; &lt;param value=&quot;high&quot; name=&quot;quality&quot;/&gt; &lt;param value=&quot;#000000&quot; name=&quot;bgcolor&quot;/&gt; &lt;embed height=&quot;90&quot; width=&quot;728&quot; type=&quot;application/x-shockwave-flash&quot;  pluginspage=&quot;http://www.macromedia.com/go/getflashplayer&quot; quality=&quot;high&quot;  src=&quot;http://files.dotblogs.com.tw/dotjum/ad/debug.swf&quot;/&gt; &lt;/object&gt; </code></pre> <p>在 &lt;object&gt; tag 中必要的 attribute 為 classid、codebase、movie、width、height,而 &lt;embed&gt; tag 的必要 attribute 為 src、pluginspage、width、height,其他選擇性的 attribute 可參閱以下網頁。</p> <p>Flash OBJECT and EMBED tag attributes<br /> <a href="http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701" target="_blank"><a href="http://kb.adobe.com/selfservice/viewContent.do?externalId=tn%5C_12701" target="_blank">http://kb.adobe.com/selfservice/viewContent.do?externalId=tn\_12701</a></a></p> <p><strong>二、實作 TFlash 控制項</strong><br /> 了解 Flash 的原始 HTML 碼後,我們就可以開始著手撰寫 TBFlash 控制項,想辨法來輸出所需要的 HTML 碼。</p> <p><strong>step1. 新增 TBFlash 控制項繼承至 TBActiveX</strong><br /> 我們先在 TBActiveX 控制項新增一個 CodeBase 屬性,用來設定 ActiveX 插入的下載位置及版本,然後新增 TBFlash 控制項繼承至 TBActiveX,並在建構函式中設定 MyBase.ClassId 及 MyBase.CodeBase 屬性。</p> <pre><code>    Public Class TBFlash        Inherits TBActiveX        ''' &lt;summary&gt;        ''' 建構函式。        ''' &lt;/summary&gt;        Sub New()            MyBase.ClassId = &quot;D27CDB6E-AE6D-11CF-96B8-444553540000&quot;            MyBase.CodeBase = &quot;http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0&quot;        End Sub    End Class </code></pre> <p><strong>step2. 加入相關屬性</strong><br /> 在 TBFlash 加入 MovieUrl 及 Quality 屬性,MovieUrl 為 Flash 檔案來源,Quality 為影音品質。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay13Flash_13AEC/image_2.png" alt="" /></p> <p><strong>step3. 輸出 Flash 相關參數</strong><br /> 覆寫 CreateChildControls 方法,輸出 MovieUrl 及 Quality 屬性對應的參數,以及在 Params 集合屬性設定的參數。</p> <pre><code>        ''' &lt;summary&gt;        ''' 加入 MediaPlayer 參數。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Name&quot;&gt;參數名稱。&lt;/param&gt;        ''' &lt;param name=&quot;Value&quot;&gt;參數值。&lt;/param&gt;        Private Sub AddParam(ByVal Name As String, ByVal Value As String)            Dim oParam As TBActiveXParam            oParam = New TBActiveXParam(Name, Value)            Me.Params.Add(oParam)        End Sub        ''' &lt;summary&gt;        ''' 建立 Embed 標記。        ''' &lt;/summary&gt;        Private Function CreateEmbed() As HtmlControls.HtmlGenericControl            Dim oEmbed As HtmlControls.HtmlGenericControl            Dim oParam As TBActiveXParam            oEmbed = New HtmlControls.HtmlGenericControl()            oEmbed.TagName = &quot;embed&quot;            oEmbed.Attributes(&quot;src&quot;) = Me.ResolveClientUrl(Me.MovieUrl)            oEmbed.Attributes(&quot;pluginspage&quot;) = &quot;http://www.macromedia.com/go/getflashplayer&quot;            oEmbed.Attributes(&quot;height&quot;) = Me.Height.ToString            oEmbed.Attributes(&quot;width&quot;) = Me.Width.ToString            'Embed 的 Attributes 加入 Params 集合屬性的設定            For Each oParam In Me.Params                If oParam.Name &lt;&gt; &quot;movie&quot; Then                    oEmbed.Attributes(oParam.Name) = oParam.Value                End If            Next            Return oEmbed        End Function        ''' &lt;summary&gt;        ''' 建立子控制項。        ''' &lt;/summary&gt;        Protected Overrides Sub CreateChildControls()            Dim oEmbed As HtmlControls.HtmlGenericControl            '加入 movie 參數            AddParam(&quot;movie&quot;, Me.ResolveClientUrl(Me.MovieUrl))            '加入 quality 參數            If Me.Quality &lt;&gt; EQuality.NotSet Then                AddParam(&quot;quality&quot;, Me.Quality.ToString.ToLower)            End If            MyBase.CreateChildControls()            oEmbed = CreateEmbed()            Me.Controls.Add(oEmbed)        End Sub </code></pre> <p><strong>三、測試程式</strong><br /> 在頁面拖曳 TBFlash 控制項,設定 MovieUrl 及 Quality 屬性,若有需要加入其他參數,可自行設定 Params 集合屬性。執行程式就可以在頁面上看到呈現出來的 Flash。</p> <pre><code>        &lt;bee:TBFlash ID=&quot;TBFlash1&quot; runat=&quot;server&quot; Height=&quot;90px&quot;            MovieUrl=&quot;http://files.dotblogs.com.tw/dotjum/ad/debug.swf&quot; Quality=&quot;High&quot;            Width=&quot;728px&quot;&gt;        &lt;/bee:TBFlash&gt; </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay13Flash_13AEC/image_4.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/14/5674.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/14/5674.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-14 00:16:30</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day12] 繼承 TBActiveX 重新改寫 TBMediaPlayer 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012196?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012196?sc=rss.iron</guid>                <description><![CDATA[<p>上篇介紹的 TBActiveX 控制項,它可以支援網頁 Media Player 的設定,這跟前面提及的 TBMediaPlayer 功能相同。TBActiveX 具有網頁設定 ActiveX ...]]></description>                                    <content:encoded><![CDATA[<p>上篇介紹的 TBActiveX 控制項,它可以支援網頁 Media Player 的設定,這跟前面提及的 TBMediaPlayer 功能相同。TBActiveX 具有網頁設定 ActiveX 通用屬性,所以 TBMediaPlayer 基本上是可以由 TBActiveX 繼承下來,再加入 Media Player 特有的屬性即可。本文將原來的 TBMediaPlayer 控制項,繼承的父類別由 WebControl 改為 TBActiveX 類別,重新改寫 TBMediaPlayer 控制項。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/200810130325962.rar" target="_blank">ASP.NET Server Control - Day12.rar</a></p> <p><strong>一、改寫 TBMediaPlayer 控制項</strong><br /> TBMediaPlayer 控制項原本是繼承 WebControl,現改繼承對象為 TBActiveX,來重新改寫 TBMediaPlayer 控制項。</p> <p><strong>step1. TBMediaPlayer 繼承至 TBActiveX</strong><br /> 新增 TBMediaPlayer 控制項,繼承至 TBActiveX,並在建構函式設定 Media Player ActiveX 的 ClassId。</p> <pre><code>    Public Class TBMediaPlayer        Inherits TBActiveX        ''' &lt;summary&gt;        ''' 建構函式。        ''' &lt;/summary&gt;        Sub New()            MyBase.ClassId = &quot;6BF52A52-394A-11D3-B153-00C04F79FAA6&quot;        End Sub    End Class </code></pre> <p><strong>step2. 加入相關屬性</strong><br /> 跟原來的 TBMediaPlayer 控制項一樣,加入 Url、AutoStart、UIMode 三個屬性,可視情形加入需要設定的屬性。</p> <p><strong>step3. 加入 Media Player 參數</strong><br /> 覆寫 CreateChildControls 方法,動態依屬性設定在 Params 集合屬性加入參數。雖然 TBMediaPlayer 控制項目前只有 Url、AutoStart、UIMode 三個屬性,但是父類別 TBActiveX 具有 Params 集合屬性,所以開發人員可以視需求加入其他未定義的參數。</p> <pre><code>        ''' &lt;summary&gt;        ''' 加入 MediaPlayer 參數。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Name&quot;&gt;參數名稱。&lt;/param&gt;        ''' &lt;param name=&quot;Value&quot;&gt;參數值。&lt;/param&gt;        Private Sub AddParam(ByVal Name As String, ByVal Value As String)            Dim oParam As TBActiveXParam            oParam = New TBActiveXParam(Name, Value)            Me.Params.Add(oParam)        End Sub        ''' &lt;summary&gt;        ''' 覆寫 CreateChildControls 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub CreateChildControls()            '加入 Url 參數            If Me.Url &lt;&gt; String.Empty Then                AddParam(&quot;URL&quot;, Me.ResolveClientUrl(Me.Url))            End If            '加入 autoStart 參數            If Me.AutoStart Then                AddParam(&quot;autoStart&quot;, &quot;true&quot;)            End If            '加入 uiMode 參數            If Me.UIMode &lt;&gt; EUIMode.NotSet Then                AddParam(&quot;uiMode&quot;, Me.UIMode.ToString)            End If            MyBase.CreateChildControls()        End Sub </code></pre> <p><strong>二、執行程式</strong><br /> 在頁面拖曳 TBMediaPlayer 控制項,設定 Url、AutoStart、UIMode 屬性,若有需要加入其他參數,可自行設定 Params 集合屬性。執行程式就可以在頁面上看到呈現出來的 Media Player。</p> <pre><code>        &lt;bee:TBMediaPlayer ID=&quot;TBMediaPlayer1&quot; runat=&quot;server&quot; AutoStart=&quot;True&quot;            Height=&quot;249px&quot; Url=&quot;D:\Movie_01.wmv&quot; Width=&quot;250px&quot;&gt;        &lt;/bee:TBMediaPlayer&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/13/5663.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/13/5663.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-13 00:13:29</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day11] ActiveX 伺服器控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012159?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012159?sc=rss.iron</guid>                <description><![CDATA[<p>Media Player 與 Flash 之類在網頁上執行的外掛控制項,都是屬於 ActiveX 控制項,它們套用在 HTML 碼中的方式差不多,除了要指定 ClassID 以外,ActiveX...]]></description>                                    <content:encoded><![CDATA[<p>Media Player 與 Flash 之類在網頁上執行的外掛控制項,都是屬於 ActiveX 控制項,它們套用在 HTML 碼中的方式差不多,除了要指定 ClassID 以外,ActiveX 使用的參數(相當於 ActiveX 控制項的屬性)以 Param Tag 來表示。本文標題命名為「ActiveX 伺服器控制項」就是避免誤解為 ActiveX 控制項,而是在 ASP.NET 中輸出 ActiveX 相關 HTML 碼的伺服器控制項;我們可透過 ActiveX 伺服器控制項可以用來輸出網頁上引用 ActiveX 的通用 HTML 碼,另外 ActiveX 的參數會以集合屬性來呈現,所以也會一併學習到集合屬性的撰寫方式。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/200810124846794.rar" target="_blank">ASP.NET Server Control - Day11.rar</a></p> <p><strong>一、集合屬性</strong><br /> ActiveX 的 Param 參數是集合屬性,所以我們定義了 TBActiveParam 類別描述 ActiveX 參數,包含 Name 及 Value 屬性;而 TBActiveXParamCollection 為 TBActiveParam 的集合類別,用來描述 ActiveX 參數集合。TBActiveXParamCollection 繼承 CollectionBase,加入操作集合的 Add、Insert、Remove、IndexOf、Contains 等方法,關於集合屬性的用法可以參閱筆者在部落格的「<a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1747.aspx" target="_blank">撰寫伺服器控制項的集合屬性 (CollectionBase)</a>」一文中有詳細說明。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay11ActiveX_166/image_thumb_1.png" alt="" /></p> <p><strong>二、實作 ActiveX 伺服器控制項</strong><br /> <strong>step1. 新增繼承 WebControl 的 TBActiveX</strong></p> <p><strong>step2. 覆寫 TagKey 屬性,傳回 object 的 Tag</strong></p> <pre><code>        Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag            Get                Return HtmlTextWriterTag.Object            End Get        End Property </code></pre> <p><strong>step3. 新增 ClassId 屬性,描述 ActiveX 的 ClassId</strong><br /> 定義 ClassId 屬性,並覆寫 AddAttributesToRender 來輸出此屬性。</p> <pre><code>        ''' &lt;summary&gt;        ''' 覆寫 AddAttributesToRender 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)            '加入 MediaPlayer ActiveX 元件的 classid            writer.AddAttribute(&quot;classid&quot;, String.Format(&quot;clsid:{0}&quot;, Me.ClassId))            MyBase.AddAttributesToRender(writer)        End Sub </code></pre> <p><strong>step4. 新增 Params 屬性,描述 ActiveX 的參數集合</strong><br /> 定義 Params 屬性,型別為 TBActiveXParamCollection 類別,套用 EditorAttribute 設定 CollectionEditor 為集合編輯器。</p> <pre><code>        ''' &lt;summary&gt;        ''' ActiveX 控制項參數集合。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;控制項參數集合。&quot;), _        PersistenceMode(PersistenceMode.InnerProperty), _        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _        Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _        &gt; _        Public ReadOnly Property Params() As TBActiveXParamCollection            Get                If FParams Is Nothing Then                    FParams = New TBActiveXParamCollection()                End If                Return FParams            End Get        End Property </code></pre> <p>當編輯 Params 時,會使用的 CollectionEditor 集合編輯器。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay11ActiveX_166/image_thumb_2.png" alt="" /></p> <p><strong>step5. 輸出 ActiveX 參數</strong><br /> 覆寫 CreateChildControls 方法,在此方法依 Params 集合屬性定義依序來輸出 ActiveX 的參數集合。</p> <pre><code>        Private Sub AddParam(ByVal Name As String, ByVal Value As String)            Dim oParam As HtmlControls.HtmlGenericControl            oParam = New HtmlControls.HtmlGenericControl(&quot;param&quot;)            oParam.Attributes.Add(&quot;name&quot;, Name)            oParam.Attributes.Add(&quot;value&quot;, Value)            Me.Controls.Add(oParam)        End Sub        ''' &lt;summary&gt;        ''' 建立子控制項。        ''' &lt;/summary&gt;        Protected Overrides Sub CreateChildControls()            Dim oParam As TBActiveXParam            '加入 ActiveX 參數集合            For Each oParam In Me.Params                AddParam(oParam.Name, oParam.Value)            Next            MyBase.CreateChildControls()        End Sub </code></pre> <p><strong>三、執行程式</strong><br /> 上一篇我們使用 TBMediaPlayer 控制項來設定 Media Player,在此我們改用 TBActiveX 控制項來設定 Media Player,一樣可以呈現相同的結果。</p> <pre><code>        &lt;bee:TBActiveX ID=&quot;TBActiveX1&quot; runat=&quot;server&quot;            ClassId=&quot;6BF52A52-394A-11D3-B153-00C04F79FAA6&quot; Height=&quot;250px&quot; Width=&quot;250px&quot;&gt;            &lt;Params&gt;                &lt;bee:TBActiveXParam Name=&quot;URL&quot; Value=&quot;d:/Movie_01.wmv&quot; /&gt;                &lt;bee:TBActiveXParam Name=&quot;autoStart&quot; Value=&quot;true&quot; /&gt;            &lt;/Params&gt;        &lt;/bee:TBActiveX&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/12/5659.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/12/5659.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-12 04:20:27</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day10] Media Player 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012142?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012142?sc=rss.iron</guid>                <description><![CDATA[<p>我們在前面幾篇文章中,已經簡要的對伺服器控制項做了基本介紹,接下來的幾篇文章中我們要開始實作伺服器控制項。在網頁上常使用 Media Player 來撥放影片,在 ASP.NET 中沒有現成的控...]]></description>                                    <content:encoded><![CDATA[<p>我們在前面幾篇文章中,已經簡要的對伺服器控制項做了基本介紹,接下來的幾篇文章中我們要開始實作伺服器控制項。在網頁上常使用 Media Player 來撥放影片,在 ASP.NET 中沒有現成的控制項來處理 Media Player,只能在 aspx 中加入 Media Player 相關的程式碼;本文將示範如何製作一個 Media Player 控制項,讓我們在 ASP.NET 中更方便的使用 Media Player。<br /> 程式碼下載:<a href="http://Files.Dotblogs.com.tw/jeff377%5C0810%5C2008101122230798.rar" target="_blank">ASP.NET Server Control - Day10.rar</a></p> <p><strong>一、Media Player 原始 HTML 碼</strong><br /> 在製作 Media Player 控制項之前,你需要先了解 Media Player 原本的 HTML 碼,控制項的作用就是想辨法把這些寫在 aspx 中的 HTML 碼交由控制項來輸出而已,以下為網頁中加入 Media Player 的 HTML 碼範例。</p> <pre><code>&lt;OBJECT id=&quot;VIDEO&quot; width=&quot;320&quot; height=&quot;240&quot; style=&quot;position:absolute; left:0;top:0;&quot; CLASSID=&quot;CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6&quot; type=&quot;application/x-oleobject&quot;&gt; &lt;PARAM NAME=&quot;URL&quot; VALUE=&quot;your file or url&quot;&gt; &lt;PARAM NAME=&quot;SendPlayStateChangeEvents&quot; VALUE=&quot;True&quot;&gt; &lt;PARAM NAME=&quot;AutoStart&quot; VALUE=&quot;True&quot;&gt; &lt;PARAM name=&quot;uiMode&quot; value=&quot;none&quot;&gt; &lt;PARAM name=&quot;PlayCount&quot; value=&quot;9999&quot;&gt; &lt;/OBJECT&gt; </code></pre> <p>在下面的網頁有 Media Player 相關參數說明。<br /> <a href="http://www.mioplanet.com/rsc/embed_mediaplayer.htm" target="_blank"><a href="http://www.mioplanet.com/rsc/embed%5C_mediaplayer.htm" target="_blank">http://www.mioplanet.com/rsc/embed\_mediaplayer.htm</a></a></p> <p><strong>二、實作 Media Player 控制項</strong><br /> <strong>step1.首先新增由 WebControl 繼承下來的 TBMediaPlayer 類別。</strong></p> <pre><code>    Public Class TBMediaPlayer        Inherits WebControl    End Class </code></pre> <p><strong>step2.覆寫 TagKey 屬性,傳回 object 的 Tag。</strong></p> <pre><code>        Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag            Get                Return HtmlTextWriterTag.Object            End Get        End Property </code></pre> <p><strong>step3.輸出 HTML Tag 的 Attribute</strong><br /> 在 object Tag 中包含 style、classid、type 二個 Attribute,WebControl 本身會處理 style,所以我們只需覆寫 AddAttributesToRender 方法,處理 classid 及 type 二個 Attribute,記得覆寫 AddAttributesToRender 方法時最後要加入 MyBase.AddAttributesToRender(writer),才會執行父類別的 AddAttributesToRender 方法。</p> <pre><code>        ''' &lt;summary&gt;        ''' 覆寫 AddAttributesToRender 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub AddAttributesToRender(ByVal writer As System.Web.UI.HtmlTextWriter)            '加入 MediaPlayer ActiveX 元件的 classid            writer.AddAttribute(&quot;classid&quot;, &quot;clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6&quot;)            writer.AddAttribute(&quot;type&quot;, &quot;application/x-oleobject&quot;)            MyBase.AddAttributesToRender(writer)        End Sub </code></pre> <p><strong>step4.加入 Url 屬性</strong><br /> 加入指定播放檔案來源的 Url 屬性,其中套用 EditorAttribute 設定 UrlEditor,使用 Url 專用的編輯器來設定屬性。</p> <pre><code>        ''' &lt;summary&gt;        ''' 播放檔案來源。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;播放檔案來源&quot;), _        Bindable(True), _        Category(&quot;Appearance&quot;), _        Editor(GetType(UrlEditor), GetType(UITypeEditor)), _        UrlProperty(), _        DefaultValue(&quot;&quot;) _        &gt; _        Public Property Url() As String            Get                Return FUrl            End Get            Set(ByVal value As String)                FUrl = value            End Set        End Property </code></pre> <p><strong>step5.輸出 Url 參數</strong><br /> 接下來覆寫 CreateChildControls 方法,輸出 Url 參數。</p> <pre><code>        ''' &lt;summary&gt;        ''' 加入參數。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Name&quot;&gt;參數名稱。&lt;/param&gt;        ''' &lt;param name=&quot;Value&quot;&gt;參數值。&lt;/param&gt;        Private Sub AddParam(ByVal Name As String, ByVal Value As String)            Dim oParam As HtmlControls.HtmlGenericControl            oParam = New HtmlControls.HtmlGenericControl(&quot;param&quot;)            oParam.Attributes.Add(&quot;name&quot;, Name)            oParam.Attributes.Add(&quot;value&quot;, Value)            Me.Controls.Add(oParam)        End Sub        Protected Overrides Sub CreateChildControls()            '加入 Url 參數            AddParam(&quot;url&quot;, Me.ResolveClientUrl(Me.Url))            MyBase.CreateChildControls()        End Sub </code></pre> <p><strong>step6.輸出 Media Player 其他參數</strong><br /> 你可以將 Media Player 的參數設定皆使用相對應的屬性來設定,然後使用 step5 的方式來輸出所有設定的參數值。</p> <p><strong>三、Media Player 控制項程式碼</strong><br /> Media Player 控制項的完整程式碼如下,此控制項只加入 URL、AutoStart、UIMode 三個參數,你可以視需求情形將使用到的參數定義為屬性來做設定即可。<br /> 因為此處有字元數限制,完整的程式碼請參閱筆者部落格相同文章<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx</a></p> <p><strong>四、執行程式</strong><br /> 把 TBMediaPlayer 控制項拖曳至頁面,設定好屬性後,執行程式就可以在頁面上看到呈現出來的 Media Player。</p> <pre><code>        &lt;bee:TBMediaPlayer ID=&quot;TBMediaPlayer1&quot; runat=&quot;server&quot; Height=&quot;250px&quot;            Width=&quot;250px&quot; Url=&quot;~/test.wmv&quot; /&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-11 19:08:27</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day9] 控制項常用 Attribute 介紹(2)</title>                <link>https://ithelp.ithome.com.tw/articles/10012060?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012060?sc=rss.iron</guid>                <description><![CDATA[<p>接續上篇 Attribute 的介紹,本文將再介紹一些伺服器控制項常用的 Attribute。<br /> <strong>六、ToolboxDataAttribute 類別</strong><...]]></description>                                    <content:encoded><![CDATA[<p>接續上篇 Attribute 的介紹,本文將再介紹一些伺服器控制項常用的 Attribute。<br /> <strong>六、ToolboxDataAttribute 類別</strong><br /> 作用:指定當自訂控制項從工具箱拖曳到頁面時,為此自訂控制項產生的預設標記。<br /> 當我們新增一個伺服器控制項,它就會預設在控制項類別套用 ToolboxDataAttribute,定義在控制項在 aspx 程式碼中的標記。</p> <pre><code>&lt;ToolboxData(&quot;&lt;{0}:TBButton runat=server &gt;&lt;/{0}:TBButton&gt;&quot;)&gt; _ Public Class TBButton    Inherits System.Web.UI.WebControls.Button End Class </code></pre> <p><strong>七、DefaultPropertyAttribute 類別</strong><br /> 作用:指定類別的預設屬性。<br /> 下面的範例中,MyTextbox 類別套用 DefaultPropertyAttribute,設定 Text 屬性為預設屬性。</p> <pre><code>&lt;DefaultProperty(&quot;Text&quot;)&gt; _ Public Class MyTextbox    Inherits WebControl    Public Property Text() As String        Get            Return CType(Me.ViewState(&quot;Text&quot;), String)        End Get        Set(ByVal value As String)            Me.ViewState(&quot;Text&quot;) = value        End Set    End Property End Class </code></pre> <p><strong>八、DefaultEventAttribute 類別</strong><br /> 作用:指定控制項的預設事件。<br /> 下面的範例中,MyTextbox 類別套用 DefaultEventAttribute,設定 TextChanged 為預設屬性。</p> <pre><code>&lt;DefaultEvent(&quot;TextChanged&quot;)&gt; _ Public Class MyTextbox    Inherits WebControl    ''' &lt;summary&gt;    ''' TextChanged 事件。    ''' &lt;/summary&gt;    Public Event TextChanged As EventHandler End Class </code></pre> <p>當設計階段在頁面上的 MyTextbox 控制項點二下時,就會產生預設事件的處理函式。</p> <pre><code>    Protected Sub MyTextbox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextbox3.TextChanged    End Sub </code></pre> <p><strong>九、LocalizableAttribute 類別</strong><br /> 作用:指定屬性是否應該當地語系化。<br /> 當屬性套用設為為 true 的 LocalizableAttribute 時,其屬性值會儲存在資源檔中,未來不需修改程式碼就可以將這些資源檔當地語系化。</p> <pre><code>        &lt;Localizable(True)&gt; _        Public Property Text() As String            Get                Return CType(Me.ViewState(&quot;Text&quot;), String)            End Get            Set(ByVal value As String)                Me.ViewState(&quot;Text&quot;) = value            End Set        End Property </code></pre> <p><strong>十、DesignerAttribute 類別</strong><br /> 作用:設定控制項在設計階段服務的類別。<br /> 指定一個設計階段的服務類別,來管理控制項在設計階段的行為,例如控制項的設計階段外觀、智慧標籤內容。例如下面範例的 TBGridView 控制項就定義了 TBGridViewDesigner 來實作設計階段的行為,未來的章節中也會介紹如何實作控制項的 Designer。</p> <pre><code>    &lt; Designer(GetType(TBGridViewDesigner)) &gt; _    Public Class TBGridView        Inherits GridView    End Class </code></pre> <p><strong>十一、EditorAttribute 類別</strong><br /> 作用:指定在屬性視窗中編輯屬性值的編輯器。<br /> 例如 ListBox 控制項的 Items 屬性,在屬性視窗編輯 Items 屬性時,會彈出 Items 集合屬性的編輯器。以下範例就是定義 Items 屬性的編輯器類別為 TBListItemsCollectionEditor,未來的章節中也會介紹如何實作屬性的 Editor。</p> <pre><code>        &lt;Editor(GetType(TBListItemsCollectionEditor), GetType(System.Drawing.Design.UITypeEditor))&gt; _        Public Overrides ReadOnly Property Items() As ListItemCollection </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/10/5653.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/10/5653.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-10 11:27:02</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day8] 控制項常用 Attribute 介紹(1)</title>                <link>https://ithelp.ithome.com.tw/articles/10012016?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012016?sc=rss.iron</guid>                <description><![CDATA[<p>Property 與 Attribute 二個術語一般都是翻譯成「屬性」,例如類別的屬性,是使用英文的 Property,而 HTML/XML 的元素屬性,使用的英文則是 Attribute。在...]]></description>                                    <content:encoded><![CDATA[<p>Property 與 Attribute 二個術語一般都是翻譯成「屬性」,例如類別的屬性,是使用英文的 Property,而 HTML/XML 的元素屬性,使用的英文則是 Attribute。在 .NET 中 Property 與 Attribute 的意義及用法不同,不過微軟線上文件也將它翻譯為「屬性」,這可能讓人發生困擾及誤解;筆者比較喜歡的方式就是 Property 是屬性,Attribute 就維持原文。在 .NET 中類別或屬性上可以套用上不同的 Attribute,使類別或屬性具有不同的特性,本文將介紹一些在伺服器控制項常使用到的 Attribute。<br /> <strong>一、DescriptionAttribute 類別</strong><br /> 作用:指定控制項或屬性的描述。<br /> 當 DescriptionAttribute 套用至控制項的類別時,設定的描述內容就會出現在工具箱中控制項的提示。</p> <pre><code>&lt;Description(&quot;按鈕控制項&quot;)&gt; _ Public Class TBButton    Inherits System.Web.UI.WebControls.Button End Class </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay7Attribute_D3/image_thumb.png" alt="" /><br /> 當 DescriptionAttribute 套用至控制項的屬性時,在屬性視窗下面就會出現設定的屬性描述內容。</p> <pre><code>&lt;Description(&quot;詢問訊息&quot;)&gt; _ Public Property ConfirmMessage() As String </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay7Attribute_D3/image_thumb_1.png" alt="" /></p> <p><strong>二、DefaultValueAttribute 類別</strong><br /> 作用:指定屬性的預設值。<br /> 使用 DefaultValueAttribute 設定屬性的預設值,若設定的屬性值與預設值相同時,此屬性值就不會出現在 aspx 程式碼中;筆者強烈建議屬性一定套用 DefaultValueAttribute,一來在 aspx 中的程式碼會比較少,另外一個重點是若因為某些因素需要修改屬性的預設值時,所有已開發頁面的控制項屬性值會一併變更;因為當初屬性值是預設值,沒有被寫入 aspx 程式碼中,所以一但控制項的屬性預設值變更,頁面已佈屬的控制項的屬性值就會全面適用。</p> <pre><code>        Private FConfirmMessage As String = String.Empty        &lt;DefaultValue(&quot;&quot;)&gt; _        Public Property ConfirmMessage() As String            Get                Return FConfirmMessage            End Get            Set(ByVal value As String)                FConfirmMessage = value            End Set        End Property </code></pre> <p><strong>三、CategoryAttribute 類別</strong><br /> 作用:指定屬性或事件的分類名稱,當屬性視窗設定為 [分類] 模式時,以群組方式來顯示屬性或事件。<br /> 例如設定 ConfirmMessage 屬性在 &quot;Behavior&quot; 分類,則 ConfirmMessage 屬性會被歸類到「行為」分類。</p> <pre><code>        &lt;Category(&quot;Behavior&quot;)&gt; _        Public Property ConfirmMessage() As String </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay7Attribute_D3/image_thumb_2.png" alt="" /></p> <p><strong>四、BindableAttribute 類別</strong><br /> 作用:指定成員是否通常使用於繫結。<br /> 在資料繫結設定視窗中中,指定屬性是否預設會出現在屬性清單中。</p> <pre><code>&lt;Bindable(True)&gt; _ Public Property ConfirmMessage() As String </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay7Attribute_D3/image_thumb_3.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/09/5647.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/09/5647.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-09 21:11:43</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day7] 設定工具箱的控制項圖示</title>                <link>https://ithelp.ithome.com.tw/articles/10011933?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011933?sc=rss.iron</guid>                <description><![CDATA[<p>當我們把自訂控制項加入到工具箱中時,你會發現所有的控制項預設都是同樣的圖示,雖然控制項的圖示不變更不會有什麼影響,不過我們還是希望為自訂控制項加上合適的外衣,本文將介紹如何設定工具箱控制項圖示。...]]></description>                                    <content:encoded><![CDATA[<p>當我們把自訂控制項加入到工具箱中時,你會發現所有的控制項預設都是同樣的圖示,雖然控制項的圖示不變更不會有什麼影響,不過我們還是希望為自訂控制項加上合適的外衣,本文將介紹如何設定工具箱控制項圖示。<br /> <strong>一、加入控制項圖示檔</strong><br /> 首先要準備一個 16 x 16 的點陣圖(bmp),如下所示。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_1.png" alt="" /><br /> 將此圖檔加入至「伺服器控制項專案」中,可以如下圖所示,用一個特定的資料夾來儲存所有工具箱的圖示。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_2.png" alt="" /><br /> 然後在圖檔的屬性視窗中,設定建置動作為「內嵌資源」。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_3.png" alt="" /><br /> <strong>二、設定控制項的圖示</strong><br /> 首先定義一個 TBResource 類別,此為一個空的類別,其命名空間需與根命名空間相同,做為引用資源檔時使用。並加上控制項圖示的 WebResource 定義,因為根命名空間是 Bee.Web,而圖檔名稱為 TBButton.bmp,所以定義如下所示。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_5.png" alt="" /><br /> 假設我們要設定 TBButton 的工具箱圖示,則在 TBButton 類別套用 ToolboxBitmapAttribute 如下,其中第一個參數為 GetType(TBResource),第二個參數為圖檔檔名。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_7.png" alt="" /><br /> 重新編輯伺服器控制項專案,再將 Bee.Web.dll 組件的控制項加入工具箱中,你就可以發現 TBButton 的圖示已經變成設定的圖示了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_8.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/08/5624.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/08/5624.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-08 22:26:13</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day6] 事件與 PostBack</title>                <link>https://ithelp.ithome.com.tw/articles/10011861?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011861?sc=rss.iron</guid>                <description><![CDATA[<p>一般類別的事件撰寫很單純,不過在 ASP.NET 中與前端使用者互動產生的事件就不是那麼簡單了;在以往的 ASP 年代是沒有事件這回事的,而在 ASP.NET 把網頁程式撰寫真正的物件導向化,用...]]></description>                                    <content:encoded><![CDATA[<p>一般類別的事件撰寫很單純,不過在 ASP.NET 中與前端使用者互動產生的事件就不是那麼簡單了;在以往的 ASP 年代是沒有事件這回事的,而在 ASP.NET 把網頁程式撰寫真正的物件導向化,用戶端使用者的操作透過 PostBack 來產生相對應的事件。例如前端使用者按鈕後會引發伺服端 Button 的 Click 事件,當前端使用者輸入文字框完畢後離開後會引發伺服端 TextBox 的 TextChanged 事件,在本文中就是要說明如何透過 PostBack 來產生與使用者互動的事件。<br /> <strong>一、IPostBackEventHandler 與 IPostBackDataHandler 介面</strong><br /> 控制項要處理 PostBack 產生的事件,必須實作 IPostBackEventHandler 或 IPostBackDataHandler 介面,這二個介面有什麼差別呢?例如 Button 是實作IPostBackEventHandler 介面,由控制項產生的 PostBack 直接引發事件,如 Button 的 Click 事件。例如 TextBox 是實作 IPostBackDataHandler 介面,當頁面產生 PostBack 時,會傳用戶端輸入的新值給控制項,由控制項本身去決定是否引發該事件;以 TextBox 舉例來說,它會判斷新值與舊值不同時才會引發 TextChanged 事件。</p> <p><strong>二、IPostBackEventHandler 介面實作</strong><br /> 首先介紹 IPostBackEventHandler 介面,它包含 RaisePostBackEvent 方法,控制項在此方法中需處理要引發那些事件。我們繼承 WebControl 撰寫 MyButton 類別來說明 IPostBackEventHandler 介面,我們簡化控制項程式碼直接在 Render 方法輸入按鈕的 HTML 原始碼,並定義一個 Click 事件。實作 IPostBackEventHandler 介面的 RaisePostBackEvent 方法,在此方法中直接引發 Click 事件。</p> <pre><code>Public Class MyButton    Inherits WebControl    Implements IPostBackEventHandler    ''' &lt;summary&gt;    ''' Click 事件。    ''' &lt;/summary&gt;    Public Event Click As EventHandler    ''' &lt;summary&gt;    ''' 引發 Click 事件。    ''' &lt;/summary&gt;    Private Sub OnClick(ByVal e As EventArgs)        RaiseEvent Click(Me, e)    End Sub    Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent        Dim e As New EventArgs()        OnClick(e) '引發 Click 事件    End Sub    ''' &lt;summary&gt;    ''' 覆寫 Render 方法。    ''' &lt;/summary&gt;    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)        Dim sHTML As String        sHTML = String.Format(&quot;&lt;INPUT TYPE=Submit Name={0} Value = '按鈕'/&gt;&quot;, Me.UniqueID)        writer.Write(sHTML)    End Sub End Class </code></pre> <p>在頁面上拖曳 MyButton 控制項,在屬性視窗找到 Click 事件,點二下產生 Click 事件處理函式,並撰寫測試程式碼如下。</p> <pre><code>    Protected Sub MyButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton1.Click        Me.Page.Response.Write(&quot;MyButton Click 事件&quot;)    End Sub </code></pre> <p>執行程式,當按下 MyButton 按鈕時,就會執行它的 RaisePostBackEvent 方法,進而引發 Click 事件,也就會執行 Click 事件處理函式的程式碼。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay5PostBack_143C0/image_2.png" alt="" /></p> <p><strong>三、IPostBackDataHandler 介面實作</strong><br /> IPostBackDataHandler 介面包含 LoadPostData 及 RaisePostDataChangedEvent 方法,當頁面 PostBack 時,會尋找具 IPostBackDataHandler 介面的控制項,先執行LoadPostData 方法,控制項在 LoadPostData 方法中會判斷用戶端傳入值決定是否引發事件,若 LoadPostData 傳回 True 表示要引發事件,此時會執行RaisePostDataChangedEvent 方法去決定要引發那些事件,反之傳回 False 表示不引發事件。</p> <p>我們繼承 WebControl 撰寫 MyText 類別來說明 IPostBackDataHandler 介面,我們簡化控制項程式碼直接在 Render 方法輸入文字框的 HTML 原始碼,並定義一個 TextChanged 事件。在 LoadPostData 方法中我們會判斷用戶端傳入值與目前的值是否不相同,不相同時才會傳回 True,此時才會執行 RaisePostDataChangedEvent 方法,進而引發 TextChanged 事件。</p> <pre><code>Public Class MyTextbox    Inherits WebControl    Implements IPostBackDataHandler    Public Property Text() As String        Get            Return CType(Me.ViewState(&quot;Text&quot;), String)        End Get        Set(ByVal value As String)            Me.ViewState(&quot;Text&quot;) = value        End Set    End Property    ''' &lt;summary&gt;    ''' TextChanged 事件。    ''' &lt;/summary&gt;    Public Event TextChanged As EventHandler    ''' &lt;summary&gt;    ''' 引發 TextChanged 事件。    ''' &lt;/summary&gt;    Private Sub OnTextChanged(ByVal e As EventArgs)        RaiseEvent TextChanged(Me, e)    End Sub    Public Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As System.Collections.Specialized.NameValueCollection) As Boolean Implements System.Web.UI.IPostBackDataHandler.LoadPostData        '前端使用者輸入值        Dim oNewValue As String = postCollection.Item(postDataKey)        If oNewValue &lt;&gt; Me.Text Then            Me.Text = oNewValue            Return True        Else            Return False        End If    End Function    Public Sub RaisePostDataChangedEvent() Implements System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent        Dim e As New EventArgs()        '引發 TextChanged 事件        OnTextChanged(e)    End Sub    ''' &lt;summary&gt;    ''' 覆寫 Render 方法。    ''' &lt;/summary&gt;    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)        Dim sHTML As String        sHTML = String.Format(&quot;&lt;INPUT Type=text Name={0} Value={1} &gt;&quot;, Me.UniqueID, Me.Text)        writer.Write(sHTML)    End Sub End Class </code></pre> <p>在頁面上拖曳 MyTextbox 及 MyButton 控制項,在 MyButton 的 Click 及 MyTextbox 的 TextChanged 事件撰寫如下測試程式碼。</p> <pre><code>    Protected Sub MyButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton1.Click        Me.Page.Response.Write(&quot;MyButton Click 事件&quot;)        Me.Page.Response.Write(&quot;&lt;br/&gt;&quot;)    End Sub    Protected Sub MyTextbox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextbox1.TextChanged        Me.Page.Response.Write(&quot;MyTextbox TextChanged 事件&quot;)        Me.Page.Response.Write(&quot;&lt;br/&gt;&quot;)    End Sub </code></pre> <p>執行程式,在 MyTextbox 輸入 &quot;A&quot;,再按下 MyButton,因為 MyTextbox 的值「空字串-&gt;&quot;A&quot;」,所以會引發 MyTextbox 的 TextChanged 事件及 MyButton 的 Click 事件。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay5PostBack_143C0/image_6.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格</p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-07 23:30:19</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day5] 屬性與 ViewState</title>                <link>https://ithelp.ithome.com.tw/articles/10011745?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011745?sc=rss.iron</guid>                <description><![CDATA[<p>在 ASP.NET 中,控制項的屬性與 ViewState 有著密不可分的關係,透過 ViewState 才有辨法維護控制項的屬性值。在本文中將介紹屬性與 ViewState 的關係,並說明屬性...]]></description>                                    <content:encoded><![CDATA[<p>在 ASP.NET 中,控制項的屬性與 ViewState 有著密不可分的關係,透過 ViewState 才有辨法維護控制項的屬性值。在本文中將介紹屬性與 ViewState 的關係,並說明屬性如何存取 ViewState 是比較有效率的方式。<br /> 當你加入一個「ASP.NET 伺服器控制項」時,類別中預設會有一個 Text 屬性寫法的範例如下所示,屬性的讀寫都是直接存取 ViewState,這是一般常見的控制項屬性寫法。可是這種屬性的寫法是沒有效率的,因為 ViewState 的成員是 Object 型別,每次讀取屬性時都是由 ViewState 取出指定鍵值的成員再轉型為屬性的型別,寫入屬性的動作也是直接寫入 ViewState 中。</p> <pre><code>    Property Text() As String        Get            Dim s As String = CStr(ViewState(&quot;Text&quot;))            If s Is Nothing Then                Return String.Empty            Else                Return s            End If        End Get        Set(ByVal Value As String)            ViewState(&quot;Text&quot;) = Value        End Set    End Property </code></pre> <p>比較好的方式應該是讀取 ViewState 成員只做一次型別轉換的動作,而寫入 ViewState 的動作可以在 Render 前做批次寫入的動作即可。為了達到這樣的需求,我們可以覆寫 LoadViewState 及 SaveViewState 方法來處理屬性與 ViewState 的存取動作;當控制項初始化後會執行 LoadViewState 方法,來載入 ViewState 還原的控制項狀態,當控制項 Render 之前,會執行 SaveViewState 方法,將控制項的最終狀態存入 ViewState 中,也就是在此方法之後對控制項所做的任何變更都將會被忽略。</p> <p>我們改寫屬性的寫法,不直接用 ViewState 來存取屬性,而是改用「屬性區域變數」來存取屬性,在 LoadViewState 時載入 ViewState 到屬性區域變數,而 SaveViewState 時再將屬性區域變數寫入 ViewState 中。我們依此方式將 Text 屬性改寫如下。</p> <pre><code>    Private FText As String    Property Text() As String        Get            Return FText        End Get        Set(ByVal Value As String)            FText = Value        End Set    End Property    ''' &lt;summary&gt;    ''' 載入 ViewState 還原控制項狀態。    ''' &lt;/summary&gt;    Protected Overrides Sub LoadViewState(ByVal savedState As Object)        If Not (savedState Is Nothing) Then            ' Load State from the array of objects that was saved at vedViewState.            Dim myState As Object() = CType(savedState, Object())            If Not (myState(0) Is Nothing) Then                MyBase.LoadViewState(myState(0))            End If            If Not (myState(1) Is Nothing) Then                FText = CType(myState(1), String)            End If        End If    End Sub    ''' &lt;summary&gt;    ''' 將控制項狀態寫入 ViewState 中。    ''' &lt;/summary&gt;    Protected Overrides Function SaveViewState() As Object        Dim baseState As Object = MyBase.SaveViewState()        Dim myState(1) As Object        myState(0) = baseState        myState(1) = FText        Return myState    End Function </code></pre> <p>利用上述的方式,我們可以在 LoadViewState 批次載入所有屬性值,而在 SaveViewState 批次寫入屬性值,如此在讀取屬性就不用一直做型別轉換的動作以改善效率。</p> <p><strong>結語</strong><br /> 雖然屬性一般都是儲存於 ViewState 中,不過若是一些無關緊要的屬性或是完全不會執行階段就變更的屬性,可以考慮不需要將這些屬性儲存於 ViewState 中;因為 ViewState 是個兩面刃,ViewState 可以很輕易幫我們維護屬性值,不過相對的也增加了面頁的傳輸量,所以可以視實際情形來決定屬性是否要儲存於 ViewState 中。</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/07/5601.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/07/5601.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-06 21:17:20</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day4] 複合控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10011633?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011633?sc=rss.iron</guid>                <description><![CDATA[<p>複合控制項就是控制項可包含其他子控制項,複合控制項繼承至 System.Web.UI.WebControls.CompositeControl,例如 Login 及 Wizard 等控制項就是屬...]]></description>                                    <content:encoded><![CDATA[<p>複合控制項就是控制項可包含其他子控制項,複合控制項繼承至 System.Web.UI.WebControls.CompositeControl,例如 Login 及 Wizard 等控制項就是屬於複合控制項。我們常在網頁上常看到一種輸入日期的方式是年月日三個下拉清單,本文將利用複合控制項來實作這個年月日下拉清單控制項,示範如何實作複合控制項。<br /> <strong>一、CompositeControl 類別的特性</strong><br /> CompositeControl 類別是抽象類別,它會實作 INamingContaner 介面,INamingContaner 介面會在子控制項的 ClinetID 加上父控制項的 ID,以確保頁面上控制項的 ClientID 是唯一的。繼承 CompositeControl 類別一般都是覆寫 CreateChildControls 方法,在此方法中將建立子控制項並加入 Controls 集合屬性中;當存取其子控制項時,若子控制項未建立,會執行 CreateChildControls 方法,以會確保所有子控制項皆已在存取 ControlCollection 之前建立。<br /> <strong>二、日期下拉清單輸入器</strong><br /> 我們繼承 CompositeControl 類別,命名為 TBDropDownDate。這個控制項會包含年月日三個下拉清單(DropDownList),所以我們只要在 CreateChildControls 方法中依序建立年月日的 DropDownList 子控制項,並加入 Controls 集合屬性中即可。</p> <pre><code>''' &lt;summary&gt; ''' 日期下拉清單輸入器。 ''' &lt;/summary&gt; &lt; _ ToolboxData(&quot;&lt;{0}:TBDropDownDate runat=server&gt;&lt;/{0}:TBDropDownDate&gt;&quot;) _ &gt; _ Public Class TBDropDownDate    Inherits System.Web.UI.WebControls.CompositeControl    Protected Overrides Sub CreateChildControls()        Dim oYear As DropDownList        Dim oMonth As DropDownList        Dim oDay As DropDownList        Dim N1 As Integer        '年下拉清單區間為 1950-2010 (年區間可以用屬性來設定)        oYear = New DropDownList        oYear.ID = &quot;Year&quot;        For N1 = 1950 To 2010            oYear.Items.Add(N1.ToString)        Next        Me.Controls.Add(oYear) '加入子控制項        Me.Controls.Add(New LiteralControl(&quot;年&quot;))        '月下拉清單區間為 1-12        oMonth = New DropDownList        oMonth.ID = &quot;Month&quot;        For N1 = 1 To 12            oMonth.Items.Add(N1.ToString)        Next        Me.Controls.Add(oMonth) '加入子控制項        Me.Controls.Add(New LiteralControl(&quot;月&quot;))        '日下拉清單區為為 1-31        oDay = New DropDownList        oDay.ID = &quot;Day&quot;        For N1 = 1 To 12            oDay.Items.Add(N1.ToString)        Next        Me.Controls.Add(oDay) '加入子控制項        Me.Controls.Add(New LiteralControl(&quot;日&quot;))    End Sub End Class </code></pre> <p>在設定階段拖曳 TBDropDownDate 到頁面上,就可以看到我們在 CreateChildControls 方法中所加入的子控制項。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay4_9C76/image_2.png" alt="" /></p> <p>執行程式,檢視它的 HTML 原始碼,會發現年月日的子控制項的 ClientID 都會在原 ID 前加上父控制項的 ID,這樣命名規則可以確保所有的控制項的 ClinetID 都是唯一值。</p> <pre><code>&lt;span id=&quot;TBDropDownDate1&quot;&gt; &lt;select name=&quot;TBDropDownDate1$Year&quot; id=&quot;TBDropDownDate1_Year&quot;&gt; ....省略 &lt;select name=&quot;TBDropDownDate1$Month&quot; id=&quot;TBDropDownDate1_Month&quot;&gt; ....省略 &lt;select name=&quot;TBDropDownDate1$Day&quot; id=&quot;TBDropDownDate1_Day&quot;&gt; &lt;/span&gt; </code></pre> <p><strong>三、結語</strong><br /> 我們已經看過三類伺服器控制項的簡單案例,不過這三個案例都只是簡單說明控制項 UI 的部分,一個完整的控制項需具備屬性、方法、事件、設計階段支援...等,在後面的文章中,我們將陸續針對這些部分做詳細的介紹。</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/05/5583.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/05/5583.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-05 22:22:25</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day3] 擴展現有伺服器控制項功能</title>                <link>https://ithelp.ithome.com.tw/articles/10011562?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011562?sc=rss.iron</guid>                <description><![CDATA[<p>相對於由無到有開發控制項,繼承現有現伺服器控制項是比較簡單且實用的方式;若希望在現有的控制項增加某些屬性或功能,直接繼承該控制項下來擴展功能是最快的方式,例如「按下 Button 會彈出詢問訊息...]]></description>                                    <content:encoded><![CDATA[<p>相對於由無到有開發控制項,繼承現有現伺服器控制項是比較簡單且實用的方式;若希望在現有的控制項增加某些屬性或功能,直接繼承該控制項下來擴展功能是最快的方式,例如「按下 Button 會彈出詢問訊息」、「TextBox 設為 ReadOnly 時,可以取得前端傳回的 Text 屬性」這類需求,都可以直接繼承原控制項下來,加上我們需要的功能即可。以下我們就以一個簡單的案例來說明如何繼承現有伺服器下來擴展功能。<br /> <strong>一、擴展 Button 控制項:按鈕加上詢問訊息</strong><br /> 按下按鈕執行某些動作前,有時會詢問使用者是否執行該動作;例如按下刪除鈕,會詢問使用者是否確定要執行刪除的動作。當然這只需要簡單的 JavaScript 就可以完成,不過相對於 .NET 的程式語言,JavaScript 是非常不易維護的用戶端指令碼,如果能讓開發人員完全用不到 JavaScript,那何樂不為呢? 那就由 Button 控制項本身提供加上詢問訊息的功能就可以,相關的 JavaScript 由控制項去處理。<br /> 一般要在 Button 加上詢問訊息,只要在 OnClientClick 屬性設定如下的 JavaScript 即可。我們的目的只是讓開發人員連設定 OnClientClick 屬性的 JavaScript 都省略,直接設定要詢問的訊息即可,接下來我們就要開始實作這個控制項。</p> <pre><code>&lt;asp:Button ID=&quot;Button1&quot; runat=&quot;server&quot; Text=&quot;Button&quot;  OnClientClick=&quot;if (confirm('確定執行嗎?')==false) {return false;}&quot; /&gt;   </code></pre> <p>在 Bee.Web 專案中,加入「ASP.NET 伺服器控制項」,此控制項繼承 Button 下來命名為 TBButton (命名空間為 Bee.Web.WebControls)。在 TBButton 類別中加入 ConfirmMessage 屬性,用來設定詢問訊息的內容。然後在 Render 方法將詢問詢息的 JavaScript 設定到 OnClientClick 屬性即可。</p> <pre><code>Namespace WebControls    &lt; _    Description(&quot;按鈕控制項&quot;), _    ToolboxData(&quot;&lt;{0}:TBButton runat=server&gt;&lt;/{0}:TBButton&gt;&quot;) _    &gt; _    Public Class TBButton        Inherits System.Web.UI.WebControls.Button        &lt;Description(&quot;詢問訊息&quot;)&gt; _        Public Property ConfirmMessage() As String            Get                Dim sConfirmMessage As String                sConfirmMessage = CStr(ViewState(&quot;ConfirmMessage&quot;))                If sConfirmMessage Is Nothing Then                    Return String.Empty                Else                    Return sConfirmMessage                End If            End Get            Set(ByVal value As String)                ViewState(&quot;ConfirmMessage&quot;) = value            End Set        End Property        ''' &lt;summary&gt;        ''' 覆寫 Render 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)            Dim sScript As String            Dim sConfirm As String            '若有設定 ConfirmMessage 屬性,則在 OnClientClick 加入詢問訊息的 JavaScript            If Me.ConfirmMessage &lt;&gt; String.Empty Then                sScript = Me.OnClientClick                '詢問訊息的 JavaScript                sConfirm = String.Format(&quot;if (confirm('{0}')==false) {{return false;}}&quot;, Me.ConfirmMessage)                If sScript = String.Empty Then                    Me.OnClientClick = sConfirm                Else                    Me.OnClientClick = sConfirm &amp; sScript                End If            End If            MyBase.Render(writer)        End Sub    End Class End Namespace </code></pre> <p>將 TBButton 拖曳到測試頁面,設定 ConfirmMessage 屬性。</p> <pre><code>&lt;bee:TBButton ID=&quot;TBButton1&quot; runat=&quot;server&quot; ConfirmMessage=&quot;確定刪除此筆資料嗎?&quot; Text=&quot;刪除&quot; /&gt; </code></pre> <p>執行結果如下。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/8cea6e0221af_B473/image_2.png" alt="" /></p> <p><strong>二、結語</strong><br /> 筆者在開發 ASP.NET 的應用程式過程中,通常會習慣把所有現有控制項繼承下來,無論目前需不需要擴展控制項功能。這種方式對於開發大型系統是相當有幫助的,因為無法預期在系統開發的過程中會不會因為某些狀況,而臨時需要擴展控制項的功能,所以就先全部繼承下來以備不時之需,也為未來保留修改的彈性。</p> <p><strong>三、相關連結</strong><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1748.aspx" target="_blank">擴展 CommandField 類別 - 刪除提示訊息</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1697.aspx" target="_blank">按鈕加上詢問訊息</a></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/04/5578.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/04/5578.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-04 21:24:49</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day2] 建立第一個伺服器控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10011523?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011523?sc=rss.iron</guid>                <description><![CDATA[<p>上一篇中已經建立「ASP.NET 伺服器控制項」專案,接下來我們將學習來撰寫第一個伺服器控制項。<br /> 撰寫伺服器控制項大致分為下列三種方式<br /> 1.由無到有建立全新的控制項,一般...]]></description>                                    <content:encoded><![CDATA[<p>上一篇中已經建立「ASP.NET 伺服器控制項」專案,接下來我們將學習來撰寫第一個伺服器控制項。<br /> 撰寫伺服器控制項大致分為下列三種方式<br /> 1.由無到有建立全新的控制項,一般會繼承至 System.Web.UI.Control 或 System.Web.UI.WebControls.WebControl 類別。<br /> 2.繼承現有控制項,擴展原有控制項的功能,如繼承原有 TextBox 來擴展功能。<br /> 3.複合式控制項,將多個現有的控制項組合成為一個新的控制項,例如 TextBox 右邊加個 Button 整合成一個控制項,一般會繼承至 System.Web.UI.WebControls.CompositeControl 類別。</p> <p>本文將先介紹第1種方式,由無到有來建立控制項,後面的文章中會陸續介紹第2、3種方式的控制項。要建立全新的控制項會繼承至 Control 或 WebControl,沒有 UI 的控制項可由 Control 繼承下來 (如 SqlDataSource),具 UI 的控制項會由 WebControl 繼承下來。接下來的範例中,我們將繼承 WebControl 來建立第一個 MyTextBox 控制項。</p> <p><strong>一、新增 MyTextBox 控制項</strong><br /> 在 Bee.Web 專案按右鍵選單,執行「加入\新增項目」,選擇「ASP.NET 伺服器控制項」,在名稱文字框中輸入 MyTextbox,按下「確定」鈕,就會在專案中加入 MyTextbox 控制項類別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/0e8fe1ad2977_14385/image_thumb.png" alt="" /><br /> 新加入的控制項預設有一個 Text 屬性,以及覆寫 Render 方法。Render 方法是「將控制項呈現在指定的 HTML 寫入器中」,簡單的說就是在 Render 方法會將控制項對應的 HTML 碼輸出,用來呈現在用戶端的瀏覽器上。假設我們要撰寫一個網頁上的文字框,那就先去看一下文字框在網頁中對應的 HTML 碼,然後在 Render 方法中想辨法輸出這些 HTML 碼即可。</p> <p><strong>二、輸出控制項的 HTML 碼</strong><br /> 你可以使用 FrontPage 之類的 HTML 編輯器,先編輯出控制項的呈現方式,進而去觀查它的 HTML 碼,再回頭去思考如何去撰寫這個伺服器控制項。假設 MyTextbox 控制項包含一個文字框及一個按鈕,那最終輸出的 HTML 碼應該如下。</p> <pre><code>&lt;input id=&quot;Text1&quot; type=&quot;text&quot; /&gt; &lt;input id=&quot;Button1&quot; type=&quot;button&quot; value=&quot;button&quot; /&gt; </code></pre> <p>我們在 MyTextbox 的 RenderContents 方法中輸出上述的 HTML 碼。</p> <pre><code>    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)        Dim sHTML As String        sHTML = &quot;&lt;input id=&quot;&quot;Text1&quot;&quot; type=&quot;&quot;text&quot;&quot; /&gt;&quot; &amp; _                &quot;&lt;input id=&quot;&quot;Button1&quot;&quot; type=&quot;&quot;button&quot;&quot; value=&quot;&quot;button&quot;&quot; /&gt;&quot;        writer.Write(sHTML)    End Sub </code></pre> <p>建置控制項專案,然後拖曳 MyTextbox 在測試頁面上,設計階段就會呈現出我們期望的結果。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/0e8fe1ad2977_14385/image_4.png" alt="" /></p> <p>執行程式,在瀏覽器看一下 MyTextbox 控制項輸出的結果,是不是跟我們預期的一樣呢。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/0e8fe1ad2977_14385/image_6.png" alt="" /></p> <p><strong>三、屬性套用到控制項 HTML 碼</strong><br /> 控制項不可能單純這樣輸出 HTML 碼而已,控制項的相關屬性設定,一般都影響到輸出的 HTML 碼。假設 MyTextbox 有 Text 及 ButtonText 二個屬性,分別對應到 文字框的內容及按鈕的文字,MyTextbox 本來就有 Text 屬性,依像畫蘆葫新增 ButtonText 屬性。</p> <pre><code>    &lt; _    Bindable(True), _    Category(&quot;Appearance&quot;), _    DefaultValue(&quot;&quot;), _    Localizable(True)&gt; _    Property ButtonText() As String        Get            Dim s As String = CStr(ViewState(&quot;ButtonText&quot;))            If s Is Nothing Then                Return String.Empty            Else                Return s            End If        End Get        Set(ByVal Value As String)            ViewState(&quot;ButtonText&quot;) = Value        End Set    End Property </code></pre> <p>RenderContents 方法改寫如下。</p> <pre><code>    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)        Dim sHTML As String        sHTML = &quot;&lt;input id=&quot;&quot;Text1&quot;&quot; type=&quot;&quot;text&quot;&quot; value=&quot;&quot;{0}&quot;&quot;/&gt;&quot; &amp; _                &quot;&lt;input id=&quot;&quot;Button1&quot;&quot; type=&quot;&quot;button&quot;&quot; value=&quot;&quot;{1}&quot;&quot; /&gt;&quot;        sHTML = String.Format(sHTML, Me.Text, Me.ButtonText)        writer.Write(sHTML)    End Sub </code></pre> <p>重新建置控制項專案,在頁面上測試 MyTextbox 的 Text 及 ButtonText 屬性。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/0e8fe1ad2977_14385/image_8.png" alt="" /></p> <p><strong>四、使 ClientID (HTML 原始碼控制項的 ID) 是唯一值</strong><br /> 在頁面上放置二個 MyTextbox 控制項,執行程式,在瀏覽器中檢查 MyTextbox 的 HTML 原始碼。你會發現 MyTextbox 會以一個 span 包住控制項的內容,而每個控制項的輸出的 ClientID 是唯一的。不過 MyTextbox 內含的文字框及按鈕卻會重覆,所以一般子控制項的 ClientID 會在前面包含父控制項的 ID。</p> <pre><code>&lt;span id=&quot;MyTextbox1&quot;&gt; &lt;input id=&quot;Text1&quot; type=&quot;text&quot; value=&quot;這是文字&quot;/&gt; &lt;input id=&quot;Button1&quot; type=&quot;button&quot; value=&quot;這是按鈕&quot; /&gt; &lt;/span&gt; &lt;br /&gt; &lt;span id=&quot;MyTextbox2&quot;&gt; &lt;input id=&quot;Text1&quot; type=&quot;text&quot; value=&quot;這是文字&quot;/&gt; &lt;input id=&quot;Button1&quot; type=&quot;button&quot; value=&quot;這是按鈕&quot; /&gt; &lt;/span&gt; </code></pre> <p>所以我們再次修改 RenderContents 方法的程式碼</p> <pre><code>    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)        Dim sHTML As String        sHTML = &quot;&lt;input id=&quot;&quot;{0}_Text&quot;&quot; type=&quot;&quot;text&quot;&quot; value=&quot;&quot;{1}&quot;&quot;/&gt;&quot; &amp; _                &quot;&lt;input id=&quot;&quot;{0}_Button&quot;&quot; type=&quot;&quot;button&quot;&quot; value=&quot;&quot;{2}&quot;&quot; /&gt;&quot;        sHTML = String.Format(sHTML, Me.ID, Me.Text, Me.ButtonText)        writer.Write(sHTML)    End Sub </code></pre> <p>執行程式,再次檢視 HTML 原始碼,所有的 ClinetID 都會是唯一的。</p> <pre><code>&lt;span id=&quot;MyTextbox1&quot;&gt; &lt;input id=&quot;MyTextbox1_Text&quot; type=&quot;text&quot; value=&quot;這是文字&quot;/&gt; &lt;input id=&quot;MyTextbox1_Button&quot; type=&quot;button&quot; value=&quot;這是按鈕&quot; /&gt; &lt;/span&gt; &lt;br /&gt; &lt;span id=&quot;MyTextbox2&quot;&gt; &lt;input id=&quot;MyTextbox2_Text&quot; type=&quot;text&quot; value=&quot;這是文字&quot;/&gt; &lt;input id=&quot;MyTextbox2_Button&quot; type=&quot;button&quot; value=&quot;這是按鈕&quot; /&gt; &lt;/span&gt; </code></pre> <p><strong>五、控制項前置詞</strong><br /> 自訂控制項的預設前置詞是 cc1,不過這是可以修改的,在專案中的 AssemblyInfo.vb 檔案中,加入如下定義即可。詳細的作法請參考筆者部落格中的「<a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1744.aspx" target="_blank">自訂伺服器控制項前置詞</a>」一本有詳細介紹,在此不再累述。</p> <pre><code>'設定控制項的標記前置詞 &lt;Assembly: TagPrefix(&quot;Bee.Web.WebControls&quot;, &quot;bee&quot;)&gt; </code></pre> <p><strong>六、結語</strong><br /> 本文中是用土法鍊鋼的方法在撰寫伺服器控制項,一般在實作控制項時會有更好的方式、更易維護的寫法,後續的文章中會陸續介紹相關作法。</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/03/5573.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/03/5573.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-03 23:51:35</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day1] 建立 ASP.NET 伺服器控制項專案</title>                <link>https://ithelp.ithome.com.tw/articles/10011408?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011408?sc=rss.iron</guid>                <description><![CDATA[<p>在 ASP.NET 開發環境中,我們常使用現成的控制項直接拖曳至頁面中使用,有沒有想過我們也可以開發自用的控制項呢?本文將本文以 VS2008 為開發工具,VB.NET 為開發程式語言,來說明如...]]></description>                                    <content:encoded><![CDATA[<p>在 ASP.NET 開發環境中,我們常使用現成的控制項直接拖曳至頁面中使用,有沒有想過我們也可以開發自用的控制項呢?本文將本文以 VS2008 為開發工具,VB.NET 為開發程式語言,來說明如何建立「伺服器控制項」專案,以及如何測試開發階段的的伺服器控制項。<br /> <strong>一、建立「ASP.NET 伺服器控制項」專案</strong><br /> 首先執行功能表「檔案\新增專案」,在專案類型中選擇 Visual Basic -&gt; Web,選取「ASP.NET 伺服器控制項」範本,在名稱文字框中輸入專案名稱,也就是組件的檔案名稱,我們輸入 Bee.Web 為專案名稱,組件檔案為 Bee.Web.dll,按下「確定」鈕即會建立新的「ASP.NET 伺服器控制項」專案。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_1.png" alt="" /><br /> 在新建立「ASP.NET 伺服器控制項」專案中,會預設加入一個伺服器控制項類別(ServerControl1.vb),這個伺服器控制項已經事件幫我們加入一些控制項的程式碼。目前暫不做任何修改,直接使用此控制項來做測試說明。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_2.png" alt="" /><br /> 接下來執行功能表「專案\Bee.Web 屬性」,設定此組件的根命名空間,一般慣用的根命名空間都會與組件名稱相同,以方便加入參考時可以快速找到相關組件。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_10.png" alt="" /><br /> 我們先儲存這個「ASP.NET 伺服器控制項」專案,指定儲存位置,按下「儲存」鈕。整個專案相關檔案,會儲存在以專案名稱的資料夾中。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_3.png" alt="" /></p> <p><strong>二、加入測試網站</strong><br /> 不要關閉目前「ASP.NET 伺服器控制項」專案,執行功能表「檔案\加入\新網站」,選擇「ASP.NET 網站」,會在方案中加入一個網站,來測試開發階段的伺服器控制項使用。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_5.png" alt="" /><br /> 在測試網站加入參考,選擇「專案」頁籤,此頁籤中會列出該方案中其他可加入參考的專案,選取 Bee.Web 專案,按下「確定」鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_6.png" alt="" /><br /> 先在 Bee.Web 專案中執行「建置」動作,然後切換到測試網站的頁面設計,工具箱中就會出現 ServerControl1 伺服器控制項。這個控制項就可以直接拖曳至頁面中使用,這個控制項只是單純 Render 出 Text 屬性值,你可以在控制項屬性視窗中,更改 Text 屬性值為 &quot;測試文字&quot;,就會看到這個控制項顯示 &quot;測試文字&quot;。將測試網站設為啟動專案,按下「F5」執行程式,就會看到該控制在執行階段的結果。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_12.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/02/5562.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/02/5562.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-02 23:16:29</pubDate>                                                                                                                                            </item>            </channel> </rss>