翻譯|其它|編輯:郝浩|2004-01-12 21:27:00.000|閱讀 1175 次
概述:
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
適用于:
Microsoft® ASP.NET
Microsoft Visual Studio® .NET
Microsoft .NET Framework
摘要:學(xué)習(xí)如何構(gòu)建利用 Microsoft Visual Studio .NET 設(shè)計(jì)時(shí)支持的控件,這種支持使控件能夠像 Microsoft ASP.NET 中的內(nèi)置控件一樣易于使用。
下載 DesignTimeSupportSample.msi。(請(qǐng)注意,在示例文件中,程序員的注釋使用的是英文,本文中將其譯為中文是為了便于讀者理解。)
本文是由《ASP.NET in a Nutshell》第二版(ISBN:0596001169)改編而成,其作者包括 G. Andrew Duthie 和 Matthew MacDonald,由 O'Reilly & Associates, Inc. 于 2003 年出版。
簡(jiǎn)介
設(shè)計(jì)時(shí)支持的類型
Blog 控件示例
添加設(shè)計(jì)時(shí)支持
小結(jié)
代碼列表
Microsoft® ASP.NET 為開發(fā)人員提供了一種適用于 Web 開發(fā)的、功能最為強(qiáng)大的新工具:服務(wù)器控件。服務(wù)器控件使開發(fā)人員能夠在短時(shí)間內(nèi)開發(fā)出響應(yīng)速度快而且功能強(qiáng)大的 Web 應(yīng)用程序,所需的時(shí)間與在典型的 ASP 中創(chuàng)建類似應(yīng)用程序的時(shí)間差不多。
ASP.NET 服務(wù)器控件之所以能夠提供生產(chǎn)效率,關(guān)鍵原因之一在于它為 Microsoft Visual Studio® .NET 開發(fā)環(huán)境中的服務(wù)器控件提供了豐富的設(shè)計(jì)時(shí)支持。開發(fā)人員可以將服務(wù)器控件從 Visual Studio .NET 工具箱拖放到頁(yè)面上,通過 Properties(屬性)窗口訪問它們的屬性,然后在 Visual Studio HTML 編輯器以及 ASP.NET 頁(yè)面的內(nèi)含代碼的類中利用 Microsoft IntelliSense® 語句完成功能。這些設(shè)計(jì)時(shí)功能為 Web 開發(fā)帶來了快速應(yīng)用程序開發(fā) (RAD) 工具,而這些工具已被 Microsoft Visual Basic® 開發(fā)人員使用了多年。
ASP.NET 還使開發(fā)人員能夠通過創(chuàng)建自定義服務(wù)器控件以封裝大量可重復(fù)使用的用戶界面特定的代碼(例如登錄或注冊(cè)表單),來進(jìn)一步提高生產(chǎn)效率。盡管開發(fā)人員已經(jīng)開始意識(shí)到開發(fā)自定義控件的重要性,但許多人可能還沒有意識(shí)到還能在控件中利用 Visual Studio 設(shè)計(jì)時(shí)支持的強(qiáng)大功能,使這些控件能夠像 ASP.NET 中的內(nèi)置控件那樣易于使用。本文將介紹 Microsoft .NET Framework 和 Visual Studio .NET 提供的設(shè)計(jì)時(shí)支持的類型,并向開發(fā)人員介紹如何構(gòu)建利用這種支持的控件。
針對(duì) Visual Studio .NET 中的服務(wù)器控件,有五種不同的設(shè)計(jì)時(shí)支持。它們是:
這些設(shè)計(jì)時(shí)支持類型是由幾個(gè)不同的機(jī)制提供的。內(nèi)含代碼的類中的 IntelliSense 由 IDE 啟用,IDE 為您的控件讀取元數(shù)據(jù)以確定控件所提供的屬性和方法及其類型和參數(shù)。要啟用內(nèi)含代碼的類中的 IntelliSense,只需對(duì)您的控件進(jìn)行編寫和編譯,然后將其程序集放到使用該控件的應(yīng)用程序的 bin 子目錄中。
Visual Studio .NET 編輯器設(shè)計(jì)視圖中的屬性瀏覽器支持通過以下兩個(gè)途徑提供:將該類型與某個(gè)屬性相關(guān)聯(lián)和/或?qū)⒃獢?shù)據(jù)特性與該屬性相關(guān)聯(lián)。將元數(shù)據(jù)特性(下文簡(jiǎn)稱為特性)添加到您的代碼中,用于標(biāo)識(shí)屬性的類別、提供屬性說明以及在需要時(shí)指定首選編輯器。有些類型的屬性(如 System.Drawing.Color)會(huì)自動(dòng)映射到 Visual Studio .NET 中的相應(yīng)編輯器中。
Visual Studio .NET 的 HTML 視圖中的 IntelliSense 和屬性瀏覽器支持通過使用一種 XSD 架構(gòu)進(jìn)行提供,該架構(gòu)用于描述與控件相關(guān)聯(lián)的類型,它使用稱為 Visual Studio 注釋的文本修飾指定控件的首選編輯器和其他首選項(xiàng)。
最后,您可以通過結(jié)合特性和帶有特定屬性的自定義位圖來支持從 Visual Studio .NET 工具箱拖放控件。
用于說明 Visual Studio .NET 中的設(shè)計(jì)時(shí)功能的控件稱作“Blog 控件”,如本文末尾的列表 1 所示。該控件提供利用 XML 作為存儲(chǔ)介質(zhì)的簡(jiǎn)單 Web 日志功能。Web 日志通常稱為 Blog,它實(shí)際上是一個(gè) Web 頁(yè)面,供人們?cè)谏厦鎻堎N有關(guān)日常生活、世態(tài)百象、時(shí)事政治或人們所關(guān)心的其他問題的定期觀察報(bào)告或評(píng)論。Blog 條目是通過 Web 瀏覽器添加的。
Blog 控件非常簡(jiǎn)單明了,它利用控件組合向?yàn)g覽器提供輸出。在組合控件中,CreateChildControls 方法(由 ASP.NET 運(yùn)行時(shí)自動(dòng)調(diào)用)會(huì)被重寫,利用此方法,我們可以創(chuàng)建構(gòu)成自定義控件 UI 的控件,并將它們添加到控件的“控件”集合中。此外,該控件還包含用于顯示和添加 Blog 以及當(dāng) XML Blog 存儲(chǔ)文件不存在時(shí)創(chuàng)建一個(gè)這樣的文件的邏輯。該控件的幾個(gè)公共屬性需要開發(fā)人員在設(shè)計(jì)時(shí)進(jìn)行設(shè)置,其中包括在添加新 Blog 時(shí)該控件將重定向到的頁(yè)面的 URL、與新 Blog 關(guān)聯(lián)的電子郵件地址、控件模式(顯示或添加)以及各 Blog 條目之間的分隔線的顏色。圖 1 所示為正在運(yùn)行的 Blog 控件。Add Blog(添加 Blog)超鏈接由 ASP.NET 超鏈接控件提供,獨(dú)立于 Blog 控件。BlogClient.aspx 的代碼如列表 2 所示。BlogClient.aspx 的 codebehind 類如列表 3 所示,它提供單擊 Add Blog(添加 Blog)鏈接時(shí)更改 Blog 模式的邏輯。
圖 1:運(yùn)行時(shí)的 Blog 控件
圖 2 所示為設(shè)計(jì)時(shí)基本 Blog 控件的外觀。請(qǐng)注意,雖然列出了屬性,但并未分類。
圖 2:設(shè)計(jì)時(shí)的 Blog 控件
雖然在 Web 窗體頁(yè)上使用 Blog 控件非常簡(jiǎn)單,但并不是很直觀。例如,如果沒有相關(guān)文檔,使用 Blog 控件的人就無法知道 Mode 屬性的有效值只能是 Display 或 Add。如果未將 Add 模式的相關(guān)信息明確地告訴使用該控件的開發(fā)人員,他們就很難自己發(fā)現(xiàn)并使用這種模式。
對(duì)于使用 Visual Studio .NET(或支持 IntelliSense 的其他 IDE)的開發(fā)人員而言,可以通過為控件添加設(shè)計(jì)時(shí)支持來解決這一問題。這可以通過綜合利用本文前面所介紹的方法來實(shí)現(xiàn)。在為自定義服務(wù)器控件提供設(shè)計(jì)時(shí)支持所面臨的挑戰(zhàn)中,部分原因來自于在自定義控件中全面支持設(shè)計(jì)時(shí)功能所需的方法的多樣性。最簡(jiǎn)單的、不需要任何附加編碼的是內(nèi)含代碼的類中的 IntelliSense 語句完成方法,如圖 3 所示,此方法適用于 BlogClient.aspx.vb。
圖 3:內(nèi)含代碼的類中的 IntelliSense
遺憾的是,語句完成功能的自動(dòng)支持并沒有擴(kuò)展到編輯 Web 窗體頁(yè)時(shí)的設(shè)計(jì)視圖或 HTML 視圖,而且 Visual Studio 也沒有提供不需要額外的控件工作就能在屬性瀏覽器中查看和編輯屬性的內(nèi)置支持。更復(fù)雜的是,要在 Web 窗體編輯器的屬性瀏覽器和設(shè)計(jì)視圖中支持 IntelliSense,需要采用一種方法,要在該編輯器的 HTML 視圖中支持 IntelliSense,則需要采用另一種方法。
要在設(shè)計(jì)視圖中支持屬性瀏覽,所需的方法是通過特性告訴 Visual Studio .NET 如何處理屬性。要在 HTML 視圖中支持語句完成和屬性瀏覽,需要生成一個(gè)自定義 XSD 架構(gòu)以描述控件中的類型。我們將在下文討論這兩種方法。
Visual Studio .NET 為使用拖放技術(shù)的動(dòng)態(tài)控件設(shè)計(jì)和修改提供了豐富的支持,同時(shí)還提供了屬性瀏覽器之類的工具以及相關(guān)的設(shè)計(jì)器(例如顏色選擇器)。對(duì)這些工具的支持是通過一系列特性提供的,您可以將這些特性添加到您的控件中。這些特性用于告訴 Visual Studio IDE 是否在屬性瀏覽器中顯示控件的屬性、屬性所屬的類型以及應(yīng)使用哪個(gè)設(shè)計(jì)器設(shè)置屬性的值。
對(duì)于將要提供設(shè)計(jì)時(shí)支持的控件版本,我們將制作一份控件文件 Blog.vb 的副本,并將其命名為 Blog_DT.vb,然后在副本文件上進(jìn)行修改。這樣可以生成該控件的設(shè)計(jì)時(shí)版本,并保留原始控件以便進(jìn)行比較。
要支持在屬性瀏覽器中編輯 AddRedirect 屬性,應(yīng)在屬性進(jìn)程之前添加以下特性,如以下代碼片段所示:
<Browsable(True), _ Category("行為"), _ Description("成功提交新的 Blog 條目后, " & _ "應(yīng)重定向到的 " & _ "頁(yè)面的 URL。"), _ Editor("System.Web.UI.Design.UrlEditor", _ GetType(UITypeEditor))> _ Public Property AddRedirect() As String '屬性進(jìn)程代碼 End Property
這些特性聲明允許在屬性瀏覽器中顯示屬性、為屬性設(shè)置所需的類別(當(dāng)屬性按類別排序時(shí))、提供屬性說明并告訴 Visual Studio .NET 使用 UrlEditor 類編輯屬性的值,如圖 4 所示。
圖 4:設(shè)計(jì)視圖中的屬性支持
此處所述的特性語法適用于 Visual Basic .NET。在 Visual Basic .NET 中,特性通過以下語法進(jìn)行聲明:
<AttributeName(AttributeParams)>
在 C# 中,特性采用如下形式:
[AttributeName(AttributeParams)]
Visual Basic .NET 要求特性聲明與其修改的成員位于同一行中,因此通常最好在特性后面跟一個(gè) Visual Basic 行接續(xù)字符以提高可讀性:
<AttributeName(AttributeParams)> _ Public Membername()
在 C# 和 Visual Basic 中,您可以在一對(duì) [ ] 或 <> 括號(hào)中聲明多個(gè)特性,特性之間用逗號(hào)分隔。而在 Visual Basic .NET 中,如果它們出現(xiàn)在不同的行中,則必須使用 Visual Basic 行接續(xù)符銜接特性,使其位于同一個(gè)語句中。
除了設(shè)置屬性級(jí)別的特性外,還可設(shè)置某些類和程序集級(jí)別的特性。例如,您可以使用程序集級(jí)別的特性 TagPrefix 來指定標(biāo)記前綴,供程序集中包含的任何控件使用。之后,當(dāng)您從 Visual Studio 工具箱中向某個(gè) Web 窗體頁(yè)上添加該控件的實(shí)例時(shí),Visual Studio .NET 將自動(dòng)插入這個(gè)標(biāo)記前綴。以下代碼片段顯示了 TagPrefix 特性的語法。該特性應(yīng)放置在定義該控件的類模塊內(nèi),但應(yīng)在類和命名空間聲明之外(請(qǐng)注意,在 Visual Basic .NET 項(xiàng)目中,命名空間是在項(xiàng)目級(jí)別定義的,因此您不用擔(dān)心如何將程序集特性放置到命名空間聲明之外)。在以下特性中,TagPrefix 特性的第一個(gè)參數(shù)是控件的命名空間,第二個(gè)參數(shù)是您希望為標(biāo)記前綴使用的文本。
<Assembly: TagPrefix("BlogControl", "BlogControl")>
要將控件集成到 Visual Studio .NET 環(huán)境中,應(yīng)將 ToolBoxData 特性(該特性用于告訴 Visual Studio .NET 從工具箱中為控件插入的首選標(biāo)記名)添加到實(shí)現(xiàn)該控件的類中:
<ToolboxData("<{0}:Blog_DT runat=server></{0}:Blog_DT>")> _ Public Class Blog_DT Inherits Panel Implements INamingContainer '控件實(shí)現(xiàn) End Class
將控件從工具箱中插入到頁(yè)面上時(shí),由 TagPrefix 特性指定的標(biāo)記前綴將插入 {0}
占位符,而其他文本將按原樣插入。
您還可以為控件提供自己的自定義圖標(biāo),以顯示在工具箱中。為此,需要?jiǎng)?chuàng)建一個(gè) 16 x 16 像素大小的位圖(左下方的像素采用透明色),其名稱與包含該控件的類相同(即 classname.bmp)。使用 Add Existing Item(添加現(xiàn)有項(xiàng))命令將該位圖添加到項(xiàng)目中,然后使用屬性瀏覽器將其 Build Action(創(chuàng)建操作)設(shè)置為 Embedded Resource(內(nèi)置資源),如圖 5 所示。
圖 5:設(shè)置 Build Action(創(chuàng)建操作)
編譯完成后,該控件將支持從工具箱中將控件添加到某個(gè)頁(yè)面中時(shí)為 Blog 控件自動(dòng)插入 @Register 指令、標(biāo)記前綴和標(biāo)記名,并在工具箱中顯示自定義圖標(biāo),如圖 6 所示。要將控件添加到 Visual Studio .NET 工具箱中,應(yīng)完成以下簡(jiǎn)單步驟:
圖 6:工具箱中的自定義控件
將控件添加到工具箱中后,可以通過雙擊該控件或?qū)⑵鋸墓ぞ呦渲型戏诺?Web 窗體頁(yè)上,將其添加到 Web 窗體頁(yè)中。無論何種情況,Visual Studio .NET 都會(huì)自動(dòng)插入正確的 @Register 指令(包括基于程序集級(jí)別的特性設(shè)置 TagPrefix),還將使用 ToolBoxData 屬性中指定的標(biāo)記名為該控件生成一組標(biāo)記。
正如前文所述,Blog 控件在 Web 窗體編輯器的設(shè)計(jì)視圖中沒有任何可視界面。這使得選擇頁(yè)面上的控件很困難,更難以理解控件在運(yùn)行時(shí)的外觀。為了解決這個(gè)問題,我們可以添加設(shè)計(jì)器支持,使設(shè)計(jì)時(shí)的 HTML 在外觀上接近于運(yùn)行時(shí)的 Blog 控件。請(qǐng)注意,您還可以生成可以完整再現(xiàn)控件運(yùn)行時(shí)輸出的設(shè)計(jì)器,但此操作相當(dāng)復(fù)雜,而且超出了本文的討論范圍。
所有服務(wù)器控件設(shè)計(jì)器都是從類 System.Web.UI.Design.ControlDesigner 派生而來,該類提供了大量方法,您可以重寫這些方法為您的控件提供設(shè)計(jì)時(shí)渲染。以下代碼簡(jiǎn)單重寫了 GetDesignTimeHtml 方法,返回設(shè)計(jì)時(shí)顯示的簡(jiǎn)單 HTML。請(qǐng)注意,該示例顯示了 Blog 控件的整個(gè)設(shè)計(jì)器類,您可以簡(jiǎn)單地將其添加到現(xiàn)有的 Blog_DT.vb 類文件中。
Public Class BlogDesigner Inherits ControlDesigner Public Overrides Function GetDesignTimeHtml() As String Return "<h1>Blog</h1><hr/><hr/>" End Function End Class
要將該設(shè)計(jì)器綁定到 Blog_DT 類中,我們使用了 Designer 特性,如以下片段所示。請(qǐng)注意,此段代碼還添加了一個(gè)描述控件功能的 Description 特性。
<Description("簡(jiǎn)單 Blog 控件。支持顯示 " & _ "Web 日志/來自 XML 文件的新條目。"), _ Designer("BlogControl.BlogDesigner"), _ ToolboxData("<{0}:Blog_DT runat=server></{0}:Blog_DT>")> _ Public Class Blog_DT Inherits Panel Implements INamingContainer
如您所見,BlogDesigner 類非常簡(jiǎn)單,但它為控件在 Web 窗體頁(yè)上的設(shè)計(jì)時(shí)外觀添加了大量?jī)?nèi)容,如圖 7 所示。
圖 7:添加設(shè)計(jì)時(shí)渲染
列表 4 顯示了 Blog 控件的代碼,它已經(jīng)使用特性進(jìn)行了更新,以啟用設(shè)計(jì)視圖和屬性瀏覽器中的控件設(shè)計(jì)時(shí)支持。請(qǐng)注意,該示例添加了多條 using 指令,以導(dǎo)入支持我們使用的特性和設(shè)計(jì)器類所需要的命名空間。這個(gè)新列表還添加了一個(gè)用于 Mode 屬性值的枚舉。
盡管前文所述的特性幫助我們?yōu)?Blog 控件提供了設(shè)計(jì)時(shí)支持,但這里遺漏了一個(gè)重要的問題:在 Web 窗體編輯器的 HTML 視圖中添加標(biāo)記和特性的 IntelliSense 支持。對(duì)于那些認(rèn)為在 HTML 環(huán)境中工作比在“所見即所得”風(fēng)格的環(huán)境中工作更舒適的開發(fā)人員來說,這是一個(gè)極大的疏忽。
因?yàn)?Web 窗體編輯器的 HTML 視圖使用 XSD 架構(gòu)決定在 Web 窗體頁(yè)上提供哪些元素和特性,所以為了糾正這一問題,我們需要提供一個(gè)描述 Blog 控件及其所支持的特性的 XSD 架構(gòu)。也可以在該架構(gòu)中添加注釋,告訴 Visual Studio .NET 各種元素的有關(guān)信息以及我們所希望的元素行為。
列表 5 包含 Blog 控件特定的 XSD 架構(gòu)的部分內(nèi)容。實(shí)際的架構(gòu)文件(可從本文的示例代碼中獲得)還包含面板控件(Blog_DT 控件就是由它派生的)的類型定義以及其他必需的特性和類型定義。這些定義是從為內(nèi)置 ASP.NET 服務(wù)器控件創(chuàng)建的 asp.xsd 架構(gòu)文件中復(fù)制的。
請(qǐng)注意,任何時(shí)候都不應(yīng)直接修改 asp.xsd 架構(gòu)文件,而只應(yīng)將必需的類型和特性定義復(fù)制到您的自定義架構(gòu)文件中。盡管這看起來是多余的,但如果直接編輯 asp.xsd,以后安裝 .NET Framework 或服務(wù)包時(shí)該文件將被覆蓋,您的自定義輸入項(xiàng)將因此而丟失。
在列表 5 中,請(qǐng)注意根架構(gòu)元素上的 targetNamespace 和 xmlns 特性,這兩個(gè)特性用于為控件的架構(gòu)定義 XML 命名空間。targetNamespace 和 xmlns 特性的值還將用于 Web 窗體頁(yè)中的特性,以“綁定”該架構(gòu)。<xsd:element> 標(biāo)記定義根 Blog_DT 元素。<xsd:complexType> 標(biāo)記定義 Blog_DT 元素的特性,包括 <xsd:attributeGroup> 標(biāo)記引用的 Web 控件特性。最后,<xsd:simpleType> 標(biāo)記定義 BlogMode 類型的枚舉,該類型被用作 Blog_DT 元素的一個(gè)特性。
請(qǐng)注意,列表 5 使用 vs:builder 注釋來告訴 Visual Studio .NET 對(duì) AddRedirect 特性使用 URL 生成器,而對(duì) SeparatorColor 特性使用顏色生成器。vs:builder 注釋是可用于修改架構(gòu)的注釋之一。表 1 列出了最常用的注釋。
表 1:常用的 Visual Studio .NET 注釋
注釋 | 用途 | 有效值 |
---|---|---|
vs:absolutepositioning | 在根 <schema> 元素上使用,用于確定 Visual Studio 是否可以插入用于定位的樣式特性。 | true 或 false |
vs:blockformatted | 表明是否可以在自動(dòng)格式化期間為元素添加前導(dǎo)空格。 | true 或 false |
vs:builder | 指定用于編輯相關(guān)屬性值的生成器。 | 顏色、樣式或 URL |
vs:deprecated | 允許將某個(gè)相關(guān)屬性標(biāo)記為“已否決”,以防止其在屬性瀏覽器和語句完成中出現(xiàn)。 | true 或 false |
vs:empty | 在元素級(jí)別使用,用于指示 Visual Studio .NET 應(yīng)對(duì)相關(guān)標(biāo)記(無結(jié)束標(biāo)記)使用一個(gè)標(biāo)記語法。 | true 或 false |
vs:friendlyname | 在根級(jí)別使用,用于為架構(gòu)提供顯示名。 | |
vs:iscasesensitive | 在根級(jí)別使用,說明 Visual Studio .NET 是否以區(qū)分大小寫的方式處理相關(guān)標(biāo)記。 | true 或 false |
vs:ishtmlschema | 在根級(jí)別使用,說明架構(gòu)是否是一個(gè) HTML 文檔架構(gòu)。 | true 或 false |
vs:nonbrowseable | 在特性級(jí)別使用,說明該特性不應(yīng)出現(xiàn)在語句完成中。 | true 或 false |
vs:readonly | 在特性級(jí)別使用,說明不能在屬性窗口中修改該特性。 | true 或 false |
vs:requireattributequotes | 在根級(jí)別使用,說明特性值必須用引號(hào)括起。 | true 或 false |
創(chuàng)建自己的 XSD 架構(gòu)后,可以將其與 asp.xsd 文件保存到同一位置(在 Visual Studio .NET 2003 中,默認(rèn)為 C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml\)。
要允許 Visual Studio .NET 讀取您的自定義架構(gòu),需要將一個(gè) xmlns 特性添加到要使用該架構(gòu)的頁(yè)面的 <body> 標(biāo)記中,如以下代碼片段所示:
<body xmlns:BlogControl="urn://www.aspnetian.com/schemas">
請(qǐng)注意,此段代碼使用具有 xmlns 特性的 BlogControl 前綴來說明該架構(gòu)適用于帶有 BlogControl 標(biāo)記前綴的控件,這個(gè)可以再次調(diào)用的前綴是使用 TagPrefix 特性進(jìn)行設(shè)置的(有關(guān)該特性的說明,請(qǐng)參見上文中的“元數(shù)據(jù)特性”一節(jié))。xmlns 特性的值應(yīng)與架構(gòu)根元素中定義的 targetNamespace 特性的值相同。
通過 xmlns 特性綁定架構(gòu)之后,即可鍵入一個(gè)開放的“<”字符,并使 Blog 控件顯示為語句完成的一個(gè)選項(xiàng),如圖 8 所示。此時(shí),還應(yīng)獲取已定義屬性的語句完成,包括 Mode 屬性的有效值,以及由 XSD 文件中的注釋指定的生成器。
圖 8:HTML 視圖中的語句完成
本文介紹了 Visual Studio .NET 中適用于 ASP.NET 服務(wù)器控件的設(shè)計(jì)時(shí)支持,還說明了開發(fā)人員如何在自己的自定義控件中利用這一支持功能。雖然在控件中添加設(shè)計(jì)時(shí)支持相對(duì)簡(jiǎn)明,但要充分利用這些功能卻需要掌握多種不同的技巧。特別欠缺的知識(shí)領(lǐng)域就是如何將自定義 XSD 架構(gòu)綁定到頁(yè)面上。在撰寫本文時(shí),還不具備將頁(yè)面與控件 XSD 架構(gòu)連接起來所需的 xmlns 特性的內(nèi)置支持。所以,還需要手動(dòng)添加這個(gè)特性。希望以后的 Visual Studio .NET 版本能夠自動(dòng)完成這一過程。
本文中的示例代碼包含一個(gè)適用于 Blog 控件基礎(chǔ)版和設(shè)計(jì)時(shí)支持版的 Visual Studio .NET 項(xiàng)目,還包含一個(gè)說明如何使用每個(gè)控件的客戶端項(xiàng)目。要運(yùn)行 BlogControlClient 項(xiàng)目,您需要在 IIS 中創(chuàng)建一個(gè)虛擬目錄 BlogControlClient,然后將其映射到硬盤驅(qū)動(dòng)器上用于保存 BlogControlClient 項(xiàng)目文件夾的位置。
真誠(chéng)地感謝 Microsoft Visual Studio .NET 團(tuán)隊(duì)的 Rob Caron,他在我編寫自定義 XSD 架構(gòu)的創(chuàng)建和綁定過程中給予了極大的幫助。
作者介紹
G. Andrew Duthie 是 Graymad Enterprises, Inc.(英文)的創(chuàng)始人和負(fù)責(zé)人,該公司提供 Microsoft Web 開發(fā)技術(shù)的培訓(xùn)和咨詢服務(wù)。自從 Active Server Pages 問世以來,Andrew 一直從事多層 Web 應(yīng)用程序的開發(fā)工作。他編寫了大量有關(guān) ASP.NET 的著作,其中包括:《Microsoft ASP.NET Step By Step》、《Microsoft ASP.NET Programming with Microsoft Visual Basic》和《ASP.NET in a Nutshell》。Andrew 經(jīng)常在一些重大活動(dòng)中發(fā)表演講,這些活動(dòng)包括“Software Development”、“Dev-Connections family of conferences”、“Microsoft Developer Days”以及“VSLive!”。他還作為 International .NET Association (INETA)(英文)Speaker's Bureau 成員在 .NET 用戶組上發(fā)表了演講。您可以從其公司的 Web 站點(diǎn) Graymad Enterprises, Inc.(英文)上了解到有關(guān) Andrew 的更多信息。
本文是根據(jù)《ASP.NET in a Nutshell》第二版(ISBN:0596001169)改編而成,其作者包括 G. Andrew Duthie 和 Matthew MacDonald,由 O'Reilly & Associates, Inc. 于 2003 年出版。
'supports Color structure Imports System.Drawing '支持 StreamWriter 類型 Imports System.IO Imports System.Web.UI '支持使用 HTML 控件 Imports System.Web.UI.HtmlControls '支持使用 Web 控件 Imports System.Web.UI.WebControls Public Class Blog Inherits Panel Implements INamingContainer Protected BlogDS As DataSet Protected TitleTB As TextBox Protected BlogText As TextBox Private _addRedirect As String Private _email As String Private _mode As String Private _separatorColor As Color = Color.Black Public Property AddRedirect() As String Get Return Me._addRedirect End Get Set(ByVal Value As String) Me._addRedirect = Value End Set End Property Public Property Email() As String Get Return Me._email End Get Set(ByVal Value As String) Me._email = Value End Set End Property Public Property Mode() As String Get Return Me._mode End Get Set(ByVal Value As String) Me._mode = Value End Set End Property Public Property SeparatorColor() As Color Get Return Me._separatorColor End Get Set(ByVal Value As Color) Me._separatorColor = Value End Set End Property Protected Overrides Sub OnInit(ByVal e As EventArgs) LoadData() MyBase.OnInit(e) End Sub Protected Overrides Sub CreateChildControls() If Not Me._mode = "Add" Then DisplayBlogs() Else NewBlog() End If End Sub Protected Sub LoadData() BlogDS = New DataSet() Try BlogDS.ReadXml(Page.Server.MapPath("Blog.xml")) Catch fnfEx As FileNotFoundException CreateBlankFile() LoadData() End Try End Sub Protected Sub DisplayBlogs() Dim BlogDate As DateTime Dim CurrentDate As DateTime = New DateTime() Dim BlogRows As DataRowCollection = _ BlogDS.Tables(0).Rows Dim BlogDR As DataRow For Each BlogDR In BlogRows Dim BDate As String = BlogDR("date").ToString() BlogDate = New DateTime _ (Convert.ToInt32(BDate.Substring(4, 4)), _ Convert.ToInt32(BDate.Substring(0, 2)), _ Convert.ToInt32(BDate.Substring(2, 2))) If Not CurrentDate = BlogDate Then Dim TempDate As Label = New Label() TempDate.Text = BlogDate.ToLongDateString() TempDate.Font.Size = FontUnit.Large TempDate.Font.Bold = True Me.Controls.Add(TempDate) Me.Controls.Add _ (New LiteralControl("<br/><br/>")) CurrentDate = BlogDate End If Dim Anchor As HtmlAnchor = New HtmlAnchor() Anchor.Name = "#" & BlogDR("anchorID").ToString() Me.Controls.Add(Anchor) Dim Title As Label = New Label() Title.Text = BlogDR("title").ToString() Title.Font.Size = FontUnit.Larger Title.Font.Bold = True Me.Controls.Add(Title) Me.Controls.Add(New LiteralControl("<p>")) Dim BlogText As LiteralControl = _ New LiteralControl("<div>" & _ BlogDR("text").ToString() & "</div>") Me.Controls.Add(BlogText) Me.Controls.Add(New LiteralControl("</p>")) Dim Email As HyperLink = New HyperLink() Email.NavigateUrl = "mailto:" & _ BlogDR("email").ToString() Email.Text = "E-mail me" Me.Controls.Add(Email) Me.Controls.Add(New LiteralControl(" | ")) Dim AnchorLink As HyperLink = New HyperLink() AnchorLink.NavigateUrl = _ Page.Request.Url.ToString() & "#" & _ BlogDR("anchorID").ToString() AnchorLink.Text = "Link" Me.Controls.Add(AnchorLink) Me.Controls.Add(New _ LiteralControl("<hr color='" & _ ColorTranslator.ToHtml(_separatorColor) & _ "' width='100%'/><br/>")) Next End Sub Protected Sub NewBlog() Dim Title As Label = New Label() Title.Text = "Create New Blog" Title.Font.Size = FontUnit.Larger Title.Font.Bold = True Me.Controls.Add(Title) Me.Controls.Add(New LiteralControl("<br/><br/>")) Dim TitleLabel As Label = New Label() TitleLabel.Text = "Title: " TitleLabel.Font.Bold = True Me.Controls.Add(TitleLabel) TitleTB = New TextBox() Me.Controls.Add(TitleTB) Me.Controls.Add(New LiteralControl("<br/>")) Dim BlogTextLabel As Label = New Label() BlogTextLabel.Text = "Text: " BlogTextLabel.Font.Bold = True Me.Controls.Add(BlogTextLabel) BlogText = New TextBox() BlogText.TextMode = TextBoxMode.MultiLine BlogText.Rows = 10 BlogText.Columns = 40 Me.Controls.Add(BlogText) Me.Controls.Add(New LiteralControl("<br/>")) Dim Submit As Button = New Button() Submit.Text = "Submit" AddHandler Submit.Click, AddressOf Me.Submit_Click Me.Controls.Add(Submit) End Sub Protected Sub Submit_Click(ByVal Sender As Object, _ ByVal e As EventArgs) EnsureChildControls() AddBlog() End Sub Protected Sub AddBlog() Dim NewBlogDR As DataRow NewBlogDR = BlogDS.Tables(0).NewRow() NewBlogDR("date") = FormatDate(DateTime.Today) NewBlogDR("title") = TitleTB.Text NewBlogDR("text") = BlogText.Text NewBlogDR("anchorID") = Guid.NewGuid().ToString() NewBlogDR("email") = _email BlogDS.Tables(0).Rows.InsertAt(NewBlogDR, 0) BlogDS.WriteXml(Page.Server.MapPath("Blog.xml")) Page.Response.Redirect(_addRedirect) End Sub Protected Function FormatDate(ByVal dt As DateTime) _ As String Dim retString As String retString = String.Format("{0:D2}", dt.Month) retString &= String.Format("{0:D2}", dt.Day) retString &= String.Format("{0:D2}", dt.Year) Return retString End Function Public Sub CreateBlankFile() Dim NewXml As StreamWriter = _ File.CreateText(Page.Server.MapPath("Blog.xml")) NewXml.WriteLine("<blogs>") NewXml.WriteLine _ (" <!-- blog field describes a single blog -->") NewXml.WriteLine(" <blog>") NewXml.WriteLine(" <!-- date field contains" & _ " the creation date of the blog -->") NewXml.WriteLine(" <date>" & _ FormatDate(DateTime.Today) & "</date>") NewXml.WriteLine _ (" <title>Temporary Blog</title>") NewXml.WriteLine(" <!-- text field " & _ "should contain the blog text, including any " & _ "desired HTML tags -->") NewXml.WriteLine(" <text>This entry " & _ "indicates that the file blog.xml was not " & _ "found.A default version of this file has " & _ "been created for you.You can modify the " & _ "fields in this file as desired.If you set " & _ "the Blog control to add mode (add the " & _ "attribute mode='add' to the control's " & _ "declaration), the control will " & _ "automatically populate the XML file when " & _ "you submit the form.</text>") NewXml.WriteLine(" <!-- anchorID field " & _ "will be autopopulated by the control -->") NewXml.WriteLine(" <anchorID></anchorID>") NewXml.WriteLine(" <!-- email field should" & _ " contain the email address for feedback -->") NewXml.WriteLine(" <email>change this to a " & _ "valid email address</email>") NewXml.WriteLine(" </blog>") NewXml.WriteLine("</blogs>") NewXml.Close() End Sub End Class
<%@ Register TagPrefix="cc1" Namespace="BlogControl" Assembly="BlogControl" %> <%@ Page Language="vb" AutoEventWireup="false" Codebehind="BlogClient.aspx.vb" Inherits="BlogControlClient.WebForm1"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Blog Client</title> </head> <body> <form id=Form1 method=post runat="server"> <p><asp:hyperlink id=Link1 navigateurl="BlogClient.aspx?mode=add" runat="server">Add Blog</asp:hyperlink></p> <cc1:blog id=Blog1 Email="andrew@graymad.com" AddRedirect="BlogClient.aspx" SeparatorColor="LawnGreen" runat="server"></cc1:blog> <p><asp:hyperlink id=Link2 navigateurl="BlogClient.aspx?mode=add" runat="server">Add Blog</asp:hyperlink></p> </form> </body> </html>
Imports BlogControl Public Class WebForm1 Inherits System.Web.UI.Page Protected WithEvents Link1 As _ System.Web.UI.WebControls.HyperLink Protected WithEvents Link2 As _ System.Web.UI.WebControls.HyperLink Protected WithEvents Blog1 As BlogControl.Blog Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If Request.QueryString("mode") = "add" Then Blog1.Mode = "Add" Link1.Visible = False Link2.Visible = False Else Blog1.Mode = "Display" Link1.Visible = True Link2.Visible = True End If End Sub End Class
'支持設(shè)計(jì)時(shí)特性 Imports System.ComponentModel '支持顏色結(jié)構(gòu) Imports System.Drawing '支持 UITypeEditor 類型 Imports System.Drawing.Design '支持 StreamWriter 類型 Imports System.IO Imports System.Web.UI '支持 ControlDesigner 類型 ' 請(qǐng)注意,必須添加程序集 ' System.Design 的引用,才能導(dǎo)入此命名空間 Imports System.Web.UI.Design '支持使用 HTML 控件 Imports System.Web.UI.HtmlControls '支持使用 Web 控件 Imports System.Web.UI.WebControls <Assembly: TagPrefix("BlogControl", "BlogControl")> Public Enum BlogMode Add Display End Enum <Description("Simple Blog control.Supports display " & _ "of Web log / news items from an XML file."), _ Designer("BlogControl.BlogDesigner"), _ ToolboxData("<{0}:Blog_DT runat=server></{0}:Blog_DT>")> _ Public Class Blog_DT Inherits Panel Implements INamingContainer Protected BlogDS As DataSet Protected TitleTB As TextBox Protected BlogText As TextBox Private _addRedirect As String Private _email As String Private _mode As BlogMode Private _separatorColor As Color = Color.Black <Browsable(True), _ Category("Behavior"), _ Description("URL to which the page should redirect after successful submission of a new Blog entry."), _ Editor("System.Web.UI.Design.UrlEditor", _ GetType(UITypeEditor))> _ Public Property AddRedirect() As String Get Return Me._addRedirect End Get Set(ByVal Value As String) Me._addRedirect = Value End Set End Property <Browsable(True), _ Category("Behavior"), _ Description("Email address the control will use for listing in new Blog entries.")> _ Public Property Email() As String Get Return Me._email End Get Set(ByVal Value As String) Me._email = Value End Set End Property <Browsable(True), _ Category("Behavior"), _ Description("Controls whether existing Blogs are displayed, or fields for creating a new Blog entry.")> _ Public Property Mode() As BlogMode Get Return Me._mode End Get Set(ByVal Value As BlogMode) Me._mode = Value End Set End Property <Browsable(True), _ Category("Appearance"), _ Description("Controls the color of the line that separates Blog entries when in display mode.")> _ Public Property SeparatorColor() As Color Get Return Me._separatorColor End Get Set(ByVal Value As Color) Me._separatorColor = Value End Set End Property Protected Overrides Sub OnInit(ByVal e As EventArgs) LoadData() MyBase.OnInit(e) End Sub Protected Overrides Sub CreateChildControls() If Not Me._mode = BlogMode.Add Then DisplayBlogs() Else NewBlog() End If End Sub Protected Sub LoadData() BlogDS = New DataSet() Try BlogDS.ReadXml(Page.Server.MapPath("Blog.xml")) Catch fnfEx As FileNotFoundException CreateBlankFile() LoadData() End Try End Sub Protected Sub DisplayBlogs() Dim BlogDate As DateTime Dim CurrentDate As DateTime = New DateTime() Dim BlogRows As DataRowCollection = _ BlogDS.Tables(0).Rows Dim BlogDR As DataRow For Each BlogDR In BlogRows Dim BDate As String = BlogDR("date").ToString() BlogDate = New DateTime _ (Convert.ToInt32(BDate.Substring(4, 4)), _ Convert.ToInt32(BDate.Substring(0, 2)), _ Convert.ToInt32(BDate.Substring(2, 2))) If Not CurrentDate = BlogDate Then Dim TempDate As Label = New Label() TempDate.Text = BlogDate.ToLongDateString() TempDate.Font.Size = FontUnit.Large TempDate.Font.Bold = True Me.Controls.Add(TempDate) Me.Controls.Add _ (New LiteralControl("<br/><br/>")) CurrentDate = BlogDate End If Dim Anchor As HtmlAnchor = New HtmlAnchor() Anchor.Name = "#" + BlogDR("anchorID").ToString() Me.Controls.Add(Anchor) Dim Title As Label = New Label() Title.Text = BlogDR("title").ToString() Title.Font.Size = FontUnit.Larger Title.Font.Bold = True Me.Controls.Add(Title) Me.Controls.Add(New LiteralControl("<p>")) Dim BlogText As LiteralControl = _ New LiteralControl("<div>" & _ BlogDR("text").ToString() & "</div>") Me.Controls.Add(BlogText) Me.Controls.Add(New LiteralControl("</p>")) Dim Email As HyperLink = New HyperLink() Email.NavigateUrl = "mailto:" & _ BlogDR("email").ToString() Email.Text = "E-mail me" Me.Controls.Add(Email) Me.Controls.Add(New LiteralControl(" | ")) Dim AnchorLink As HyperLink = New HyperLink() AnchorLink.NavigateUrl = _ Page.Request.Url.ToString() & "#" & _ BlogDR("anchorID").ToString() AnchorLink.Text = "Link" Me.Controls.Add(AnchorLink) Me.Controls.Add _ (New LiteralControl("<hr color='" & _ ColorTranslator.ToHtml(_separatorColor) & _ "' width='100%'/><br/>")) Next End Sub Protected Sub NewBlog() Dim Title As Label = New Label() Title.Text = "Create New Blog" Title.Font.Size = FontUnit.Larger Title.Font.Bold = True Me.Controls.Add(Title) Me.Controls.Add(New LiteralControl("<br/><br/>")) Dim TitleLabel As Label = New Label() TitleLabel.Text = "Title: " TitleLabel.Font.Bold = True Me.Controls.Add(TitleLabel) TitleTB = New TextBox() Me.Controls.Add(TitleTB) Me.Controls.Add(New LiteralControl("<br/>")) Dim BlogTextLabel As Label = New Label() BlogTextLabel.Text = "Text: " BlogTextLabel.Font.Bold = True Me.Controls.Add(BlogTextLabel) BlogText = New TextBox() BlogText.TextMode = TextBoxMode.MultiLine BlogText.Rows = 10 BlogText.Columns = 40 Me.Controls.Add(BlogText) Me.Controls.Add(New LiteralControl("<br/>")) Dim Submit As Button = New Button() Submit.Text = "Submit" AddHandler Submit.Click, AddressOf Me.Submit_Click Me.Controls.Add(Submit) End Sub Protected Sub Submit_Click(ByVal Sender As Object, _ ByVal e As EventArgs) EnsureChildControls() AddBlog() End Sub Protected Sub AddBlog() Dim NewBlogDR As DataRow NewBlogDR = BlogDS.Tables(0).NewRow() NewBlogDR("date") = FormatDate(DateTime.Today) NewBlogDR("title") = TitleTB.Text NewBlogDR("text") = BlogText.Text NewBlogDR("anchorID") = Guid.NewGuid().ToString() NewBlogDR("email") = _email BlogDS.Tables(0).Rows.InsertAt(NewBlogDR, 0) BlogDS.WriteXml(Page.Server.MapPath("Blog.xml")) Page.Response.Redirect(_addRedirect) End Sub Protected Function FormatDate(ByVal dt As DateTime) As String Dim retString As String retString = String.Format("{0:D2}", dt.Month) retString &= String.Format("{0:D2}", dt.Day) retString &= String.Format("{0:D2}", dt.Year) Return retString End Function Public Sub CreateBlankFile() Dim NewXml As StreamWriter = _ File.CreateText(Page.Server.MapPath("Blog.xml")) NewXml.WriteLine("<blogs>") NewXml.WriteLine _ (" <!-- blog field describes a single blog -->") NewXml.WriteLine(" <blog>") NewXml.WriteLine(" <!-- date field contains" & _ " the creation date of the blog -->") NewXml.WriteLine(" <date>" & _ FormatDate(DateTime.Today) & "</date>") NewXml.WriteLine _ (" <title>Temporary Blog</title>") NewXml.WriteLine(" <!-- text field " & _ "should contain the blog text, including any " & _ "desired HTML tags -->") NewXml.WriteLine(" <text>This entry " & _ "indicates that the file blog.xml was not " & _ "found.A default version of this file has " & _ "been created for you.You can modify the " & _ "fields in this file as desired.If you set " & _ "the Blog control to add mode (add the " & _ "attribute mode='add' to the control's " & _ "declaration), the control will " & _ "automatically populate the XML file when " & _ "you submit the form.</text>") NewXml.WriteLine(" <!-- anchorID field " & _ "will be autopopulated by the control -->") NewXml.WriteLine(" <anchorID></anchorID>") NewXml.WriteLine(" <!-- email field should" & _ " contain the email address for feedback -->") NewXml.WriteLine(" <email>change this to a " & _ "valid email address</email>") NewXml.WriteLine(" </blog>") NewXml.WriteLine("</blogs>") NewXml.Close() End Sub End Class Public Class BlogDesigner Inherits ControlDesigner Public Overrides Function GetDesignTimeHtml() As String Return "<h1>Blog</h1><hr/><hr/>" End Function End Class
<?xml version="1.0" encoding="utf-8" ?> <xsd:schema targetNamespace="urn://www.aspnetian.com/schemas" elementFormDefault="qualified" xmlns="urn://www.aspnetian.com/schemas" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:vs="http://schemas.microsoft.com/Visual-Studio-Intellisense" vs:friendlyname="Blog Control Schema" vs:ishtmlschema="false" vs:iscasesensitive="false" vs:requireattributequotes="true" > <xsd:annotation> <xsd:documentation> Blog Control schema. </xsd:documentation> </xsd:annotation> <xsd:element name="Blog_DT" type="BlogDef" /> <!-- <aspnetian:Blog> --> <xsd:complexType name="BlogDef"> <!-- <aspnetian:Blog>-specific attributes --> <xsd:attribute name="AddRedirect" type="xsd:string" vs:builder="url"/> <xsd:attribute name="Email" type="xsd:string"/> <xsd:attribute name="Mode" type="BlogMode"/> <xsd:attribute name="SeparatorColor" type="xsd:string" vs:builder="color"/> <!-- <asp:Panel>-specific attributes --> <xsd:attribute name="BackImageUrl" type="xsd:anyURI" /> <xsd:attribute name="HorizontalAlign" type="HorizontalAlign" /> <xsd:attribute name="Wrap" type="xsd:boolean" /> <xsd:attribute name="Enabled" type="xsd:boolean" /> <xsd:attribute name="BorderWidth" type="ui4" /> <xsd:attribute name="BorderColor" type="xsd:string" vs:builder="color" /> <xsd:attribute name="BorderStyle" type="BorderStyle" /> <xsd:attributeGroup ref="WebControlAttributes" /> </xsd:complexType> <!-- DataTypes --> <xsd:simpleType name="BlogMode"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="Add" /> <xsd:enumeration value="Display" /> </xsd:restriction> </xsd:simpleType> </xsd:schema>
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn