TypeScript

  • 1744
  • 0

TypeScript

日常任務成長紀錄 :: 2021 iThome 鐵人賽 https://ithelp.ithome.com.tw/users/20129286/ironman https://ithelp.ithome.com.tw/images/ironman/13th/fb.jpg 日常任務成長紀錄 :: 2021 iThome 鐵人賽 https://ithelp.ithome.com.tw/users/20129286/ironman zh-TW Mon, 04 Jul 2022 07:31:35 +0800 快取機制 心得紀錄 https://ithelp.ithome.com.tw/articles/10281831?sc=rss.iron https://ithelp.ithome.com.tw/articles/10281831?sc=rss.iron 這幾天聽到一個新名詞,eTag,我想說這究竟是什麼東西,要上高速公路了嗎?、還是什麼特別的tag呢? 原來是快取機制使用的,快取簡單來說,就是把不易變動的資料,存在client端,對clent來...]]> 這幾天聽到一個新名詞,eTag,我想說這究竟是什麼東西,要上高速公路了嗎?、還是什麼特別的tag呢? 原來是快取機制使用的,快取簡單來說,就是把不易變動的資料,存在client端,對clent來說,如果需要,可以直接拿,快速省時間,對於server來說,可以省下不必要的傳輸成本,那快取機制延伸到快取策略,都是根據不同需求與情境演變,以下找到一篇好文,去蕪存菁,作者簡單的舉例說明,讓人快速了解快取機制的重要關鍵字與情境,以下紀錄心得

https://blog.techbridge.cc/2017/06/17/cache-introduction/

  1. 為什麼要快取?

    1. 節省傳輸成本,因為每一次傳輸都是一筆錢,尤其各種商品圖片
    2. 提升用戶體驗,因為可以快速取得自己有興趣的商品照片
  2. 有哪些快取關鍵字,各自代表什麼意義?

    • Expires: 到期時間點,可能被瀏覽器使用電腦本身時間,判斷過期
    • Cache-Control
      • max-age 資源有效時間(秒)
      • no-store 快取策略,不存任何快取
      • no-cache 快取策略,存快取,但馬上過期,需要持續確認是否有異動
    • Last-Modified & If-Modified-Since 資源上一次變動時間
      • Last-Modified 放在 res Header跟著資源回到 browser
      • If-Modified-Since 下一次請求,放在 req Header 提供給Server做檢查
    • Etag & If-None-Match 資源Hash值(類似但不是)
      • ETag 放在 res Header跟著資源回到 browser
      • If-None-Match 下一次請求,放在 req Header 提供給Server做檢查是否相符
    • 小結
      • Expires 、 Cache-Control: max-age
        • Server 跟 Browser 説這個資源的新鮮度(有效日期、期間)
      • Last-Modified & If-Modified-Since , Etag & If-None-Match
        • 在過期後,Browser 用來跟 Server 確定資源是否有被更新用
      • Cache-Control: no-store 、Cache-Control: no-cache
        • 快取策略,一件設定的概念
  3. 在什麼情況,要用哪些快取機制?

    1. 資料不常變動,不及時更新也不會有影響 → 可以設定固定時間、時間點過期
      1. Expires 、 Cache-Control: max-age
    2. 資料過期,如果server沒有異動,也可以繼續用,但是要跟 Server 確認
      1. Last-Modified & If-Modified-Since , Etag & If-None-Match
    3. 資料異動時間不一定,但是一但有變動,就要更新給 user
      1. Cache-Control: max-age: 0 + Etag ,Browser 一拿到資料會存起來,但馬上就過期,所以要一直確認是不是新的
      2. Cache-Control: no-cache 是第一點的一鍵設定
    4. 組合技,讓問都不用問,近一步省下傳輸量
      1. 雙快取策略,詳情請見文章
  4. 還有什麼值得紀錄

    1. 價值面溝通
      1. 跨部門溝通,需要有效抓住對方重視價值
      2. 練習價值轉換,將我方重視價值,轉換成對方重視價值 (ex. (我方) 使用快取可以減少不必要傳輸量 → (對方) 使用快取,每月可以省下200元)
      3. 每個人都有自己所重視的事情,沒有人有義務要去理解你,所以要練習 轉換價值、進行溝通
]]>
azsx96385 2021-10-15 16:23:25
[心得]資料異動行為,需要有意識的風險管理 https://ithelp.ithome.com.tw/articles/10281566?sc=rss.iron https://ithelp.ithome.com.tw/articles/10281566?sc=rss.iron 紀錄一下最近遇到的問題

一支異動訂單的API ,稱作 Patch Order,在一秒內被打了兩次,造成資料產生非預期的變化,例如 訂單金額 10,假設這是一個給折扣的動作(-5),...]]> 紀錄一下最近遇到的問題

一支異動訂單的API ,稱作 Patch Order,在一秒內被打了兩次,造成資料產生非預期的變化,例如 訂單金額 10,假設這是一個給折扣的動作(-5),原本預期 10 -5 ,但最後是 10 - 5 - 5 =0 ,天上掉下的禮物~

針對這件事,我得到以下心得

  1. 任何資料異動API 應當把並發、短時間多req 的情境考慮進去,並思考後續影響
  2. 若影響幾乎沒有,那麼可以不用作太多措施
  3. 若影響會造成困擾,則應該採取手段(ex. redisLock , 樂觀鎖 ..)
  4. 若使用 typeOrm 因當謹慎使用 save() ,盡可能搭配 find() 使用,作為資料驗證使用
]]> azsx96385 2021-10-14 19:18:08
TypeScript | namespace 心得紀錄 https://ithelp.ithome.com.tw/articles/10281205?sc=rss.iron https://ithelp.ithome.com.tw/articles/10281205?sc=rss.iron 這邊一樣是在研究 D14 介面的函式超載運用,又提到 namespace 的知識點,所以回頭來補相關知識,簡言之應用情境,當我們因需求,需要第三方套件以外的功能時,會有需要針對第三方套件進行一些前處理、後處理,這時候可以應用到 namespace 的 命名空間融合 以下為心得紀錄

Day 35. 戰線擴張・命名空間 X 組織分明 - TypeScript Namespaces Introduction - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

  1. 什麼是 namespace , 有什麼用處?以下引述作者的解釋,我覺得非常貼切易懂
    1. 建立命名空間,組織並包裝程式碼,達成隔離變數命名,不互相污染變數的情況
  2. 重點紀錄
    1. 命名空間宣告,使用關鍵字 “namespace”
    2. 若需要使用 命名空間之 屬性、方法,必須先要 export 方可使用
    3. 支援巢狀命名空間,但記得要“export”,方可使用
    4. 可 export 變數、函式、型別(Types)、介面、類別、命名空間,唯獨 值不可export
    5. 命名空間融合,相同命名空間,可以有聯集的結果,不同區塊同樣命名空間,可使用各自輸出之功能,但不可以覆蓋任一命名空間宣告之功能
    6. 命名空間融合 vs 介面融合
      1. 介面宣告的是規格,而命名空間是一系列功能包裝,為了防止全域污染
      2. 兩者皆可動態擴充(融合),但擴充的意義不同,介面聚焦在規格,但命名空間則聚焦程式包裝,範圍更廣
      3. 函式超載僅有介面支援,但是命名空間則沒有,因為本質意義不同
]]> azsx96385 2021-10-13 17:20:26
TypeScript | nerver 型別 心得紀錄 https://ithelp.ithome.com.tw/articles/10280902?sc=rss.iron https://ithelp.ithome.com.tw/articles/10280902?sc=rss.iron 今天要了解 interface 進一步的觀念,剛好撞到 nerver 觀念還不是那麼懂,所以回來補個never 觀念,我覺得要了解nerver,可以根據情境去理解會比較容易,雖然看了多遍還是有點...]]> 今天要了解 interface 進一步的觀念,剛好撞到 nerver 觀念還不是那麼懂,所以回來補個never 觀念,我覺得要了解nerver,可以根據情境去理解會比較容易,雖然看了多遍還是有點模糊,但就之後多看例子吧,以下紀錄心得,細節案例請見文章,非常詳細

Day 10. 前線維護・特殊型別 X 永無止盡 - Never Type - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

nerver 型別

  1. 使用情境目前看來有三,變數主動註記、函式return 主動註記、自動推論
  2. never 精神,永遠不會結束、永遠不會有預期結果
  3. 主動註記 vs. 被動推論,代表意義各不相同
  4. 人中有我,我中只有我概念,所有型別都有never情況、但是nerver型別,只有never
  5. 重點紀錄
    1. never型別意義,無法跳脫函式執行(無窮迴圈)、出現例外中斷執行(永不會有預期結果)
    2. never 型別為所有型別 subtype ,任何型別都有可能出錯,導致無法成為指定型別,因此都會有 never case ( number | never 等於 number )
    3. never 型別變數指派,註記任意型別,可以被指派對應型別外、也可被指派 never ,但若被註記 never 則不可指派其他型別
    4. 函式型別回傳值推論與註記關係,若函式確定不會有結束一天(無窮迴圈),回傳值將自動被推論 never ,若你主動註記 never 則,你就必須確定函式不會有結束執行點
]]>
azsx96385 2021-10-12 19:40:29
TypeScript | interface 研究心得紀錄 1 https://ithelp.ithome.com.tw/articles/10280334?sc=rss.iron https://ithelp.ithome.com.tw/articles/10280334?sc=rss.iron 我習慣理解一個東西,可以套用日常的生活經驗,找出類比、擬人化會幫助我更好理解,今天的議題是最近看到 Type vs Interafce 看起來寫法差不多,讓我興起兩者有什麼不一樣的疑問,所以回頭...]]> 我習慣理解一個東西,可以套用日常的生活經驗,找出類比、擬人化會幫助我更好理解,今天的議題是最近看到 Type vs Interafce 看起來寫法差不多,讓我興起兩者有什麼不一樣的疑問,所以回頭去找相關資料,眾所周知有位大大把TypeScript 寫得很清楚又平易近人,我在閱讀其中兩篇幫助我了解 Type 用法,並記錄心得

https://ithelp.ithome.com.tw/articles/10215586

  1. 什麼是 interface ?

    1. 機動藍圖(這個命名真的讓我跪,簡單明暸),規格概念,可以被組裝、延展
  2. 有什麼特性?

    1. 可以被拓展,讓開發可以表達抽象概念,且複用
  3. 與 type 有什麼不一樣?

    1. 介面(interface),規格(藍圖)概念,用來作為實作時的參考,
    2. 型別(type),靜態資料型態,靜態有固定不變的意思,雖可被複合使用,但本質還是資料型態
  4. 小結

    過去總會分不清兩者,原因是其用法其實多少有些類似,但又有些微不同,看完本篇之後,我發現,不應該先從特性去理解,應該由本質去理解,會比較容易,type 之於 interface 有點 沙發椅 之於 房屋室內設計圖,沙發椅型別一但被定義,那就不能變動,若有尺寸需要,可以再定義出 加長版沙發椅(想表達複合概念),室內設計圖則可能擺入沙發椅型別在每個空間中,但我完成客臥設計後,我可以視為客臥的擺設藍圖為其他空間之基礎規格,因此我需要設計主臥空間配置,就可以直接繼承(沿用)客臥配置去做變更

]]>
azsx96385 2021-10-11 08:18:30
TypeScript | Type 研究心得紀錄 2 https://ithelp.ithome.com.tw/articles/10280057?sc=rss.iron https://ithelp.ithome.com.tw/articles/10280057?sc=rss.iron 我習慣理解一個東西,可以套用日常的生活經驗,找出類比、擬人化會幫助我更好理解,今天的議題是最近看到 Type vs Interafce 看起來寫法差不多,讓我興起兩者有什麼不一樣的疑問,所以回頭...]]> 我習慣理解一個東西,可以套用日常的生活經驗,找出類比、擬人化會幫助我更好理解,今天的議題是最近看到 Type vs Interafce 看起來寫法差不多,讓我興起兩者有什麼不一樣的疑問,所以回頭去找相關資料,眾所周知有位大大把TypeScript 寫得很清楚又平易近人,我在閱讀其中兩篇幫助我了解 Type 用法,並記錄心得

Day 08. 前線維護・明文型別 X 格式為王 - Literal Types - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

  1. 所以什麼是 Type ?
    1. 靜態的資料型別格式,用於簡化程式碼、表示抽象物件
  2. 重點心得紀錄
    1. 明文型別 Literal Type 主要是表達廣義物件格式皆屬於此型別,換句話說 自定義有意義之型別(ex. 請給我一份“炸雞腿套餐”,我可以自定義為一種型別,表達這種型別,需要具有的項目 雞腿、白飯、飲料)
    2. 抽象化思考在程式設計是重要能力,他像是思考“一種感覺”,但核心來說目的是用來定義、區分、具象化某些難以理解的事物,因為沒有具體參照,所以要自行創造參照
    3. 型別化名,使用 type 關鍵字,目的為簡化程式碼、進行型別抽象化,目的一樣是為了讓開發夥伴快速理解用法、簡化程式法、型別驗證檢查
    4. 完整性定律,不無目的新增物件不存在屬性、不指派錯誤型別覆寫物件、及其屬性
]]>
azsx96385 2021-10-10 14:49:30
TypeScript | Type 研究心得紀錄 1 https://ithelp.ithome.com.tw/articles/10279702?sc=rss.iron https://ithelp.ithome.com.tw/articles/10279702?sc=rss.iron 我習慣理解一個東西,可以套用日常的生活經驗,找出類比、擬人化會幫助我更好理解,今天的議題是最近看到 Type vs Interafce 看起來寫法差不多,讓我興起兩者有什麼不一樣的疑問,所以回頭...]]> 我習慣理解一個東西,可以套用日常的生活經驗,找出類比、擬人化會幫助我更好理解,今天的議題是最近看到 Type vs Interafce 看起來寫法差不多,讓我興起兩者有什麼不一樣的疑問,所以回頭去找相關資料,眾所周知有位大大把TypeScript 寫得很清楚又平易近人,我在閱讀其中兩篇幫助我了解 Type 用法,並記錄心得

Day 02. 前線維護・型別推論 X 註記 - Type Inference & Annotation - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

TypeScript 觀念

  1. 什麼是TypeScript?
    1. 讓 js 可以實現強型別特性的開發語言,並可進行型別檢查,減少開發錯誤
    2. 我把它稱作開發者的 賈維斯,內建強大功能提供使用,幫助你嚴謹的開發程式
  2. 為什麼我要用 TypeScript ?
    1. 因為 js 弱型別,讓他變得得過且過,因此在不自律當開發習慣中,埋下雷,讓產品運行錯誤
    2. 因為 TypeScript 型別註記功能,讓他可以利用註記去檢查我的變數運用是否嚴謹(是否心口不一,原本是 string 後來變成 boolean ),像個小管家
    3. 因為 TypeScript 型別註記功能,讓不只我,其他的開發夥伴,看到註記就知道該怎麼正確使用這段程式碼、接受督促,等於寫一份註記,幫助大家降低錯誤
  3. 什麼是型別?
    1. 換個生活說法 “ 樣子、模樣 ”,用來描述任一物質的定義(ex. 純水 - 無色透明液體)
    2. 只是在資料的世界,我們用不同標示來表示不同型別,就像語言一樣,目的都是溝通和運用 (ex. "3" , 3 , true )
    3. 型別的理解,可以以描述任一物質為基礎,做狹義、廣義的認知(只要你覺得他是一個物質,那他就可以是一種型別)
    4. 原始型別:最基礎存在,可以被複合應用成為新的型別(ex. "3":string //純水 )
    5. 物件型別: 原始型別的複合應用,用來表示更複雜的情境 (ex. ["3","3" ]:Object //紅茶)
    6. 明文型別:廣義型別,你自己認為他是什麼,他就是什麼 (珍珠奶茶型別,牛奶、紅茶、珍珠、冰塊)
  4. 本文標示重點之理解
    1. 型別推論目的是即使沒有註記型別,也可透過推論變數型別,作爲驗證檢查的基礎,達成除錯目的
    2. any 是造成型別混亂根源,是因為什麼型別都可以的特性也可以是一種型別 ( any ),但什麼都可以代表就是沒有規則可以隨意變動,那就跟沒有使用ts 一樣,因此並不能達成使用 ts 型別檢查、降低錯誤發生之目的,隨之帶來混亂
    3. 延遲性指派,是指 只命名變數,卻沒有賦值行為,會讓ts 自動判定為 any ,根據第二點,就會帶來混亂
    4. 對延遲性指派,進行型別註記,為了解決 ts 發動自己型別推論的技能,讓變數被註記成 any ,造成混亂,不如就在變數命名時候,就註記為指定型別,讓變數受到 ts 監督驗證
    5. 型別註記的目的,幫助開發者了解變數型別與應用方式,避免 ts 貼心自動推論 any,造成混亂
]]>
azsx96385 2021-10-09 18:23:51
練習嚴謹 https://ithelp.ithome.com.tw/articles/10279307?sc=rss.iron https://ithelp.ithome.com.tw/articles/10279307?sc=rss.iron 我自己秉持著最少力氣解決問題,但換句話說就是,有點得過且過,尤其如果快要放假的時候,這時候“選對環境很重要”這句話就派上用場了,身邊有些標準更高、更嚴謹的同事,其實也是個滿好的事情,他們就像教練...]]> 我自己秉持著最少力氣解決問題,但換句話說就是,有點得過且過,尤其如果快要放假的時候,這時候“選對環境很重要”這句話就派上用場了,身邊有些標準更高、更嚴謹的同事,其實也是個滿好的事情,他們就像教練,在你想要墮落時候,他會提出很多建議,而且理由還很充分,雖然每次我都很想下班,但我還是會針對建議進行調整、討論.然後心裡佩服,這位同事做事讓人放心,真的是有原因的,以下紀錄,最近改善心得

  1. 建立變數需注意,合理性、scope、易讀性、維護性、記憶體資源

    1. 記憶體資源,開發時候,應該心裡對於哪些部分可能造成記憶體負擔要有敏感度,並盡可能做相關處理
    2. scope,method、class、static 應該好好思考這個變數最適合的位置,以及顧慮到可能造成的後續影響
    3. 易讀性,要以隨便拉路人過來看命名,都可以不用解釋就懂的程度
    4. 維護性,重複多次相同的內容,心裡應該要有警示,應該要給他一個名字統一管理了
  2. 有清楚目的的埋Log,且必須知道 log 終極目的是幫助有效確認問題點,進而設計符合目的格

    1. 注意命名,幫助快速找到 ex. Tag + service
    2. 注意格式 ,可以做更有效問題分析 ex. processTime:number
  3. 注意API 規格開出的欄位,並欄位要有明確用法、型別,應該保持有限、可控之彈性

    1. ex. FilesURLs:string[] 就是必須是字串陣列、此外不接受其他格式
    2. files: binary 只能傳檔案,不能當作連結
]]>
azsx96385 2021-10-08 18:38:06
深呼吸、喘口氣,所以你這麼努力是要往哪裡去?(拼命努力,不好嗎? ) https://ithelp.ithome.com.tw/articles/10278753?sc=rss.iron https://ithelp.ithome.com.tw/articles/10278753?sc=rss.iron 周...]]> 周慕姿x鄧惠文 聊《過度努力》、《我想看妳變老的樣子》

今天看到《過度努力》這本書,一路找到這個訪談影片,讓我想起了這一兩年自己很大的轉變,從努力學習開發,到享受、探索開發,這之間最大的差別是,我變得快樂了,所以我想要用這邊文章紀錄這過程中的心得,希望讓正在過度努力的做任何事的朋友,知道原來自己可以有另一種選擇

在過去努力是種生存之道,你知道努力,就一定會有收穫,即使筋疲力盡

那能不能再努力過後,給自己一個不努力的選擇,好好休息充電

我全都要! 沒有目的地的努力,只會讓你累死在半路

記得自己在轉職的時候,接觸程式全新領域,當時並不確定是不是接下來就是要走這行,但又覺得錢都繳了,沒轉職成功,好像很浪費錢,所以努力上課,死背硬背重複練習的撰寫程式碼,但還是效果不彰、甚至想說哎!我是不是真的不適合,一樣的情況在我畢業後回頭去擔任助教,我注意到很多同學作業做得不太好,但其實他很努力,你從他註解很多行就知道,有時候從他的回覆,你可以感覺到他的挫折,很努力、查了很多資料,但怎麼還是寫不好? 有趣的是教學方也一直強調不要這麼研究太多細節,夠用就好,但是對於會過度努力的人,就是會忍不住,因為害怕我少知道一點,我就會失敗,那究竟是失敗什麼?我也不知道,但就是怕

看看自己腳下的位置,想想自己想去的地方,決定路途要有哪些休息站

其實我們並不總是知道自己究竟要到哪一個地方、成為怎麼樣的人,我在報名上課的當下,我也不確定自己是否真的會轉職成工程師,所以我不喜歡這個問題,所以你三年、五年的目標是怎麼樣?,我只能分享我半年、一年的樣子,因為世界變化太快、我也是,記得在某次1on1我跟我的主管這樣說,所以呢?難道我們就當一灘爛肉嗎?其實也是可以,只要你自己喜歡、享受這樣,關鍵是你確定這樣的狀態是你想要的,如果是那你已經在終點,又何須移動

看看自己腳下位置,是想表達你自己是否清楚自己目前的定位、狀態、條件,如果你連自己在哪裡都不清楚,又如何設定目的地,又如何畫出兩點一線的那條線,所以你需要知道自己目前的位置,以當初上課目標轉職為例,剛從資策畢業、對程式開發只知道卻不理解,我有一份junior的工作,有機會學以致用,但我現在在適用期,我需要再三個月證明自己有成長空間,我目前薪資26K,已經報名課程,有既有資源可以讓我提問程式相關問題,諸如此類的對自己現況的描述,換句話說,盤點自已的資源

想想自己想去的地方,想像現階段會讓自己覺得滿意、幸福、快樂的藍圖,我強調現階段是指當下,因為當你了解的越多,會開始發現原來之前自己的藍圖,已經不能讓自己滿意了,所以請允許這個藍圖是否可以被隨時調整的,只要任何時候你覺得不滿意了,就可以調整,這裡的關鍵是畫出藍圖的能力,根本上是為了你的滿意、期待而服務的,以我當時的藍圖為例,我要有能力做產品、接觸數據分析、影響產品決策,我想要能做出帶給使用者好體驗的產品

決定路上有哪些休息站,藍圖有點像是目的地,休息站是通往目的地的中繼休息、沈澱、肯定自己用的,換句話說就是階段性目標,你可以自己決定達到這個目標想做哪些事,以我來說我可能就會徹底耍廢、出門玩玩、充充電,讓自己心滿意足、想要繼續上路、努力走到目的地再出發,允許自己可以不努力,這件事其實不簡單,尤其是對有自我要求的人,但我發現其實休息夠再出發、想清楚再做,可以過得更快樂、更容易達到目標,以我為例,那時候我的休息站是利用學由所成、做出成果、爭取加薪,爭取更好機會,我要30K、我要能夠接觸主要產品、我要能夠開發功能等等

小結

抵達目的地,不只一套劇本,很努力、很犧牲、身心俱疲是一種,走走停停、跌跌撞撞、享受過程也是一種,但真正的關鍵是搞清楚自己究竟想怎麼走這條路,而不是一昧的只接受社會、家庭給你那套說詞跟理由

對於現在努力學習程式的朋友,我想跟他說,如果你要轉職,那先把想要的工作條件寫下來、再回頭去看自己要努力到什麼程度,才可以成功走到目的地,程式開發很有趣,不像讀書考試有背有分,在這裡沒有標準答案、沒有一百分、沒人跟你說好棒棒、沒人會跟你出題庫、拼命努力不一定有收穫,跟現實生活一樣,所以你只要滿足自己、對得起自己就好.

]]> azsx96385 2021-10-07 11:26:21
聽故事,了解問題解決、 lock 、 tranaction - 小白成長篇 https://ithelp.ithome.com.tw/articles/10278430?sc=rss.iron https://ithelp.ithome.com.tw/articles/10278430?sc=rss.iron 前言

最近遇到連續短時間Req,造成資料出現非預期的變動,因此開始在爬文了解可能的解決方案,剛好看到這邊文章,滿親切的重現工作日常,也簡單說明了幾個觀念,因此特別記錄下來,我覺得有...]]> 前言

最近遇到連續短時間Req,造成資料出現非預期的變動,因此開始在爬文了解可能的解決方案,剛好看到這邊文章,滿親切的重現工作日常,也簡單說明了幾個觀念,因此特別記錄下來,我覺得有感的部分、觀念簡單紀錄.

[進程。Processing] 24.鎖定(Lock) - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天

  1. 遭遇問題的邏輯思路

    小白的日常,就是常常遇到問題,然後腦袋一片空白、不知所措,這是正常的,因為接觸的還不夠多、知識量累積的不夠,會腦袋空白、沒有頭緒是完全正常的,但是這不影響你解決問題,因為你腦袋空白,不代表其他人腦袋空白,所以這時候如何正確求助、提問、並從旁了解別人的思路、解方就很重要,偷學的概念,通常這也是學得最快的方式

    現在以我是小艾身份,拆解主管如何解決這個問題的思路

    1. 了解問題,做出適當的緊急處置 (程式掛掉,終止process)
    2. 盤查線索、進行假設、驗證假設(搜索錯誤訊息和過去類似情況、假設有加transation、驗證確實有加入)
      1. 有時候雖然無法百分百確認,但至少可以在許多情境,盤點出可能性最高的下去解決
    3. 總結問題
      1. 因為不必要transaction,導致程式因為等待lock太久不知名原因掛掉
      2. 沒有埋下錯誤提示訊息,導致第一時間無法有效抓出錯誤
    4. 列出解決行動(1. 移除transaction - 避免再次掛掉 2. 加上錯誤提示- 後續發生問題,可以有提示線索)
  2. 新手成長從,不只知道怎麼做,也知道為什麼開始

    回想我當小白的時候,最常做的就是複製貼上,大概理解原因、邏輯就好,遇到問題,我知道要採取什麼行動,但是我卻分不清楚,當下有三個方案,我該怎麼選擇適合的,那時候我覺得,反正問題解決就好,但是隨著接觸越來越多,我發現以上屬於短線思維,只在乎當下解決問題,但是沒考慮到後續可能的情況,包含是否重複發生、是否引發更嚴重問題、是否影響他人作業等等的影響範圍評估能力,這在後續接觸越來越多、旁聽夥伴討論越多就會慢慢建立起來自己的評估能力,以前可能三個方案放在眼前,我覺得他們都一樣,1分鐘做出決定,但現在我可能要花到10分鐘才能做出決定,因為需要更多評估,評估目前我所有的條件、每個方案適合的程度等,時間雖然更久,但是做出的行動,開始有所本、更能根本解決問題,其中關鍵是有方法的從旁學習.

]]> azsx96385 2021-10-06 19:48:19
我目前常用的思考框架 https://ithelp.ithome.com.tw/articles/10277967?sc=rss.iron https://ithelp.ithome.com.tw/articles/10277967?sc=rss.iron 整理一下,我目前的思考框架,學新的東西時候、解決問題的時候可以用,有時候我也常想要偷懶省略一些步驟,但多虧有個嚴謹的夥伴,讓我每次確實把步驟做到,思考框架這個東西,其實一定有些公版,但我覺得每個...]]> 整理一下,我目前的思考框架,學新的東西時候、解決問題的時候可以用,有時候我也常想要偷懶省略一些步驟,但多虧有個嚴謹的夥伴,讓我每次確實把步驟做到,思考框架這個東西,其實一定有些公版,但我覺得每個人可以根據自己常用的情境,客製化自己的框架

學習新東西的時候

本人不是一個好學的人,所以通常會想要學新東西,不外乎兩個原因 興趣、解決問題,我給自己的期許是不想學、就不要學,要學就好好理解,以下是我習慣的思考脈絡,我會列點下來,幫助自己聚焦問題,而不紀錄流水帳

  1. 我要了解什麼新東西?為什麼我需要了解
  2. 這個新東西是什麼東西(用自己的話描述,簡單一句)
  3. 這個新東西有哪些重點?(可以用心智圖延伸)
  4. 然後呢?(消化理解後,我可以做出什麼行動)

遇到問題的時候

