|
經(jīng)常會碰到在前臺代碼中要使用(或綁定)后臺代碼中變量值的問題。一般有<%= str%>和<%# str %>兩種方式,這里簡單總結(jié)一下。如有錯誤或異議之處,敬請各位指教。
一方面,這里所講的前臺即通常的.ASPx文件,后臺指的是與ASPx相關(guān)聯(lián)的CodeBehind,文件后綴名為.ASPx.cs;另一方面,這里的綁定是指用戶發(fā)出訪問某一頁面指令后,服務(wù)器端在執(zhí)行過程中就已經(jīng)將前臺代碼進(jìn)行了賦值,而后生成html格式回傳客戶端顯示,而并非已經(jīng)顯示到客戶端后,然后通過其他方法(如ajax)去服務(wù)器端獲取相應(yīng)變量。
備注:上面說的兩個(gè)文件是常見的代碼隱藏(code-behind)模式,還有一種是代碼嵌入(code-beside, inline)模式,那就是只存在ASPx一個(gè)文件,而后臺代碼則寫入此文件的<script type="text/Javascript" runat="server"></script>之中(還有一些語法上區(qū)別),這對于本文討論的問題略有影響,因?yàn)榇a嵌入是聲明性代碼與C#/VB.NET代碼都一起編譯到一個(gè)類里面,而代碼隱藏則將聲明性代碼與C#/VB.NET代碼分開幾次進(jìn)行翻譯/編譯,因此前者是局部與局部(partial)的關(guān)系后者基類與派生類的關(guān)系,但這僅僅影響所能綁定變量的范圍(與修飾符有關(guān)),下面會提到。以下均以代碼隱藏模式為例。
一般來說,在前臺代碼的三種位置可能會用到(綁定)后臺變量:
- 服務(wù)器端控件屬性或HTML標(biāo)簽屬性
- JavaScript代碼中
- Html顯示內(nèi)容的位置(也就是開始標(biāo)簽與結(jié)束標(biāo)簽之間的內(nèi)容,如<div>這里</div>(Html標(biāo)簽)或者<ASP:Label ID="Label2" runat="server" Text="Label">這里</ASP:Label>(服務(wù)器端控件),它作為占位符把變量顯示于符號出現(xiàn)的位置)
對于第一種位置,有一些約束條件:
(1)一般的屬性要求是字符串型或數(shù)值型(下面會提到有些服務(wù)器端屬性支持屬性為數(shù)據(jù)集合);
(2)并不是所有的屬性都可以綁定變量,有些屬性例如runat屬性必須是"server"常量,即使綁定的字符串是server,也會導(dǎo)致分析器分析時(shí)出錯;
(3)有一種屬性,他要求屬性值有約束(類型約束,比如服務(wù)器端控件要求TabIndex屬性是short類型,或者字符串內(nèi)容有約束),也應(yīng)該在綁定時(shí)滿足,否則依然可能編譯時(shí)報(bào)錯;
(4)還一種屬性,雖然屬性本身有約束,但即使綁定的變量不滿足約束,也可以編譯通過,比如input的checked屬性,它只有checked字符串是合法的,但如果通過綁定獲取到的字符串不是checked,那么這些屬性將有自己內(nèi)部處理機(jī)制,來保證可以正常使用;
(5)還要注意,即使對于同一類屬性,服務(wù)器端和HTML的屬性的處理機(jī)制也不同,同樣是TabIndex(tabIndex),前者如果不滿足,則分析器錯誤,后者則忽略這一問題。
對于第二種位置,一般只要綁定的后臺變量和JavaScript中數(shù)據(jù)類型兼容即可。
對于第三種位置,如果綁定出現(xiàn)的位置不在服務(wù)器端控件內(nèi)部,則沒有約束條件,只要是常量字符串可以出現(xiàn)的位置,均可以綁定。但是對于置于服務(wù)器端控件內(nèi)部,也就是上面那種<ASP:Label ID="Label2" runat="server" Text="Label">這里</ASP:Label>的方式,則有約束條件。通過總結(jié),歸納為四類服務(wù)器端控件,如果綁定的代碼出現(xiàn)在這些控件的開始和結(jié)束標(biāo)簽之間(這里所說的控件,是指如果綁定代碼外有多層的嵌套控件包圍,則是指包圍綁定代碼的最內(nèi)層控件),有不同的顯示結(jié)果:
(1)約束型控件:這類控件要求它的開始標(biāo)簽和結(jié)束標(biāo)簽中只能包含指定的子控件,因此如果在這里出現(xiàn)代碼塊,將編譯錯誤。例如:
<ASP:DataList runat="server"></ASP:DataList>,在它之間,要求必須嵌套<ItemTemplate></ItemTemplate>。
(2)非嵌套類控件:這類控件,不允許在內(nèi)部嵌套其他控件或標(biāo)簽,只能是常量字符串,它會將開始標(biāo)簽和結(jié)束標(biāo)簽中常量字符串內(nèi)容作為他的屬性。例如上面提到的TextBox,它會將標(biāo)簽間內(nèi)容作為它的Text屬性值。
(3)嵌套類控件:這類控件,可以嵌套其他任意控件,也可以包含字符串,因此可以正常顯示綁定代碼塊所表示的字符串內(nèi)容。例如Label控件、Panel等。
(4)數(shù)據(jù)綁定類控件:這類控件是ASP.NET提供的服務(wù)器端控件,除了可以綁定普通的變量類型,也可以綁定一個(gè)數(shù)據(jù)集合(只能采取下面的第二種方式實(shí)現(xiàn))。
關(guān)于是否加引號:在以上三個(gè)位置使用時(shí),是否應(yīng)該將<%= str%>或<%# str %>置于單引號或雙引號中呢?對于在不同位置,處理的方式是不同的:(具體請?jiān)谙旅鎯煞N方式的具體介紹時(shí),加以體會)
(1)對于第一種位置,由于JavaScript是弱類型的,如果綁定時(shí)加引號,顯然就認(rèn)為就當(dāng)做字符串來處理,這始終是正確的;如果綁定時(shí)不加引號,它將認(rèn)為這是個(gè)數(shù)值型的,那么如果獲取的真是數(shù)值,當(dāng)然可以,如果是非數(shù)值型,則將產(chǎn)生腳本錯誤,這即使對于JavaScript賦值常量時(shí),也是同樣的:
var test1 = 123b;//運(yùn)行時(shí)報(bào)錯
var test2=123;//正確,是數(shù)值型
var test3="123b";//正確,字符串型
(2)對于第二種位置,經(jīng)過測試,無論是對于服務(wù)器端控件屬性還是HTML標(biāo)簽屬性,加引號總是正確的;如果不加引號,則兩種屬性的處理方式不同:
- 對于服務(wù)器端控件屬性,如果綁定的代碼塊不加引號,則編譯時(shí)會提示“驗(yàn)證(ASP.NET):特性值前后必須加引號”的警告信息,但是生成為HTML后,對應(yīng)生成的HTML屬性已經(jīng)被加上引號并獲取了正確的綁定結(jié)果,因此加不加引號不會影響使用,但是建議對于規(guī)范的代碼,還是加上為好;
- 對于HTML標(biāo)簽屬性,如果不加引號,則編譯時(shí)會提示“驗(yàn)證(XHTML 1.0 Transitional): 特性值前后必須加引號”的警告信息,并且生成為HTML屬性也確實(shí)沒有加上引號,那么雖然屬性后面確實(shí)是沒有加上引號的正確的綁定值,但是不一定能展示出想要看到的結(jié)果。比如對于input標(biāo)簽的value屬性,如果綁定的字符串是" hello world from variable”,則在客戶端的input顯示出的內(nèi)容實(shí)際上只是"hello”字符串,生效的屬性值是一個(gè)被截?cái)嗟淖址鼜膶傩院蟮囊淮址ㄈ粑醇右枺┑牡谝粋€(gè)非空字符開始,截止到下一個(gè)空字符的前一個(gè)字符為止(比如對于" hello world”,結(jié)果將是"hello”),因此,加上引號是必須的。
(3)對于第三種位置,加與不加引號,獲取的值及其顯示均不受影響。
因此建議,所有綁定表達(dá)式都加上引號,作為字符串獲取,然后根據(jù)實(shí)際需求,用相應(yīng)函數(shù)進(jìn)行轉(zhuǎn)換,得到所需要的類型。
另外,這里所說的后臺變量是泛指的,包括如下:
- 成員變量
- 方法或?qū)傩缘姆祷刂?nbsp;
- 表達(dá)式,也就是所有后臺能夠執(zhí)行的代碼,運(yùn)行后所得到的值(也就是直接將后臺代碼寫在前臺代碼中,記得使用完全限定名或在后臺中using相關(guān)namespace)
- 數(shù)據(jù)集合
后臺變量有一些約束條件,需要滿足:
(1)變量修飾符要求。變量是靜態(tài)或者實(shí)例字段均可。對于代碼隱藏模式的ASP.NET,以上的所述的變量必須為public或protected類型(因?yàn)槭腔惻c派生類的關(guān)系),private或者internal都不行,而代碼嵌入模式則任何修飾符的變量均可訪問(一個(gè)類內(nèi)部的關(guān)系)。
(2)變量類型要求。由于前臺屬性一般是字符串類型,而JavaScript基本類型也就是字符串型、數(shù)字型、布爾型,因此對應(yīng)的變量應(yīng)該也是這幾種方式,其余類型如果不被支持(如復(fù)雜類型、數(shù)組、引用類型等),前臺獲取的就是調(diào)用了變量的ToString()方法所得到的字符串。因此,在綁定時(shí),要根據(jù)情況看是否能進(jìn)行隱式類型轉(zhuǎn)換,必要時(shí)還要用相關(guān)函數(shù)來強(qiáng)制轉(zhuǎn)換,以保證前臺可以獲得正確的值。當(dāng)然,對于數(shù)據(jù)綁定類控件,它的有些屬性可以為數(shù)據(jù)集合,但這時(shí)的綁定只能通過下面第二種方式才被支持。
以上是一些概念和基本約束,這些都是兩種方式都應(yīng)該滿足的,下面具體介紹兩種方式,來實(shí)現(xiàn)前臺代碼中(以下稱為代碼塊)綁定后臺變量的功能。
一. <%= str%>
此種方式其實(shí)是ASP 時(shí)代就支持的,ASP 通過包含在 < % 和 %>中的表達(dá)式將執(zhí)行結(jié)果輸出到客戶瀏覽器 , 如:< % =test %>就是將變量test的值發(fā)送到客戶瀏覽器中。在ASP.NET中,這個(gè)表達(dá)式依然可以使用,并可以出現(xiàn)在前臺代碼的上述三個(gè)位置,但是要注意,除了上述的一般性約束外,對于控件屬性,還必須是綁定到非服務(wù)器端控件的屬性。另外,它只能綁定上面講的前三種變量類型,不支持綁定數(shù)據(jù)集合。例子如下:
后臺代碼:
public partial class WebForm2 : System.Web.UI.Page
{
public string GetVariableStr;//注意變量的修飾符
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
GetVariableStr = "hello world from variable";
}
}
protected string GetFunctionStr()//注意返回值的修飾符
{
return "hello world from Function";
}
}
前臺代碼:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/Javascript">
function fun() {
var str = '<%= DateTime.Now %>';
//前臺位置1,綁定的是第三種變量類型(也是第二種方式,?因?yàn)镹ow是個(gè)屬性)
alert(str);
}
</script>
</head>
<body onload="fun()">
<form id="form1" runat="server">
<div>
<input type="text" value="<%= GetVariableStr %>" />
<%--前臺位置2,綁定的是成員變量--%>
"<%= GetFunctionStr() %>"
<%--前臺位置3,綁定的是一個(gè)方法的返回值>--%>
</div>
</form>
</body>
</html>
一些錯誤的使用:
之所以說第一種綁定方式要用于非服務(wù)器端控件的屬性,是因?yàn)槿绻?a href=/pingce/yingyong/ target=_blank class=infotextkey>應(yīng)用于這些服務(wù)器端屬性時(shí),這些代碼實(shí)際上不被解析。 比如:
<ASP:Label ID="Label1" runat="server" Text="<%= GetVariableStr %>"></ASP:Label>
<ASP:TextBox ID="TextBox1" runat="server" Text="<%= GetVariableStr %>"></ASP:TextBox>
則顯示出來的Label1的文本是空,而TextBox中文本是"<%= GetVariableStr %>”,所以記住,對服務(wù)器端控件的屬性加這樣的代碼塊,將不被解析,而是將這一字符串直接作為屬性值了,所以不是想要的結(jié)果。如果引號也不加上,將會編譯錯誤,提示“服務(wù)器標(biāo)記不能包含 <% ... %> 構(gòu)造。”。
這里結(jié)合開篇提到的關(guān)于將綁定代碼快置于“Html顯示內(nèi)容的位置”時(shí),如果在服務(wù)器端控件內(nèi),那四類控件如何顯示的問題。如下:
<ASP:Label ID="Label1" runat="server" >"<%= GetVariableStr %>"</ASP:Label>
<ASP:TextBox ID="TextBox1" runat="server" >"<%= GetVariableStr %>"</ASP:TextBox>
其中,Label1屬于嵌套類控件,Label1確實(shí)顯示了正確的結(jié)果,TextBox屬于非嵌套類控件,TextBox如果用這種方式,將會產(chǎn)生編譯錯誤,提示“此上下文中不支持代碼塊。”
二. <%# str %>
ASP.NET 引入了一種新的聲明語法 <%# %>。該語法是在 .ASPx 頁中使用數(shù)據(jù)綁定的基礎(chǔ),所有數(shù)據(jù)綁定表達(dá)式都必須包含在這些字符中。這里從用法和適用范圍等方面與第一種綁定方式進(jìn)行區(qū)分。
從出現(xiàn)的位置來看,除了能出現(xiàn)在第一種代碼塊出現(xiàn)的所有位置外,他還可以出現(xiàn)在服務(wù)器端控件的屬性中。
從綁定的變量類型上看,他還可以配合ASP.NET的數(shù)據(jù)綁定類控件,來綁定上述的第四種“變量”類型,就是數(shù)據(jù)集合(DropDownList,DataList,DataGrid,ListBox這些是數(shù)據(jù)綁定類控件,數(shù)據(jù)集合包括ArrayList(數(shù)組),Hashtable(哈稀表,DataView(數(shù)據(jù)視圖),DataReader等)。
從用法上看,在前臺代碼中除了在相應(yīng)位置寫上<%# %>外,在后臺代碼中,還需要使用DataBind()方法。以下是實(shí)例:
前臺代碼:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/Javascript">
function fun() {
var str = '<%# DateTime.Now %>';
alert(str);
}
</script>
</head>
<body onload="fun()">
<form id="form1" runat="server">
<div>
<input type="text" value="<%# GetVariableStr %>" /><br />
"<%# GetVariableStr %>"
<ASP:Label ID="Label1" runat="server" Text="<%# GetVariableStr %>"></ASP:Label>
<%--此種方式可以綁定服務(wù)器端控件的屬性--%>
<ASP:DropDownList ID="DropDownList1" runat="server" DataSource='<%# arraylist %>'>
<%-- 將集合綁定到數(shù)據(jù)綁定類控件,通過DataSource屬性來實(shí)現(xiàn),從而在下拉框看到集合中的內(nèi)容--%>
</ASP:DropDownList>
<ASP:DataList ID="DataList1" runat="server" DataSource='<%# dt %>'>
<%-- 同上,綁定了DataTable數(shù)據(jù)集合?--%>
<ItemTemplate>
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td>
<ASP:Label ID="Label2" runat="server" Text='<%# Bind("row0")%>'></ASP:Label>
<%--由于綁定的數(shù)據(jù)集合具有多列,并且此數(shù)據(jù)綁定類控件支持模板,
因此需要在模板中指定需要綁定的列以及格式--%>
</td>
<td>
<%# Eval("row1")%>
</td>
</tr>
</table>
</ItemTemplate>
</ASP:DataList>
</div>
</form>
</body>
</html>
可以看出,這種方式在使用時(shí),不但可以實(shí)現(xiàn)(取代)<%=... %>所滿足的功能,還可以綁定服務(wù)器控件屬性(如上面的Label1),也可以將集合類型綁定到支持的數(shù)據(jù)綁定類控件。在用法上,前臺代碼除了對數(shù)據(jù)綁定類控件綁定數(shù)據(jù)集合外有所差別,其他的使用上與第一種沒區(qū)別。在綁定類控件的模板中,如何使用Eval、Bind、DataBinder.Eval等,不在此文討論中,可以參考下面鏈接的參考文章。
后臺代碼:
public partial class WebForm2 : System.Web.UI.Page
{
public string GetVariableStr;
public ArrayList arraylist;
public DataTable dt;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
GetVariableStr = "hello world from variable";
arraylist = new ArrayList();
arraylist.Add("選?項(xiàng)?1");
arraylist.Add("選?項(xiàng)?2");
dt = new DataTable();
dt.Columns.Add("row0");
dt.Columns.Add("row1");
DataRow dr1 = dt.NewRow();
dr1[0] = "1.1";
dr1[1] = "1.2";
DataRow dr2 = dt.NewRow();
dr2[0] = "2.1";
dr2[1] = "2.2";
dt.Rows.Add(dr1);
dt.Rows.Add(dr2);
Page.DataBind();
//DropDownList1.DataBind();
//DataList1.DataBind();
}
}
}
在后臺代碼中,與第一種方式唯一不同的,就是需要調(diào)用DataBind方法。只有執(zhí)行了相應(yīng)控件的DataBind方法,前臺代碼中這些控件中使用<%# %>的綁定才會發(fā)生(并且控件內(nèi)部的所有綁定也會發(fā)生,比如又嵌套了一個(gè)綁定后臺數(shù)據(jù)的控件),否則得話將不會被賦值,而是默認(rèn)空值。上面我們用的是Page的DataBind方法,那么整個(gè)頁面所有綁定都會執(zhí)行。當(dāng)然,如果我們只執(zhí)行DataList1或者DropDownList1的DataBind方法,那么只有相應(yīng)控件的綁定才會發(fā)生。需要注意的是,這里說的需要執(zhí)行DataBind包括了顯示和隱式執(zhí)行,有些數(shù)據(jù)綁定類控件,當(dāng)它們通過 DataSourceID 屬性綁定到數(shù)據(jù)源控件時(shí),會通過隱式調(diào)用 DataBind 方法來執(zhí)行綁定。這時(shí)就不必顯示的再次調(diào)用了。
兩者區(qū)別:
兩種綁定方式上,他們的約束基本相同,都要求與屬性匹配,出現(xiàn)在他們可以出現(xiàn)的位置。后者的使用位置更廣泛,尤其是支持服務(wù)器端控件和綁定數(shù)據(jù)集合。后臺代碼方面,后者需要調(diào)用DataBind才能完成綁定,前者則沒有這方面要求。這里主要區(qū)別一下兩者在執(zhí)行機(jī)制上的區(qū)別:<%=...%>是在程序執(zhí)行時(shí)調(diào)用(應(yīng)該是在頁面的RenderControl事件過程中完成的,也就是通常情況下我們可以看到的后臺代碼都執(zhí)行完畢后再去到前臺代碼中進(jìn)行賦值綁定),而<%#... %>是在DataBind()方法之后被調(diào)用,一旦調(diào)用了DataBind(),則它對應(yīng)的控件將綁定變量,因此,請注意:如果在DataBind()后再修改變量,那么綁定的就不是最新值了,這就需要在完成變量的賦值后,再去DataBind()。其實(shí)這兩種方式,它的運(yùn)行過程可以在VS中通過設(shè)置斷點(diǎn)來看看,看兩者的綁定賦值分別是在什么時(shí)候發(fā)生的。
尚存的疑問:
1.不知道為什么不能獲取到internal修飾的變量?
答:此問題已經(jīng)在下一篇文章《ASP.NET前臺無法訪問后臺internal類型變量的問題》中分析并解決。(2010-10-25日更新)
參考文章:
深入理解 ASP.NET 動態(tài)控件 (Part 2 - 編譯過程)
<%# Eval("name")%>與<%# Bind("name")%>區(qū)別
NET技術(shù):ASP.NET前臺代碼綁定后臺變量方法總結(jié),轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時(shí)間聯(lián)系我們修改或刪除,多謝。