伺服器控制項屬性範例
更新:2007 年 11 月
本範例示範如何建立名為 Book 的控制項,這個控制項會保存簡單屬性和含有子屬性的屬性。
簡單屬性是其型別為字串,或型別可方便對應至字串的屬性。簡單屬性 (Property) 會在控制項的開頭標記上當做屬性 (Attribute) 保存,而不需要您執行任何動作。String 型別的屬性和 .NET Framework 類別庫中的基本實值型別 (例如 Boolean、Int16、Int32 和 Enum),都是簡單屬性。您可以針對跨回傳 (Postback) 的狀態管理,加入程式碼以便在 ViewState 字典中存放簡單屬性。
當屬性型別也是一個類別同時本身具有屬性 (也就是子屬性) 的類別時,稱為複雜屬性。例如,WebControl 的 Font 屬性型別是 FontInfo 類別,而 FontInfo 屬性包含像是 Bold 和 Name 的屬性。而 Bold 和 Name 是 WebControl 之 Font 屬性的子屬性。ASP.NET Web 網頁架構會使用連字號連接的語法 (例如,Font-Bold="true") 在控制項的開頭標記上保存子屬性,但是在控制項的標記內 (例如,<font Bold="true">) 保存子屬性時,能夠更容易讀取子屬性。
若要讓視覺化設計工具 (Visual Designer) 將子屬性保存為控制項子系,必須將數個設計階段屬性 (Attribute) 套用至屬性 (Property) 與其型別;預設持續性 (Persistence) 是在控制項的標記上以連字號連接的屬性 (Attribute)。此外,具有子屬性的屬性也需要自訂狀態管理才能使用檢視狀態,如本主題稍後的「程式碼討論」章節所述。
範例中定義的 Book 控制項是可以在網頁中使用的控制項,以顯示目錄中的書籍詳細資訊。Book 控制項會定義下列屬性:
Author,具有子屬性的屬性,其型別是自訂型別 Author。Author 型別具有本身的屬性,例如 FirstName 和 LastName,這些是 Author 屬性的子屬性。
BookType,簡單屬性,其型別是自訂列舉型別 BookType。BookType 列舉型別具有如 Fiction 和 NonFiction 的值。
CurrencySymbol,內建 String 型別的簡單屬性。
Price,內建 Decimal 型別的簡單屬性。
Title,內建 String 型別的簡單屬性。
BookType、CurrencySymbol、Price 和 Title 屬性 (Property) 都是簡單屬性 (Property),因此在網頁持續性上不需要任何特殊屬性 (Attribute)。網頁架構預設會在控制項標記上將這些屬性 (Property) 保存為屬性 (Attribute),如下列範例所示:
<aspSample:Book Title="Wingtip Toys Stories"
CurrencySymbol="$"
Price="16"
BookType="Fiction">
</aspSample:Book>
Author 屬性 (Property) 和 Author 類別的屬性 (Property) 需要設計階段屬性 (Attribute),以啟用控制項標記中的持續性,如下列範例所示:
<aspSample:Book >
<Author FirstName="Judy" LastName="Lew" />
</aspSample:Book>
Book 控制項會將簡單屬性存放在 ViewState 字典中。但是,Book 控制項必須對 Author 屬性實作自訂狀態管理,以跨回傳 (Postback) 管理屬性的狀態。
Book 控制項的實際執行品質實作可定義其他書籍相關的屬性,例如發行者和發行日期。此外,Author 屬性可以使用集合屬性取代。如需實作集合屬性的詳細資訊,請參閱 Web 控制項集合屬性範例。
注意事項: |
---|
網頁開發人員可以停用網頁的檢視狀態,或停用網頁中個別控制項的檢視狀態。如果控制項需要跨回傳維護重要狀態以在內部運作,您可以使用在 ASP.NET 2.0 中定義的控制項狀態機制。控制項狀態會在控制項狀態和檢視狀態的比較範例中描述。 |
Book 控制項的程式碼清單
' Book.vb
Option Strict On
Imports System
Imports System.ComponentModel
Imports System.Security.Permissions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace Samples.AspNet.VB.Controls
< _
AspNetHostingPermission(SecurityAction.Demand, _
Level:=AspNetHostingPermissionLevel.Minimal), _
AspNetHostingPermission(SecurityAction.InheritanceDemand, _
Level:=AspNetHostingPermissionLevel.Minimal), _
DefaultProperty("Title"), _
ToolboxData("<{0}:Book runat=""server""> </{0}:Book>") _
> _
Public Class Book
Inherits WebControl
Private authorValue As Author
Private initialAuthorString As String
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("The name of the author."), _
DesignerSerializationVisibility( _
DesignerSerializationVisibility.Content), _
PersistenceMode(PersistenceMode.InnerProperty) _
> _
Public Overridable ReadOnly Property Author() As Author
Get
If (authorValue Is Nothing) Then
authorValue = New Author()
End If
Return authorValue
End Get
End Property
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(BookType.NotDefined), _
Description("Fiction or Not") _
> _
Public Overridable Property BookType() As BookType
Get
Dim t As Object = ViewState("BookType")
If t Is Nothing Then t = BookType.NotDefined
Return CType(t, BookType)
End Get
Set(ByVal value As BookType)
ViewState("BookType") = value
End Set
End Property
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("The symbol for the currency."), _
Localizable(True) _
> _
Public Overridable Property CurrencySymbol() As String
Get
Dim s As String = CStr(ViewState("CurrencySymbol"))
If s Is Nothing Then s = String.Empty
Return s
End Get
Set(ByVal value As String)
ViewState("CurrencySymbol") = value
End Set
End Property
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue("0.00"), _
Description("The price of the book."), _
Localizable(True) _
> _
Public Overridable Property Price() As Decimal
Get
Dim p As Object = ViewState("Price")
If p Is Nothing Then p = Decimal.Zero
Return CType(p, Decimal)
End Get
Set(ByVal value As Decimal)
ViewState("Price") = value
End Set
End Property
< _
Bindable(True), _
Category("Appearance"), _
DefaultValue(""), _
Description("The title of the book."), _
Localizable(True) _
> _
Public Overridable Property Title() As String
Get
Dim s As String = CStr(ViewState("Title"))
If s Is Nothing Then s = String.Empty
Return s
End Get
Set(ByVal value As String)
ViewState("Title") = value
End Set
End Property
Protected Overrides Sub RenderContents( _
ByVal writer As HtmlTextWriter)
writer.RenderBeginTag(HtmlTextWriterTag.Table)
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
writer.RenderBeginTag(HtmlTextWriterTag.Td)
writer.WriteEncodedText(Title)
writer.RenderEndTag()
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
writer.RenderBeginTag(HtmlTextWriterTag.Td)
writer.WriteEncodedText(Author.ToString())
writer.RenderEndTag()
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
writer.RenderBeginTag(HtmlTextWriterTag.Td)
writer.WriteEncodedText(BookType.ToString())
writer.RenderEndTag()
writer.RenderEndTag()
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
writer.RenderBeginTag(HtmlTextWriterTag.Td)
writer.Write(CurrencySymbol)
writer.Write(" ")
writer.Write(String.Format("{0:F2}", Price))
writer.RenderEndTag()
writer.RenderEndTag()
writer.RenderEndTag()
End Sub
Protected Overrides Sub LoadViewState( _
ByVal savedState As Object)
MyBase.LoadViewState(savedState)
Dim auth As Author = CType(ViewState("Author"), Author)
If auth IsNot Nothing Then
authorValue = auth
End If
End Sub
Protected Overrides Function SaveViewState() As Object
If authorValue IsNot Nothing Then
Dim currentAuthorString As String = _
authorValue.ToString()
If Not _
(currentAuthorString.Equals(initialAuthorString)) Then
ViewState("Author") = authorValue
End If
End If
Return MyBase.SaveViewState()
End Function
Protected Overrides Sub TrackViewState()
If authorValue IsNot Nothing Then
initialAuthorString = authorValue.ToString()
End If
MyBase.TrackViewState()
End Sub
End Class
End Namespace
// Book.cs
using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Samples.AspNet.CS.Controls
{
[
AspNetHostingPermission(SecurityAction.Demand,
Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand,
Level=AspNetHostingPermissionLevel.Minimal),
DefaultProperty("Title"),
ToolboxData("<{0}:Book runat=\"server\"> </{0}:Book>")
]
public class Book : WebControl
{
private Author authorValue;
private String initialAuthorString;
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The name of the author."),
DesignerSerializationVisibility(
DesignerSerializationVisibility.Content),
PersistenceMode(PersistenceMode.InnerProperty),
]
public virtual Author Author
{
get
{
if (authorValue == null)
{
authorValue = new Author();
}
return authorValue;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue(BookType.NotDefined),
Description("Fiction or Not"),
]
public virtual BookType BookType
{
get
{
object t = ViewState["BookType"];
return (t == null) ? BookType.NotDefined : (BookType)t;
}
set
{
ViewState["BookType"] = value;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The symbol for the currency."),
Localizable(true)
]
public virtual string CurrencySymbol
{
get
{
string s = (string)ViewState["CurrencySymbol"];
return (s == null) ? String.Empty : s;
}
set
{
ViewState["CurrencySymbol"] = value;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue("0.00"),
Description("The price of the book."),
Localizable(true)
]
public virtual Decimal Price
{
get
{
object price = ViewState["Price"];
return (price == null) ? Decimal.Zero : (Decimal)price;
}
set
{
ViewState["Price"] = value;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue(""),
Description("The title of the book."),
Localizable(true)
]
public virtual string Title
{
get
{
string s = (string)ViewState["Title"];
return (s == null) ? String.Empty : s;
}
set
{
ViewState["Title"] = value;
}
}
protected override void RenderContents(HtmlTextWriter writer)
{
writer.RenderBeginTag(HtmlTextWriterTag.Table);
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.WriteEncodedText(Title);
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.WriteEncodedText(Author.ToString());
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.WriteEncodedText(BookType.ToString());
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.Write(CurrencySymbol);
writer.Write(" ");
writer.Write(String.Format("{0:F2}", Price));
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderEndTag();
}
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
Author auth = (Author)ViewState["Author"];
if (auth != null)
{
authorValue = auth;
}
}
protected override object SaveViewState()
{
if (authorValue != null)
{
String currentAuthorString = authorValue.ToString();
if (!(currentAuthorString.Equals(initialAuthorString)))
{
ViewState["Author"] = authorValue;
}
}
return base.SaveViewState();
}
protected override void TrackViewState()
{
if (authorValue != null)
{
initialAuthorString = authorValue.ToString();
}
base.TrackViewState();
}
}
}
程式碼討論
套用至 Book 控制項 Author 屬性的 DesignerSerializationVisibilityAttribute 和 PersistenceModeAttribute,會在 Web 控制項集合屬性範例中描述。序列化和保存 Author 類別的屬性 (Property) 時,會需要這些屬性 (Attribute)。
檢視狀態 (View State) 機制會參考 ASP.NET 用於維護跨回傳 (Postback) 狀態的技術。此機制會在網頁處理結束時,將網頁狀態及其控制項樹狀結構序列化至字串表示,然後在回傳時還原序列化字串。根據預設,網頁會將字串傳送至瀏覽器做為隱藏欄位。如需詳細資訊,請參閱 ASP.NET 狀態管理概觀。
若要管理簡單屬性的狀態,可將之定義為存放在控制項 ViewState 屬性中的可讀/寫屬性。Book 控制項會使用這個方法定義其簡單屬性 (BookType、CurrencySymbol、Price 和 Title)。將為您管理存放在 ViewState 屬性中的屬性狀態,而不需要採取任何動作。
若要管理具有子屬性的屬性,可以將屬性定義為唯讀,並撰寫程式碼以管理物件的狀態。若要完成這項作業,可以覆寫下列方法:
TrackViewState,對控制項發出初始設定之後開始追蹤屬性變更的信號。
SaveViewState,在網頁要求結束時儲存屬性 (如果屬性已變更)。
LoadViewState,回傳時將儲存的狀態載入屬性。
注意事項: 除了上面討論的方法以外,還有其他方法可以管理複雜屬性的狀態。可以透過以每個屬性的變更修改檢視狀態,進而延伸逐步解說:開發和使用自訂的伺服器控制項中所使用的方法。您也可以在屬性類別中管理狀態,如自訂屬性狀態管理範例所示。
ViewState 屬性的型別 StateBag,是具有內建狀態管理的字典。StateBag 類別會實作 IStateManager 介面,可定義 TrackViewState、SaveViewState 和 LoadViewState 方法。StateBag 類別會實作這些方法,以便在初始設定之後開始追蹤控制項屬性的變更、於網頁要求結束時儲存修改的項目,並在回傳時將儲存的狀態載入項目。如果在對網頁要求執行 OnInit 方法之後設定項目,StateBag 會將項目標記為已修改,對這些項目進行追蹤。例如,如果網頁上的程式碼在網頁初始設定之後執行,並設定 Book 控制項的 Title 屬性,則會對 ViewState["Title"] 產生變更。因此,在 ViewState 中的 "Title"索引鍵下存放的值,會標記為已修改。如需詳細資訊,請參閱 ASP.NET 網頁存留週期概觀。
Book 控制項會將 Author 屬性定義為唯讀屬性,並實作自訂狀態管理,如下所示:
在 TrackViewState 方法中,Book 控制項會先將初始 Author 屬性儲存至字串,然後透過叫用基底類別的 TrackViewState 方法,開始進行狀態追蹤。
在 SaveViewState 方法中,Book 控制項會判斷 Author 屬性是否已從初始值變更。如果屬性已變更,Book 會使用索引鍵 "Author",將 Author 屬性儲存在 ViewState 字典中。Book 控制項接著會叫用基底類別的 SaveViewState 方法。由於已啟動狀態追蹤,儲存在 ViewState 的 Author 物件會自動標記為已修改,並當做基底類別檢視狀態的一部分儲存。
在 LoadViewState 中,Book 控制項會先叫用基底類別的 LoadViewState 方法。這個呼叫會自動還原 ViewState 字典。Book 控制項接著會判斷,ViewState 字典是否具有存放在 "Author" 下的項目。如果有的話,控制項會將檢視狀態值載入 Author 屬性。
Author 型別 (在隨後列出的程式碼中定義) 具有自訂型別轉換子 (Type Converter),因此可以在檢視狀態中存放 Author 執行個體。型別轉換子會將 Author 執行個體轉換為字串,反之亦然。定義型別轉換子可允許在視覺化設計工具中設定 Author 的子屬性。自訂型別轉換子會在型別轉換子範例中描述。您可以存放在檢視狀態中的型別是由 LosFormatter 類別所限制,ASP.NET 會使用這個類別進行檢視狀態序列化 (Serialization)。能夠最有效序列化的型別為 String、NET Framework 類別庫中的基本實值型別 (例如 Boolean、Int16、Int32、Enum、Pair、Triplet、Array、ArrayList 和 Hashtable),以及包含這些基本型別 (Primitive Type) 的型別。此外,您也可以在檢視狀態中存放自訂型別,這些型別具有定義的型別轉換子,例如 Pair、Triplet、Array、ArrayList 和 Hashtable。對控制項定義檢視狀態序列化時,必須將控制項資料轉換為下列其中一個型別。如果將型別存放在與檢視狀態序列化機制不相容的檢視狀態,控制項可能會進行編譯,但會在執行階段時產生錯誤。最後,可序列化型別 (也就是實作 ISerializable 介面或使用 SerializableAttribute 標記的型別) 可以存放在檢視狀態,但這些型別的序列化速度明顯會比基本型別還慢。
控制項對序列化所產生的狀態物件是控制項的檢視狀態。控制項的 ViewState 屬性只是控制項檢視狀態的一小部分,它可以是自動參與檢視狀態機制的屬性,而不需要您採取任何動作。Control 類別會實作邏輯,以便使用 SaveViewState 和 LoadViewState 方法,將已修改的項目儲存和載入 ViewState 字典中。其他檢視狀態部分是額外的物件,您 (和控制項基底類別) 可覆寫 SaveViewState 方法,將額外的物件儲存在檢視狀態中。覆寫 SaveViewState 和 LoadViewState 方法時,必須呼叫基底類別的對應方法。
Author 類別的程式碼清單
將 NotifyParentPropertyAttribute 套用至 FirstName、LastName 和 MiddleName 屬性 (Property),並將屬性 (Attribute) 的建構函式引數設為 true,會讓視覺化設計工具將這些屬性 (Property) 的變更傳送並序列化至其父屬性 (Author 執行個體)。
' Author.vb
Option Strict On
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Globalization
Imports System.Web.UI
Namespace Samples.AspNet.VB.Controls
< _
TypeConverter(GetType(AuthorConverter)) _
> _
Public Class Author
Dim firstNameValue As String
Dim lastNameValue As String
Dim middleNameValue As String
Public Sub New()
Me.New(String.Empty, String.Empty, String.Empty)
End Sub
Public Sub New(ByVal firstname As String, _
ByVal lastname As String)
Me.New(firstname, String.Empty, lastname)
End Sub
Public Sub New(ByVal firstname As String, _
ByVal middlename As String, ByVal lastname As String)
firstNameValue = firstname
middleNameValue = middlename
lastNameValue = lastname
End Sub
< _
Category("Behavior"), _
DefaultValue(""), _
Description("First name of author."), _
NotifyParentProperty(True) _
> _
Public Overridable Property FirstName() As String
Get
Return firstNameValue
End Get
Set(ByVal value As String)
firstNameValue = value
End Set
End Property
< _
Category("Behavior"), _
DefaultValue(""), _
Description("Last name of author."), _
NotifyParentProperty(True) _
> _
Public Overridable Property LastName() As String
Get
Return lastNameValue
End Get
Set(ByVal value As String)
lastNameValue = value
End Set
End Property
< _
Category("Behavior"), _
DefaultValue(""), _
Description("Middle name of author."), _
NotifyParentProperty(True) _
> _
Public Overridable Property MiddleName() As String
Get
Return middleNameValue
End Get
Set(ByVal value As String)
middleNameValue = value
End Set
End Property
Public Overrides Function ToString() As String
Return ToString(CultureInfo.InvariantCulture)
End Function
Public Overloads Function ToString( _
ByVal culture As CultureInfo) As String
Return TypeDescriptor.GetConverter( _
Me.GetType()).ConvertToString(Nothing, culture, Me)
End Function
End Class
End Namespace
// Author.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Web.UI;
namespace Samples.AspNet.CS.Controls
{
[
TypeConverter(typeof(AuthorConverter))
]
public class Author
{
private string firstnameValue;
private string lastnameValue;
private string middlenameValue;
public Author()
:
this(String.Empty, String.Empty, String.Empty)
{
}
public Author(string firstname, string lastname)
:
this(firstname, String.Empty, lastname)
{
}
public Author(string firstname,
string middlename, string lastname)
{
firstnameValue = firstname;
middlenameValue = middlename;
lastnameValue = lastname;
}
[
Category("Behavior"),
DefaultValue(""),
Description("First name of author."),
NotifyParentProperty(true),
]
public virtual String FirstName
{
get
{
return firstnameValue;
}
set
{
firstnameValue = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Description("Last name of author."),
NotifyParentProperty(true)
]
public virtual String LastName
{
get
{
return lastnameValue;
}
set
{
lastnameValue = value;
}
}
[
Category("Behavior"),
DefaultValue(""),
Description("Middle name of author."),
NotifyParentProperty(true)
]
public virtual String MiddleName
{
get
{
return middlenameValue;
}
set
{
middlenameValue = value;
}
}
public override string ToString()
{
return ToString(CultureInfo.InvariantCulture);
}
public string ToString(CultureInfo culture)
{
return TypeDescriptor.GetConverter(
GetType()).ConvertToString(null, culture, this);
}
}
}
BookType 列舉型別的程式碼清單
' BookType.vb
Option Strict On
Imports System
Namespace Samples.AspNet.VB.Controls
Public Enum BookType
NotDefined = 0
Fiction = 1
NonFiction = 2
End Enum
End Namespace
// BookType.cs
using System;
namespace Samples.AspNet.CS.Controls
{
public enum BookType
{
NotDefined = 0,
Fiction = 1,
NonFiction = 2
}
}
Book 控制項的測試頁
下列範例顯示使用 Book 控制項的 .aspx 網頁。
<%@ Page Language="C#" Debug="true" Trace="true"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script >
void Button_Click(object sender, EventArgs e)
{
Book1.Author.FirstName = "Bob";
Book1.Author.LastName = "Kelly";
Book1.Title = "Contoso Stories";
Book1.Price = 39.95M;
Button1.Visible = false;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" >
<title>
Book test page
</title>
</head>
<body>
<form id="Form1" >
<aspSample:Book ID="Book1" Runat="server"
Title="Tailspin Toys Stories" CurrencySymbol="$"
BackColor="#FFE0C0" Font-Names="Tahoma"
Price="16" BookType="Fiction">
<Author FirstName="Judy" LastName="Lew" />
</aspSample:Book>
<br />
<asp:Button ID="Button1" OnClick="Button_Click"
Runat="server" Text="Change" />
<asp:Button ID="Button2" Runat="server" Text="Refresh" />
<br />
<br />
<asp:HyperLink ID="Hyperlink1" href="BookTest.aspx"
Runat="server">
Reload Page</asp:HyperLink>
</form>
</body>
</html>
建置及使用範例
使用在型別轉換子範例中所列出的 AuthorConverter 類別,編譯這個範例中的類別。
如需編譯及使用自訂控制項範例的詳細資訊,請參閱建置自訂伺服器控制項範例。