不管是生活、工作,總是會遇到問題,當然會有一些情緒反應(ex. 緊張、害怕、興奮..),不過最後還是需要解決問題,我是容易分心、被情緒淹沒的人,所以我會在當下問這些問題,幫助我冷靜下來、聚焦問題、想辦法

  1. 現在發生什麼事?影響範圍多大?

  2. 我現在可以先做什麼行動?(緊急處置、爭取時間)

  3. 所以問題是什麼?有什麼線索,可以證明是根本原因?

  4. 我有哪些解決方案?這些解決方案為什麼可以解決問題、優缺點?

  5. 我推薦哪一個解決方案?為什麼?考慮哪些資源限制?(時間、金錢、可行、難易、人力)

  6. 這個方案是否確實解決問題?(重現問題、進行驗證、失敗原因)

  7. 後續問題是否會再次發生?

  8. 這個問題有什麼徵兆?

  9. 如果再次發生,我怎麼快速掌握關鍵線索,進行判斷?(分析、監控)

]]>
azsx96385 2021-10-05 20:21:16
所以什麼是MQTT啊? https://ithelp.ithome.com.tw/articles/10277397?sc=rss.iron https://ithelp.ithome.com.tw/articles/10277397?sc=rss.iron
  • 前言

    在某次跟同事的討論中聽到這個名詞,"A:你知道mqtt嗎? W:痾 我不知道 A:那需要去懂一下",這不是來懂一下了嗎?其實剛開始真的...]]>

  • 前言

    在某次跟同事的討論中聽到這個名詞,"A:你知道mqtt嗎? W:痾 我不知道 A:那需要去懂一下",這不是來懂一下了嗎?其實剛開始真的不知道協議什麼的,聽到都是一頭霧水,腦袋直接關機,不是postman按下去,然後有回應就好了,後來才發現根據不同情境、不同限制下,有不同適合的解決方案這件事

  • 重點紀錄

    • 所以什麼是MQTT?

      • 物聯網的通訊協定

      • 適應窄頻寬、低耗能的情況下,傳送與接受訊息

      • 使用 publish/subscribe 模式傳送資料

      • 角色:Publisher \ Subscriber \ Broker

        既然是通訊,就會有發布方、接收方、中間商,就跟我打給你,我是發布方、你是接收方一樣,當然還會有個中間商,以手機來說就是各大電信(ex. 中華電信)
        既然是協定,就會有Server 和 client ,server 負責處理需求、client 負責發送需求給server 處理

        在 MQTT 有三個角色 Publisher \ Subscriber \ Broker ,Server 扮演 Broker 角色,負責處理 Client 需求( Publisher & Subscriber ),進行發布訊息、接受訊息

      • 訊息和發送方式: Topic & Qos

    • 什麼是 publish/subscribe ?

      publish/subscribe 模式,簡單説 Subscriber 決定要訂閱的 topic ,往後有相關資訊,Broker 就會遞送資訊給Subscriber,而 Publisher 則是針對 topic 傳送指定訊息,給Broker 發布

      舉個例子,“喜歡我的影片,別忘記訂閱、按讚、開啟小鈴鐺,以後最新的資訊,你就會第一時間收到喔!(os: 但我就不想第一時間收到 ><)” ,每當你(Subscriber)在 YT 按下訂閱的時候,你就是跟 YT( Broker)説,以後這個頻道的上新片,都要跟我說,假設今天晚上六點 這個頻道(Publisher) 上了新片,就會同步送一個訊息跟 YT(Broker) 説 “我上新片囉!” ,YT(Broker) 就會因為你之前的訂閱,送推播到你的手機 “我上新片囉!”

    • 為什麼要適應 窄頻寬、低耗能 ?

      常聽到輕量、輕量,就不太知道什麼叫做輕量、為什麼要輕量,換個生活的例子,我們會說“哇!這個生乳捲的口感好輕盈”就大概是類似概念,在現實世界,什麼都需要錢,在開發世界,server 處理每一個行為,都需要耗費資源,因此我們通常會根據不同情境,選擇CP最佳的選擇
      回到這裏,為什麼要適應窄頻寬? 因為在物聯網的是世界中,我只是要下命令(“開燈”),讓裝置做出相對應的動作,不像是我看YT我還可以要求顏色、畫質等,相對來說單純簡單,因此只需要窄頻寬就可以,再多我也用不到(ps 我的理解)

      為什麼低耗能?每個裝置處理指令是需要耗費資源的(ex. 智能電燈要處理開燈指令),因此如果是屬於充電式的物聯網裝置,那麼低耗電就等於更長的使用時間

    • 什麼是 Qos ?

      Qos 我理解的是傳送品質,分為 0 \ 1 \ 2 數字越大,品質越好,品質來自是否確實遞送訊息

      • Qos 0 : 最多傳一次 (送出訊息後就不管了 適用於重要性不高、漏接也沒關係的情況)
      • Qos 1 :至少傳成功一次 (會確實讓 Broker 收到,但可能因網路訊號,有重複訊息的情況)
      • Qos 2 :確實傳送一次 (更嚴謹的傳送,多了回報已經發送訊息的環節)
  • 然後呢?

    • 方案不只是方案,需要去了解背後的WHY(為什麼這個方案適合?) WHAT(那方案要怎麼適用、有什麼顧慮)
    • 為什麼他是一個適合的方案,這個思辨很有趣,也是讓我有點樂在其中的原因
  • ref

    iOS x IoT ( 2 ) - MQTT 簡介

    murmur

    最近工作地方,出現了一個神奇的東西,就是好好摸手工皂,以前都是洗手乳,洗完都會滑滑的、更可惡的是有人居然在空了之後補了洗碗精,真是天理不容(為什麼要假裝洗手乳),所以手工皂的出現,讓我的工作日常多了一些小確幸,終於手上可以不要是洗碗精的的味道!!

    ]]> azsx96385 2021-10-04 17:11:46
    過了一年,我更能享受其中了 謝謝你們 https://ithelp.ithome.com.tw/articles/10276997?sc=rss.iron https://ithelp.ithome.com.tw/articles/10276997?sc=rss.iron 想想距離去年參加鐵人也過了一年了,記得當時我才剛到職大概滿半年吧,到現在也一年半了,我發生了哪些變化呢?又成長了什麼了呢?,我想用簡單列點的方式來回顧一下,填滿沒有庫存文章的一天.

    想想距離去年參加鐵人也過了一年了,記得當時我才剛到職大概滿半年吧,到現在也一年半了,我發生了哪些變化呢?又成長了什麼了呢?,我想用簡單列點的方式來回顧一下,填滿沒有庫存文章的一天.

    1. 終於定下心,要好好發展BE能力樹

      記得去年的自己,心情很浮動,對於自己未來的發展還有很多想像,例如PM、manager、資料工程師等等,一個什麼都想嘗試看看的心情,以至於無法真正踏實的好好學習技術,即使我真的想,記得那時候也剛Mentor聊了不少次,當初的我只想要他給我一個明確的目標,然後我就會努力的向那個目標衝刺,即使很晚下班,直到他跟我說 “我很開心你視我為榜樣,並且想像我一樣,甚至是得到我的肯定,但我不希望你這麼做,你應該要完成你自己的目標、你想成為的樣子,不要總是問怎麼做,因為即時我跟你說了,那也是我的東西,不是你的,要去追為什麼,雖然會慢一點,但是你理解後的東西才真正是你自己的” ,那時候我知道,原來我該問的不是what而是why,我要追求的不是多快多有效率的完成任務,而是了解怎麼把任務做好,並且確實的完成.

      回想當時的心態,自已其實做得很辛苦、也不快樂,我感覺壓力很大、又覺得事情做不好、達不到自己的目標,看著同事熱情的討論問題,我整個提不起勁,因為我聽不懂、而且我自己任務都做不完,哪有時間去理解那些問題,後來我開始允許自己做不到某些任務、可以稍微讓任務延遲一點,讓自己減壓,並且利用時間好好去了解、去討論、去詢問問題,找到對於我來說,比起任務更重要的是自我成長,而我有權利為了自我成長去跟主管爭取更多時間好好把事情做好,第一次也許會花比較多時間,但後續速度會提升.

    2. 找到自己生活節奏,好好工作、好好生活
      記得有次討論,我分享工作很多做不完、又一直出錯,讓我很挫折壓力很大,然後就很厭世,意外的是我的厭世會擴散,讓同事感覺得到,當我狀況不好,我的表現會有連帶影響,當時有位同事就提到說,那就想辦法,不要讓自己進入那個狀態,想一想有什麼辦法可以讓你維持好心情、好好工作.

      首先先承認自己能力不足,有些事情做不到就是做不到,不要讓自己壓力那麼大,但不是擺爛,而是去爭取時間、資源讓自己變成做得到,也許是協商需要更多時間、需要其他夥伴討論、需要相關文件等等,了解自己擁有什麼資源,讓自己可以做到,這是以前逼自己使命必達的我,沒有想過的事,原來我還可以協商,做不好真的沒關係,再來一次就好,重點是過程有沒有成長學習到.

      最後給自己一個目標,準時下班,這真的是我覺得最幸福的事,要是離開公司的時候,還可以看到太陽,我就會心情很好,工作很重要、但是下班時間更重要,因為那是你可以好好休息的時候,拼命三郎不適合開發工作,因為你會需要大量時間消化吸收學習成長,否則只會越做越錯,越做越慢,這樣的日常對我來說,就像是打怪,要是今天打不過、卡關了,那就明天再繼續挑戰,甚至可以招喚神隊友一起打怪,於是我終於可以開始享受工作.

    3. 放棄設定目標,好好做好當下每一件事

      在工作期間我一直在摸索適合自己的工作方式,設定目標、使命必達的短跑型努力,似乎很容易讓我耗費精力,後來我摸索出享受開發、好好進步的方式,我不再設定明確目標,而是好好把自己手上的任務,好好的做到理想的標準、好好的面對遇到的問題,了解他的底細,就像為了破關努力研究攻略的自己,我覺得去掉目標導向,改成解任務破關的心態,讓我不再每天壓力山大、緊張兮兮、疲憊不堪,開始慢慢的進度、能夠開口跟同事討論問題、提出解決方案等等,這讓我感受的自己的成長,也覺得這是適合我的方式,一天比一天懂一點,做得好一點,就好.

    最後要感謝,在這一路幫助我長大的兩位很棒的夥伴Windy、Jack,謝謝你們無私的分享、額外花時間幫助我成長,幫助我找到適合自己的成長路徑和職涯目標,雖然你們可能還不知道,但我會有一天讓你們知道der

    ]]>
    azsx96385 2021-10-03 21:24:45
    nestJS-MicroService-gRpc 處理更新null情況 https://ithelp.ithome.com.tw/articles/10276254?sc=rss.iron https://ithelp.ithome.com.tw/articles/10276254?sc=rss.iron
  • 前情提要&遭遇問題

    nestJs 是一個以express為基礎的後端框架,該框架可以選擇gRPC實作MicroService,並撰寫proto作為溝通介面,作為...]]>

  • 前情提要&遭遇問題

    nestJs 是一個以express為基礎的後端框架,該框架可以選擇gRPC實作MicroService,並撰寫proto作為溝通介面,作為server來說,可以成功透過接收到資料,不過根據proto3三特性,選填、永不null、初始化給預設值,因此導致一情況,null欄位會消失,GET資料,null 欄位直接消,PATCH資料,null欄位送到server端一樣消失

    補充:

    • 架構:endPoint - MicroService
  • 研究歷程

    • Protobuf and Null Support
      • 三種處理Pattern: OneOf NullValue Pattern 、 FieldMask Pattern 、Has Pattern
      • 兩種避免使用情況:Default Value、Null String
      • 結果:
        • 三種建議Pattern,因都有使用到gRPC支援語言的內建方法,尚無找到在JS實作的方法,因此無法採用
        • 兩種避免情況,確實現在有如此的誤用,因此後續會避免,進而提出以下暫時解決方案
  • 解決方案

    • 目的:提供前端一致的串接體驗,只要是空值就送 null ,較為直覺
    • 針對 Patch 資料情境(只送需要更新欄位)
      1. endpoint 解析 FE request 找出 null 的欄位,記錄下來,繼續往microService丟
      2. 針對巢狀資料,需要一次送全部並字串化,因為在DB 是單一 column
    • 針對 Get 情況,還在想
    User Entity
    
    {
      "name":string
    	"phone":string
    	"age":number
    	"isVip":boolean
    	"createdAt":Datetime
    	"contactInfo":{  //for 支援jsonDB
    		"address":string
    		"email":string
    		}
    }
    
    //針對 Patch 資料情境(只送需要更新欄位)
    //1. endpoint 解析 FE request 找出 null 的欄位,記錄下來,繼續往microService丟
    //2. 針對巢狀資料,需要一次送全部並字串化,因為在DB 是單一 column 
    
    -- FE 傳送的req
    {
      "name":"王小明"
    	"phone":null
    	"age":number
    	"isVip":boolean
    	"createdAt":null
    	"contactInfo":{ 
    		"address":null
    		"email":"e@xample.com"
    		}
    }
    
    -- endpoint 傳給 microService 結構
    {
      "name":"王小明"
    	"phone":null
    	"age":number
    	"isVip":boolean
    	"createdAt":null
    	"contactInfo":'{"address":null,"email":"e@xample.com"}' //巢狀結構字串化
    	+ "setNullProperty":["phone","createdAt"] //直接紀錄需要null欄位
    }
    
    --proto
    
    message patchUser{
    	repreated string setNullProperty = 1
    	string name = 2
    	string phone = 3
    	number age = 4
    	boolean isVip = 5
    	string contactInfo = 6
    
    }
    
  • ref:

    Protobuf and Null Support

    Documentation | NestJS - A progressive Node.js framework

    ]]> azsx96385 2021-10-02 10:26:53
    TypeOrm | Repository APIs 用法紀錄 3 https://ithelp.ithome.com.tw/articles/10275285?sc=rss.iron https://ithelp.ithome.com.tw/articles/10275285?sc=rss.iron https://typeorm.io/#/repository-api

    關於 save() \ delete() \ remove() 有些進階設定以下紀錄

    save()

    userRepository.save(users, {chunk: users.length / 1000});
    
    • data & listeners 牽涉監聽、訂閱,研究清楚後更新
    • transaction:boolean 預設true ,
      • ex. 若不需要可以關閉 { transaction: false }
    • chunk 批次處理資料
      • ex. { chunk: 10000 }
    • reload 完成save後回傳資料,預設true
      • ex. 若不需要可以關閉 {reload: false}

    delete() \ remove()

    userRepository.remove(users, {chunk: entities.length / 1000});
    
    • data & listeners 牽涉監聽、訂閱,研究清楚後更新
    • transaction:boolean 預設true ,
      • ex. 若不需要可以關閉 { transaction: false }
    • chunk 批次處理資料
      • ex. { chunk: 10000 }
    ]]> azsx96385 2021-10-01 09:51:10
    TypeOrm | Repository APIs 用法紀錄 2 https://ithelp.ithome.com.tw/articles/10275079?sc=rss.iron https://ithelp.ithome.com.tw/articles/10275079?sc=rss.iron https://typeorm.io/#/repository-api

    常常在使用,但也只有使用到其中主要的method,剛好遇到 save vs. update 差在哪裡的疑問,就一起記下來

    • count() 計算符合條件的資料數量,常用於分頁功能

      • await repository.count({ firstName: "Timber" })
    • increment() 增加指定欄位的定數量

      • await manager.increment(User, { firstName: "Timber" }, "age", 3);
    • decrement() 減少指定欄位的指定數量

      • await manager.decrement(User, { firstName: "Timber" }, "age", 3);
    • find() 調用指定條件資料

      • ex. await repository.find({ firstName: "Timber" });
    • findAndCount() 調用指定條件資料,並計算資料數

      • ex. const [timbers, timbersCount] = await repository.findAndCount({ firstName: "Timber" });
    • findByIds() 使用id 調用資料

      • ex. await repository.findByIds([1, 2, 3]);
    • findOne() 查詢符合條件的第一筆資料

      • ex. await repository.findOne({ firstName: "Timber" })
    • findOneOrFail() 查詢符合條件的第一筆資料,若無則報錯

      • ex. await repository.findOneOrFail(1);
    • query() 執行sql 語法

      • ex. await repository.query(SELECT * FROM USERS)
    • clear() 清宮指定table所有資料

      • ex. await repository.clear();
    ]]> azsx96385 2021-09-30 09:02:07
    TypeOrm | Repository APIs 用法紀錄 1 https://ithelp.ithome.com.tw/articles/10274469?sc=rss.iron https://ithelp.ithome.com.tw/articles/10274469?sc=rss.iron https://typeorm.io/#/repository-api

    常常在使用,但也只有使用到其中主要的method,剛好遇到 save vs. update 差在哪裡的疑問,就一起記下來

    • hasId( ) 確認Table 是否已經有指定id 紀錄存在
      • ex. repository.hasId(user)
      • 適用新增需要指定ID 的情境
    • getId( ) 回傳紀錄 pk id (如果是複合主鍵,則會回傳傳Object)
      • 適用複合主鍵快速取得
      • ex. repository.getId(user);
    • create( ) 建立一實例,不確定會不會存到db
      • ex. repository.create() = new User()
    • merge( ) 合併多欄位成為一個單一實例
      • ex. repository.merge(user, { firstName: "Timber" }, { lastName: "Saw" })
    • preload() 可以直接覆寫讀取的資料,返回實例,但覆寫的物件必須包含id
      • ex. await repository.preload(partialUser)
    • save() 儲存給予的實例,若DB已存在,更新資料(須包含id),若資料不存在,則建立新的一筆
      • ex. await repository.save(user);
    • remove() 移除給予的實例資料
      • ex. await repository.remove(user)
    • insert() 插入一新實例、陣列實例
      • ex. await repository.insert(User)
    • update() 根據指定條件,更新符合條件之資料
      • ex. await repository.update(1, { firstName: "Rizzrak" })
    • delete() 根據指定條件,刪除符合條件之資料
      • ex. await repository.delete({ firstName: "Timber" });
    • softDelete() & restore () 使用id 軟刪除、恢復資料
      • ex. await repository.softDelete(1)
      • ex. await repository.restore(1)
    • softRemove() & recover() 使用實例 軟刪除、恢復資料
      • await repository.softRemove(entities)
      • await repository.recover(entitiesAfterSoftRemove)
    ]]> azsx96385 2021-09-29 10:24:45
    管理API 變化之API version https://ithelp.ithome.com.tw/articles/10273767?sc=rss.iron https://ithelp.ithome.com.tw/articles/10273767?sc=rss.iron REST API V...]]> REST API Versioning

    本來對API分版號有個大概的印象,不外乎是邏輯、res 有了異動,但確切是為了什麼?又有哪些切版方式呢?我找到了這篇文章,讀完之後,我理解到重點不是怎麼切,而是怎麼管理變動,又可以確保API 使用的一至性

    以下為整理之重點

    1. 什麼時候要升API版本呢?

      重大變動時刻,包含以下三種情況

      • response 格式異動
      • request 格式異動
      • API 移除了任一部分

      其實開發過程中不免會有陸續的變動,但我想以上屬於大致情況,我認為是當需求有所變動,而既有的API所提供的內容已經不敷使用、但又有一些情況會用到,那麼這時候可以選擇切版,關鍵是 重大變動+有不能更新既有版本API的情況

    2. 有哪些方式可以升API 版本呢?

      1. URI (最常用情況,通常是v[x],x 為版號數字)
      2. Custom Request Header 客製化表頭 (建立一專屬表頭,作為識別用)
      3. Accept Header (合併在 accept 表頭中)

    總結,其實怎麼做切版通常不是重點,因為就只是一個flag作為辨識用而已,但真的有挑戰的是,多少的變動,應該切版,這個決策的過程,如作者提到的管理變動,讓API呈現一至性的體驗,不會因為升版,而倒置既API 出現問題

    ]]> azsx96385 2021-09-28 09:12:34
    到底是什麼意思?Typescript Partial https://ithelp.ithome.com.tw/articles/10273198?sc=rss.iron https://ithelp.ithome.com.tw/articles/10273198?sc=rss.iron 今天在翻舊code的時候,看到了 Partial 的寫法,所幸來查查這到底是什麼意思.弄懂了之後看code也比較不會想睡,因為終於知道在什麼囉~

    以下紀錄心得

      <...]]> 今天在翻舊code的時候,看到了 Partial 的寫法,所幸來查查這到底是什麼意思.弄懂了之後看code也比較不會想睡,因為終於知道在什麼囉~

      以下紀錄心得

      1. 什麼是 Partial ?

        簡單說Partial 幫你複製了一份 Type ,然後把裡頭的 property 設為 optional ,也就是可有可無

      2. 什麼時候適合使用 Partial ?

        當你在更新一筆紀錄的時候,假如有三個欄位,而你只想更新一個欄位,那麼你就可以使用 Partial 作為 req 的標示

        節錄官方文檔

        interface Todo {
          title: string;
          description: string;
        }
        
        function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
          return { ...todo, ...fieldsToUpdate };
        }
        
      3. 那假如我要欄位都必填呢?

        Required 是你的好選擇,他就是 Partial 的反面,建立一個 type ,其中屬性設為 required 必填

      4. 還有其他類似的 Utility Type 嗎?

        1. Readonly 不可對Object 內容重新指派
        2. Record<Keys,Type> 建立類似mapping 的 Type
      ]]>
      azsx96385 2021-09-27 14:17:28 到底是什麼意思?static 靜態方法 https://ithelp.ithome.com.tw/articles/10272339?sc=rss.iron https://ithelp.ithome.com.tw/articles/10272339?sc=rss.iron 記得第一份工作時候,有個同事一直在那邊嚷嚷靜態方法比非靜態效率高,偶而喊喊抽象化思考很重要,我當時心裡就是一頭霧水,然後覺得崩潰,怎麼這個世界有這麼多無法理解的命名.

      最近看到同事...]]> 記得第一份工作時候,有個同事一直在那邊嚷嚷靜態方法比非靜態效率高,偶而喊喊抽象化思考很重要,我當時心裡就是一頭霧水,然後覺得崩潰,怎麼這個世界有這麼多無法理解的命名.

      最近看到同事在一個method前標註了 static 表示這個function 為靜態方法,剛好掀起我的回憶和興趣,所幸查詢了一下,現在終於看得懂一些了,一下紀錄我的理解

      1. 靜態方法是什麼意思?

        我其實沒有查到為什麼叫做靜態,不過自己的理解靜態可以表示儲存在記憶體的時間,有個固定的意味,意思是當載入程式時,會主動配給記憶體給靜態方式,後續無論如何實例都會在固定的記憶體位置.

        至於 static 就是一個標示符號,註記這個method 為靜態

      2. 靜態方法有什麼特性?
        只能透過類別Class呼叫,不可透過實例化類別的實例呼叫

      3. 靜態方法跟非靜態的差異?

        1. 生命週期:靜態方式載入程式即配給記憶體中,不會被清除,除非關閉程式,非靜態當不被參考時,就會被釋放
        2. 效率:靜態方法效率較非靜態高
        3. 所有權:靜態方法屬於類別,非靜態方法屬於類別的實例

      https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Classes/static

      https://www.njarts.cn/a_keji/202106/211084.html

      https://ithelp.ithome.com.tw/articles/10242580

      ]]> azsx96385 2021-09-26 06:48:57
      要上傳檔案,你需要知道的事-stream binary https://ithelp.ithome.com.tw/articles/10271682?sc=rss.iron https://ithelp.ithome.com.tw/articles/10271682?sc=rss.iron Do you want a better understanding of Buffer in Node.js? Check this out.

      串流平台、串流平台,常聽人這樣形容netfliex 、 spotify ,但一直不明白到底是啥意思,剛好最近在接觸檔案上傳的任務,藉此認識到 stream binary 概念,爬了許多文章,找到這篇,作者用很平近人好懂的方式,解釋 Binary Data 、Buffer 、Stream 的概念,激推

      以下紀錄我的理解重點

      1. 什麼是 Binary Data ?

        To store or represent a piece of data, a computer needs to convert that data to its binary representation

        Binary Data 使用Google 翻譯就是 二進位資料,我對 Binary Data 理解是,他是資料的最小型態由許多01組成,每個0、1 都是一個bit ,一如我們在瀏覽器看到12這個數字,但是在電腦理解和處理則是 1100 ,你會發現原來你在畫面上看到12數字,其實是電腦編碼 1100 後的結果,顯示在畫面上.
        以上數字只是其中一個舉例,我們日常的檔案如 圖片、影音等其他不同類型的資料.

      2. 什麼是 Stream ?

        The whole concept is, you have a huge amount of data to process, but you don’t need to wait for all the data to be available before you start processing it.

        我的理解是有順序的,把資料從一端送到另一端去,以上引述作者原文,我的理解是,其實為了讓使用體驗更好,使用資料串流的技術,將一份切割成為細塊,然後有順序的遞送到顯示端,顯示端也不需要等到完整資料到齊,可以根據順序,先來先處理,這樣的好處是讓使用者可以節省時間及早操作資源,實際應用如Youtube,載入多少看多少

        從生活經驗發想,假如今天來去吃餐廳,點了一份套餐,有湯、沙拉、飲料、主菜,通常在完成點之後,餐廳會先送湯品、沙拉讓你先開始享受,爭取時間處理主菜,等到你吃得差不多了,主菜也吃差不多,剛好可以送上讓你享受,所這樣我可以說,送餐的過程使用了串流的概念,把餐點切割成幾個部分,依序遞送到客戶端,讓客戶節省時間、及早存取資源.

      3. 什麼是 Buffer ?

        Buffer 中文是緩衝區,作者舉一個例子,我覺得滿貼切的

        作者提到,客運站通常會有個設定,就是需要等待車上的乘客都到齊了、發車時刻到了,才會出發,但在過程中乘客可能不會都準時抵達,因此會需要有個等待區,讓顧客依序就位,準備出車,通常是車子上的車位.

        所以在這裡等待區的概念就是 buffer ,用於暫存資料,依序等待被處理,乘客就是被切割成小塊的資料,通常資料是暫存在電腦的記憶體中

      4. 所以這個上傳檔案的任務有什麼關係?了解了帶來了什麼好處?

        最近的任務有需要做轉檔案的任務,在查看doc 時候,發現他是需要先取得檔案的buffer,然後再進行轉檔案,因此理解了這個概念,我可以知道,只要我成功取得buffer 就可以執行後續轉檔案,不需要透過doc範例必須讀取實際檔案

      ]]> azsx96385 2021-09-25 08:02:30
      要上傳檔案,你需要知道的事-MIME(1) https://ithelp.ithome.com.tw/articles/10271086?sc=rss.iron https://ithelp.ithome.com.tw/articles/10271086?sc=rss.iron MIME 類別 (IANA 媒體類別) - HTTP | MDN


      當我們要發 request 時候,我們需要在 header 中設定 Content Type ,告訴對方說“ 嘿! 我送給你的資料是這種類型 ”讓對方可以用適合的方式解析處理你所提供的資料,想像一下,要是你不說、亂說一個錯誤的類型,會發生什麼事? 對方可能對你已讀不會、或者無法正確取得,你所提供的資料,這樣不是很傻嗎?

      Content Type 中有個屬性 media type,是讓你填寫所送資料類型,而你必須根據 MIME 格式填寫,不可以亂填

      什麼是 MIME 媒體類別 ?

      MIME 是一種標準,用來表示文件、檔案、各種位元組

      ex. image/jpg

      那 MIME 結構由什麼組成呢?

      type/subtype;parameter=value
      
      ex. 
      text/plain;charset=UTF-8
      

      主類別、子類別、參數

      理解方式第一字是主要類別,第二字是子類別,使用“/”隔開,並不包含區隔,後方可加上參數,提供更多細節,如該如何解析

      主要類別 ( type ) 子類別(subtype) 是什麼? 有哪些呢?

      type 為廣義分類,subtype 則為資料精確類別

      主要類別 ( type ) 簡介
      根據傳送資料多種,分為 discrete 單一 and multipart 兩個家族

      image/jpeg vs multipart/form-data
      
      

      discrete 單一家族

      中文是離散,但可以簡單理解為單一資料類型的標註(ex. image/jpg ),適用於單次req只傳送單一種類型資料,家族成員如下

      • application 適用多數 二進制資料 (binary data) ex. application/pdf
      • audio 適用音訊類資料 ex. audio/mpeg
      • font 適用各類文字型檔案 ex. font/ttf
      • image 適用各種圖片行檔案 ex. image/jpg
      • model 適用各種模型累資料, 3D object ex. model/3mf
      • text 適用各種人類可閱讀之文字資料 ex. text/html
      • video 適用各種影像資料ex. video/mp4

      問:萬一我就不想送的精確,我想要通用、模糊、概括一點行嗎? 可

      text/plain 可適用各種文字資料

      application/octet-stream 適用各種二進制資料

      multipart 家族

      當你要一次送多種 MIME Type 就可以使用這家族的主類別

      • message 適用寄信時、大量文字情境 ex. message/partial
      • multipart 適用填表單情境 ex. multipart/form-data

      後話

      一般常見要填寫問卷、上傳檔案都會用 multipart/form-data ,這也是最近有開到相關api 才來了解這一塊der

      ]]> azsx96385 2021-09-24 11:14:00
      要上傳檔案,你需要知道的事-Content-Type https://ithelp.ithome.com.tw/articles/10270541?sc=rss.iron https://ithelp.ithome.com.tw/articles/10270541?sc=rss.iron 之前一直矇矇懂懂,現在開始認識也不遲

      Content-Type: text/html; charset=UTF-8
      Con...]]>
                                          之前一直矇矇懂懂,現在開始認識也不遲

      Content-Type: text/html; charset=UTF-8
      Content-Type: multipart/form-data; boundary=something
      

      Content Type 用來表示資源的 media Type

      Content Type 之於 Header 就如同營養標籤之於品客洋芋片,標示著這個容物的熱量、碳水、蛋白質,而Content type 標示著,這個 request 內含什麼類型的資料.

      Content Type 有三種組成

      • media-type req所傳送的資料類型(MIME)
      • charset 資料類型適用的編碼
      • boundary 多段資料適用

      為什麼需要知道?

      因為當你再開api時候,有時你需要明確設定你可以接受的資料類型,例如,我接受你上傳圖片給我,但是我只接受jpeg,就可以根據 MIME 設定處理條件 image/jpeg ,對於BE來說,是可以增加資料正確性的手段

      ]]>
      azsx96385 2021-09-23 17:06:38 任務開發檢討 https://ithelp.ithome.com.tw/articles/10269648?sc=rss.iron https://ithelp.ithome.com.tw/articles/10269648?sc=rss.iron 我理想的情況是,

      事前planing好API(req、res),完成每隻api估時,妥善把開發過程分配在每週

      API 開發,採開完就交付,交棒FE串接,讓PM達成在預...]]> 我理想的情況是,

      事前planing好API(req、res),完成每隻api估時,妥善把開發過程分配在每週

      API 開發,採開完就交付,交棒FE串接,讓PM達成在預期時間,可以測試預期功能

      事實情況是

      BE開發前置時間耗時過久(建立entity、開module、建立DB)、需求變更與討論(異動DB schema level )、BE開發意外情況(技術研究(GoogleStorage)、需求意外地無法達成)、邏輯規劃不完全等因素,導致

      1. api 雖然準時交付、但是品質不佳、規格不清,提升FE的溝通成本(確認用法、錯誤回報)
      2. 自身程式碼雜亂不彰,易讀性差、嚴謹度不夠

      針對以上情況,我採取措施

      1. 以準時交付約定的API 為原則,完成度70%就可先上,至少讓FE可以先接、先測
      2. 阻礙夥伴開發的問題,優先解決
      3. api 錯誤回報、需求變更,訂定優先度,利用當周完成進度的 buffer time 進行處理
      4. 需求變更部分,另外開卡紀錄變更,也另外估時,便於回顧時檢視是否可以優化部分

      這次做得好的部分

      1. api 都有如期交付
      2. 任務優先度安排,以交付api 優先,錯誤處理、需求異動次之

      這次做得差的部分

      1. 重要文件缺乏完整性,讓文件使用者不易理解、取得必要資訊
      2. 交付api 品質不佳、程式碼雜亂,需要再花時間修整

      如果再來一次,我可以怎麼樣做得更好(交付品質好API \ 去除不必要溝通成本)?

      1. 優先產出 API DOC
        1. swagger 用來提供完整的 api 串接(url \ req \ res \ error Exception \ 參數使用方式 )
        2. google doc 用來給FE report api bug 、error 、欄位異動告知,於上版後淘汰
      2. api 邏輯plaining 時候,應該再更細緻(api 資料流的flow \ 例外處理 \ 需要儲存的欄位 )
      3. plaining 時候,應該包含 module 架構、預計會產生的 dto 、enum 、config ,盤查哪些可能後續有效能問題
      4. 盡可能盤點所需之範例資源(要開權限的名單、資料匯出匯入格式、檔案上傳範例檔案、需分析欄位)
      ]]> azsx96385 2021-09-22 10:22:11
      開 api 日常心得筆記 https://ithelp.ithome.com.tw/articles/10269225?sc=rss.iron https://ithelp.ithome.com.tw/articles/10269225?sc=rss.iron 開 api 規格是個有趣的事情,從欄位命名、資料階層設計、易讀性、合理性、方便性都是需要考量的點,對於BE來說,開api規格就像是在推出一件產品,而使用者是前端夥伴,我們都接過很鳥的api,所以...]]> 開 api 規格是個有趣的事情,從欄位命名、資料階層設計、易讀性、合理性、方便性都是需要考量的點,對於BE來說,開api規格就像是在推出一件產品,而使用者是前端夥伴,我們都接過很鳥的api,所以自己會希望自己開出來的規格是盡量能夠適應變化,又給前端有好使用體驗的api

      api 結構設計

      目的導向:方便寫入DB vs. 易讀易理解的結構

      這是最近任務做Patch API 帶來的啟發,過往在更新資料時,我會希望api req 盡可能與DB schema 相同,這樣我可以省下從req 擷取相關欄位組成需要儲存資料的過程,不過這樣的情況比較適合成熟穩定、不易變動的需求,因為每當遇到變動需求,我就需要重新調整 api req 規格,造成FE 多出相關成本來適應這場變動,只因為我想要可以不需要有資料擷取的動作.

      那麼好的方式是什麼呢?
      我認為是,複雜度較高的Patch Api (ex.超過一個階層的req Object),應該以易讀易理解的結構為目標,讓FE可以快速理解,相關資料應該放到哪些物件中,而非一個規格適應不同規格,只因為可以直接存進去DB,這樣帶來的好處是,如果需求有所變動,FE 可以只針對變動部分調整,而省下因為相依性帶來變動成本

      api 易讀性標註

      新手時開api 只覺得,阿我給你欄位名稱就好,後來開始知道最好還標上型別,再後來知道有enum欄位最好也寫上enum,欄位命名精準好理解,也能幫助api串接正常,

      後來了解到,寫的越清楚,可以省下不必要的溝通成本,讓開發者都好好專心開發,不過開發時間總會有時間壓力,究竟要寫得多完整是可以取捨的,但長期來說,我還是會期待自己可以有效的運用時間,開出一份盡可能適應變動、易讀易理解的好規格,讓串接我開的api的FE夥伴可以有愉快的串接經驗,我只能說我還在往這條更好的路上邁進,可能還不夠好,但是會越來越好der

      ]]>
      azsx96385 2021-09-21 20:13:42
      看Youtube學|proxy vs. reverse proxy https://ithelp.ithome.com.tw/articles/10268435?sc=rss.iron https://ithelp.ithome.com.tw/articles/10268435?sc=rss.iron https://www.youtube.com/watch?v=ozhe__GdWC8&ab_channel=HusseinNasser

      proxy vs reverse proxy

      • 根據代理對象、角度作為區分
        • proxy - 代理 client 向 server 發送請求 ,server 不會知道 client 是誰

          • 好處

            • 匿名性 - Proxy 替 client 發送請求,所以Server不會知道 client 是誰
            • 快取功能 - 針對常用資源做快取,省下request時間、資源
            • 封鎖特定資源 - 設定條件,讓Proxy 不幫忙發送 request
            • 地理位置限制 - 設定條件,拒絕特定地理位置的 request
          • 說個故事好了

            靜香人美心也美,長大後搬入公寓,公寓有管理員,會負責幫靜香處理一些日常事務

            • 匿名性
              • 熊貓送餐到靜香公寓,管理員幫她轉交晚餐,靜香不會知道外送員是誰,她只知道管理員會把餐點送給她
            • 快取功能
              • 靜香熱愛團購沐浴乳,又堅持下班親自收件,但每次都要接貨運人員電話確認時間,覺得厭煩疲倦,所以她交代管理員要是貨運人員詢問幾點可收件,一率回答 19:00 後,貨運人員從此不用耗費時間等待靜香回覆,管理員可以直接回答
            • 封鎖特定資源
              • 由於太愛團購,導致個資外洩,所以靜香一天到晚收到推銷傳單,因此她跟管理員說,以後推銷傳單,直接幫我丟到垃圾桶,不用拿給我
            • 地理位置限制
              • 不說靜香了,就是有些影集台灣看不到,國外才看得到,因為設定了地理限制
        • reverse proxy - 代理 server 跟 client 接受請求,client 不會知道 Server 是誰

          • 好處
            • 負載平衡 - proxy 把流量平均分配到不同server
            • 快取功能 - proxy 把常用 request 的 res 存下來,相同請求來的時候,可以直接回覆
            • 隔離內部流量 - proxy
            • 紀錄細節 - proxy 可以紀錄 request 內容細節,供開發者除錯、等用途
            • 金絲雀部署 - proxy 可以按比例分配流量,到不同版本的server,可用雙版本測試
          • 說個故事好了
            靜香人美心也美,討厭流汗,所以打掃家裡這種事,都請清潔公司處理
            • 負載平衡
              • 靜香不定時呼叫清潔公司服務,為了讓靜香滿意,清潔公司會找到當下有空的阿姨上門服務
            • 快取功能
              • 由於跟靜香同一棟公寓還有其他客戶,因此清潔公司會以天為單位,盡量把清潔阿姨排在同一天服務附近的住戶
            • 隔離內部流量

            • 紀錄細節
              • 清潔公司會詳細記錄,每一個客戶的清潔需求,提供清潔公司內部了解大部分客戶需求
            • 金絲雀部署
              • 清潔公司會持續優化服務,為了瞭解客戶對於這個服務的喜好度,清潔公司會接受指定
      ]]> azsx96385 2021-09-20 16:11:50
      樂觀鎖 vs. 悲觀鎖 https://ithelp.ithome.com.tw/articles/10267450?sc=rss.iron https://ithelp.ithome.com.tw/articles/10267450?sc=rss.iron ref : https://medium.com/dean-lin/真正理解資料庫的悲觀鎖-vs-樂觀鎖-2cabb858726d

      爲什麼要了解這個呢?

      我相信工作一段時間,一定會遇到資料被重複異動、覆蓋的情況,初學者可能想說,那就多設where條件啊,但這不能根本的解決這個議題,不過設定where條件想法,已經有點接觸這個議題了.

      以前常聽同事,提到一些關鍵字 dead lock 、red lock 、row lock 都會一頭霧水,發生dead lock 事件的時候,也不能體會他們在緊張什麼,所幸現在知道的事情多了,就想說把這個觀念給記錄下來吧

      所以什麼是樂觀鎖 vs 悲觀鎖 ?

      樂觀鎖、悲觀鎖是種概念,主要指的是針對DB table 欄位異動的限制,解決資料出現不符合預期的異動

      • 樂觀鎖
        • 允許多個 sql command 同時操作資料
        • 優點
          • 可以快速的完成異動
        • 缺點
          • 造成資料出現不合預期的結果
      • 悲觀鎖
        • 一次只能一個 sql command 操作資料,其他 command 無法在資料正在被操作時異動,只能排隊等
        • 優點
          • 可以確保資料正確性
        • 缺點
          • 因為需要排隊異動,因此處理時間拉長

      我對樂觀鎖、悲觀鎖的理解是,樂觀鎖就像是信任制,會給你條件讓你遵守,確保結果,悲觀鎖就是覺得人性本惡,所以必須用強制規定,確保結果

      舉個爸爸零錢箱例子

      家裡爸爸準備了共用零錢箱給家裡兩個小孩,裡頭固定有五百元.錢可以取用,但是當天晚上必須把錢補回500

      如果爸爸採用樂觀鎖,制定規則

      爸爸相信大家都會遵守規則,所以兩位可以隨時取用零錢,但是必須在當天晚上把拿走的補齊,讓零錢箱固定有500

      如果爸爸採用悲觀鎖,制定規則

      爸爸才不相信大家會乖乖把錢補進去,因此每個人取用零錢,必須寫下誰、在幾點幾分、拿走多少,反之亦然,一樣當天晚上零錢桶需有500

      小結

      用樂觀鎖,取用零錢的人可以自由取用、但相對的要做到軟性的要求,就是把拿走的補齊,此情況雖然方便快速,但肯定會有人性的忘記,導致結果不符合預期(晚上金額500)

      用悲觀鎖,取用零錢的人,取用、歸還時,都需要強制完成紀錄的動作,否則不能達成目的,這相較樂觀鎖,是用強制力規範,確保結果符合預期,但相對的需要紀錄支出,本身就會耗費時間

      然後呢?

      其實在開發過程中,沒有什麼絕對,所以我們首先需要知道有這個選擇,然後根據當下的情境,選擇最適合的方案組合

      樂觀鎖的情境,比較適用修改量不大、資料若真的有非預期異動,其損害也還能承受的情況

      悲觀鎖的情境,比較適用有大量的修改量、資料不容許些微失誤的情況(金額交易)

      ]]> azsx96385 2021-09-19 09:35:39
      Open API 概念 https://ithelp.ithome.com.tw/articles/10266624?sc=rss.iron https://ithelp.ithome.com.tw/articles/10266624?sc=rss.iron
    1. 為什麼想寫這個?

      一樣是在某次開會聽到這個名詞,腦中開始想說這跟api有什麼不一樣,為什麼要特別加上 open 呢?,那個 swagger 有什麼不一樣?

    2. 為什麼想寫這個?

      一樣是在某次開會聽到這個名詞,腦中開始想說這跟api有什麼不一樣,為什麼要特別加上 open 呢?,那個 swagger 有什麼不一樣?

      ref: http://www.openbanking.com.tw/originhttps://medium.com/alexchanglife/fintech觀點-用生活中的場景-來理解微服務-microservice-與open-api-f60a9a49b217

    3. 所以什麼是 OPEN API ?

      • 我的理解

        open api 比較特定指的是開放給外部第三方使用的api,讓第三方使用其內部資料,實現特定目的(ex 整合個人財務狀況)

      • 為什麼要這麼做?

        • 對於開放方來說

          其實開放本身不一定對自身有特定好處,所以通常有個明確目的,如主管機關要求、第三方合作等(常見有 開放天氣資訊、路況資訊等)

        • 對第三方來說

          通常是使用既有資料,進一步進行處理加工(視覺化)等,用以符合特定用戶需求(ex.記帳軟體-整合用戶銀行的情況 )

    4. 然後呢?

      我覺得看待這個議題時,可以從open api 精神、帶來的效益來理解爲什麼要做這件事,同時可以切換身為開發者、身為使用者、身為串接者,怎麼去包裝資料、取用資料、享受資料帶來的方便,可以幫助後續看待相關議題時,有更深刻的了解

    5. ]]> azsx96385 2021-09-18 08:18:45
      DB 常見dataType 介紹 https://ithelp.ithome.com.tw/articles/10265833?sc=rss.iron https://ithelp.ithome.com.tw/articles/10265833?sc=rss.iron
    6. 為什麼要寫這篇?

      專案開發過程中,常會需要設計schema,建立新的table,由於我是自學轉職的,所以其實對於什麼型別的特性與佔用空間沒什麼概念,以前只是覺得...]]>

    7. 為什麼要寫這篇?

      專案開發過程中,常會需要設計schema,建立新的table,由於我是自學轉職的,所以其實對於什麼型別的特性與佔用空間沒什麼概念,以前只是覺得型別不就是 字串、數字、日期嗎,但隨著經驗累積,發現原來在這之下還有許多更細緻的型別,適用在不同情況,還有更進階的設計,而給與column適當的型別,對於正確性、效能也是有影響的,我們總會希望產品能夠好好的,在不同情況下都能運作正常,因此在前期設計的時候就滿重要的,以下是整理目前我常用且對於這些型別的理解,或許沒有到太深,但我秉持著夠用就好,能夠判別就好,如果後續有情境需要了解更深再了解囉~(ps: 以下是 postgresql 的例子)

    8. 有什麼重點呢?

      • Bit vs Byte ?

        Bit(位元)、Byte(位元組),1 Byte = 8 Bits

        Bit 為電腦運算的基礎,常用來作資料傳輸的單位,二進制(ex. 資料傳輸 用 bps = bit per second)

        Byte電腦檔案大小的基本計算單位(ex. 檔案大小 Byte)

        1byte = 8 Bits = 0~255的數字(二進位) = 一個英文字 = 一個符號 (2byte = 1中文字)

        ref: https://ytliu0.pixnet.net/blog/post/229587764-bit、byte是什麼意思?bit、byte有什麼不同?

      • BigSerial 是什麼?

        結尾為 Serial 皆不是正式型別,而只是方便建立唯一識別的值的語法,該特性是在插入資料時,資料庫會自動對單一筆資料給於順序編號

      • int2 vs. int4(int) vs int8

        後方的數字表示是儲存時佔用的 bytes(ex. int2 = 2bytes 數字範圍 1 to 32767 ),要是儲存資料超過數字範圍,就會出錯.
        目前設計時候會根據實際可能使用到的數字範圍,作為選擇依據
        ref: https://docs.postgresql.tw/the-sql-language/data-types/numeric-types

      • json vs jasonb

        資料欄位還可以是 json ,我第一次知道時真是驚為天人,這個欄位適合儲存重要性低、可能常變動的資料,重要性不足道可以給他一個正式欄位(ex. 說明備註)

        兩者差異?
        jsonb 把資料存進DB時,會多把輸入資料轉換成為二進位格式儲存,因此儲存資料時速度稍慢,但後續取用則不需解析,且增加索引處理功能

        json 資料型別儲存與輸入字串完全相同的內容,處理函數必須在每次執行時重新解析;jsonb 資料型別則以分解後的二進位格式儲存,由於增加了轉換成本,因此資料輸入的速度稍慢,但由於後續不需要解析,因此處理速度明顯加快。jsonb 還支援索引處理,這是一個很大的優勢。

    9. 所以呢?

      • 認識型別、與其佔用的空間,可以幫助自己在設計規劃的時候,單筆資料的重量有基本的觀念,我認為這是後續效能優化的基本功
      • 型別形形色色,雖不需要了解到全部,但可以在設計時,多思考這個欄位後續的發展、容錯率等等,藉以選擇最適合的配置,降低出錯,提升效率
    10. ]]> azsx96385 2021-09-17 09:08:23
      LibShare-typeOrmGenerator https://ithelp.ithome.com.tw/articles/10264931?sc=rss.iron https://ithelp.ithome.com.tw/articles/10264931?sc=rss.iron
    11. 為什麼要寫這篇呢?

      新任務開發總會遇到一些麻煩的前置動作(ex. 設計DB schema 、建立table 、準備ORM 使用的entity..),這些動作雖然...]]>

    12. 為什麼要寫這篇呢?

      新任務開發總會遇到一些麻煩的前置動作(ex. 設計DB schema 、建立table 、準備ORM 使用的entity..),這些動作雖然繁瑣,不過卻要小心仔細,否則後續錯誤就會手牽手一個個跑出來,到時候反覆修改耗費時間與心力,更讓人覺得阿雜,以前覺得有就好了拉~,現在開始注重精準這件事,這就是成長嗎?XD

      今年要紀錄的套件是 typeorm-model-generator ,是他讓我可以不用在型別字裡行間掙扎、是他讓我獲得開發中悠閒喝一杯咖啡的時間、說讓我延緩老化都不為過,他的主要的功能是,直接幫你從 已經開好的DB 轉成為 專案中使用的Entity,而且關聯關係也幫你照顧到囉~

      ref: https://www.npmjs.com/package/typeorm-model-generator

    13. 有什麼重點呢?

      • 什麼是ORM?

        ORM (Object-Relational Mapping),我的理解是 將資料庫映射為物件,讓開發者可以直接透過操作物件的方式,存取資料,且通常ORM都會支援大部分資料庫操作(ex. mysql 、postgresql),這降低專案與資料庫的耦合,不會因為更換資料庫,而需要資料提取邏輯.

        如果沒有使用ORM,那麼不僅要撰寫 raw sql ,且在後續更換資料庫時,需要花時間去調整對應心資料庫的sql,但其實使用 ORM 相對的,你需要依賴ORM幫你轉的sql語法,可能比較無法做效能的調整、複雜的sql,但大部分ORM 還是有提供讓你直接下sql的選擇的
        ref:
        ORM介紹及ORM優點、缺點 http://blog.twbryce.com/what-is-orm/

      • 簡單說說這個套件吧

        現況這個套件狀態為維護、但不開發新功能,每週下載量(5000~6000),根據作者說法是,目前typeOrm設計還是有些問題,導致資料缺失的情況,且重大issue似乎都沒有處理,也遲遲等不到最新的更新,因此作者認為目前開發新功能已非他的最高優先.
        所以我會把這件事放心裡、並注意是否有相關資料缺失的情況,但至少在現有的專案下,這依舊是一個好用的工具呢

    14. 所以咧

      • 趁這這次機會了解了ORM觀念,以前看不懂,這次在了解就大概知道在做啥了
      • 開發過程中,可以花一些時間找幫助開發的小工具(省時、省心力、提高正確性),讓自己有愉快的開發生活
      • 使用套件前,可以先注意套件目前的年紀、短期更新頻繁度、issue內容、下載數量等,幫助自己評估套件的支援性、穩定性等
    15. ]]> azsx96385 2021-09-16 08:54:50

    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>