數據源控件需要使用參數值來指定需要選擇哪些數據,或者指定如何修改數據以及修改什麼數據。通常情況下,頁面包含一些 UI,以定義那些必須在選擇操作中使用的參數,而數據綁定控件提供了參數值來進行插入、更新和刪除操作。但是,在任意一種情況下,都可能同時出現兩種現象。在第 1 部分中,數據源控件揭示了 ZipCode 屬性,該屬性可進行聲明性設置,或者以編碼來設置以響應用戶操作。參數被設計為以聲明性(且可擴展)的方式來完成此方案。
引言
Parameter 基類代表一個通用參數。Microsoft Visual Studio 2005 提供了諸如 QueryStringParameter 之類的參數,以便將數據從查詢字符串參數請求到數據源中。另一個非常有用的參數是 ControlParameter,該參數允許從任一控件屬性中請求數據。如果內置參數類型不能滿足您的要求,則您可以定義自己的參數類型。這樣您就可以使頁面與粘接代碼不相關,而是將該代碼整齊地封裝在參數實現中。
除了從不同的源中請求值,這些參數還可以跟蹤值的更改情況,並通知這些更改的所屬數據源,進而引發數據源更改通知,最終在數據綁定控件中觸發數據綁定操作。簡而言之,這就是使用 ControlParameters 時,主要的聲明性詳細方案所依據的原理。
示例
在此將向 WeatherDataSource 添加參數功能,然後進一步闡述。
public class WeatherDataSource : DataSourceControl {
public static readonly string ZipCodeParameterName = "ZipCode";
...
private ParameterCollection _parameters;
private ParameterCollection Parameters {
get {
if (_parameters == null) {
_parameters = new ParameterCollection();
_parameters.ParametersChanged += new EventHandler(this.OnParametersChanged);
if (IsTrackingVIEwState) {
((IStateManager)_parameters).TrackVIEwState();
}
}
return _parameters;
}
}
...
public string GetSelectedZipCode() {
if (_parameters != null) {
Parameter zipCodeParameter = _parameters[ZipCodeParameterName];
if (zipCodeParameter != null) {
IOrderedDictionary parameterValues = _parameters.GetValues(Context, this);
return (string)parameterValues[zipCodeParameter.Name];
}
}
return ZipCode;
}
protected override void LoadVIEwState(object state) {
object baseState = null;
if (state != null) {
Pair p = (Pair)state;
baseState = p.First;
if (p.Second != null) {
((IStateManager)Parameters).LoadVIEwState(p.Second);
}
}
base.LoadVIEwState(baseState);
}
protected override void OnInit(EventArgs e) {
Page.LoadComplete += new EventHandler(this.OnPageLoadComplete);
}
private void OnPageLoadComplete(object sender, EventArgs e) {
if (_parameters != null) {
_parameters.UpdateValues(Context, this);
}
}
private void OnParametersChanged(object sender, EventArgs e) {
CurrentConditionsVIEw.RaiseChangedEvent();
}
protected override object SaveVIEwState() {
object baseState = base.SaveVIEwState();
object parameterState = null;
if (_parameters != null) {
parameterState = ((IStateManager)_parameters).SaveVIEwState();
}
if ((baseState != null) || (parameterState != null)) {
return new Pair(baseState, parameterState);
}
return null;
}
protected override void TrackVIEwState() {
base.TrackVIEwState();
if (_parameters != null) {
((IStateManager)_parameters).TrackVIEwState();
}
}
}
Microsoft ASP.Net 提供了 ParameterCollection,您可以完全按原樣使用該集合。它同時包含更改跟蹤和狀態管理功能。您只需相應地調用該集合的 API 來合並這些功能,另外還可以在控件外將該集合揭示為屬性。在上述代碼中,需要注意的關鍵點為:
·該數據源控件揭示了一個 ParameterCollection 類型的屬性,以使開發人員能夠添加表示要使用的郵政編碼值的參數。如果已經設置了參數,則使用該參數;否則,將使用 ZipCode 屬性值。
·該控件替代了與狀態管理相關的方法,以請求 ParameterCollection 中內置的狀態管理功能。
·該控件使用頁面生命周期的新 LoadComplete 事件來更新參數值,它通過替代 OnInit 來注冊這些值。
如果在初始化、回發處理或頁面編碼(當引發 LoadComplete 時,全部都會發生)期間更改了任何參數的值,則該數據源控件還會注冊 ParameterCollection 所引發的 ParametersChanged 事件。與上述情況一樣,如果設置了 ZipCode 屬性,將會引發更改通知,向數據綁定控件指明它需要再次執行數據綁定操作(隨後在 PreRender 期間將會發生此情況)。
·需要參與生命周期是數據源作為控件(即使是非可視控件)來實現的一個原因。另一個原因是為了使數據綁定控件能夠通過使用其 DataSourceID 屬性來使用 FindControl,並能夠獲得基於 INamingContainer 的分層名稱領域的益處(這樣就能夠實現嵌套數據方案,方法是在模板內放置一個數據源控件,並使其在每行中重復一次)。數據源是控件這一事實早已是爭論的焦點 - 但願這能夠說明此問題的一些論據。
在此 DataSourceVIEw 只需調用 GetSelectedZipCode,而不是直接使用 ZipCode 屬性。此外,還更改了數據源視圖代碼,以便在未選中 ZipCode 的情況下返回 null(而不是拋出異常),這會導致數據綁定控件顯示“空”視圖。這在通常情況下是一個慣例,但是回顧來看,這應該成為數據源控件語義的一個不可獲缺的方面。
private sealed class WeatherDataSourceView : DataSourceVIEw {
...
internal Weather GetWeather() {
string zipCode = _owner.GetSelectedZipCode();
if (zipCode.Length == 0) {
return null;
}
WeatherService weatherService = new WeatherService(zipCode);
return weatherService.GetWeather();
}
}
完整的代碼就是這個樣子。以下是經過更新的用法示例,該示例現在是聲明性的。
Zip Code: <ASP:TextBox runat="server" id="zipCodeTextBox" />
<ASP:Button runat="server" Text="查找" />
<hr />
<ASP:FormVIEw runat="server" DataSourceID="weatherDS">
<ItemTemplate>
<ASP:Label runat="server"
Text='<%# Eval("Temperature",
"當前溫度是 {0}。") %>' />
</ItemTemplate>
</ASP:FormVIEw>
<nk:WeatherDataSource runat="server" id="weatherDS">
<Parameters>
<ASP:ControlParameter Name="ZipCode" ControlID="zipCodeTextBox" />
</Parameters>
</nk:WeatherDataSource>
請注意,在標記中並未指定 Text 作為在 ControlParameter 標記上查找的屬性。ControlParameter 自動計算出了在未指定屬性的情況下要使用的默認屬性。它通過檢查該類中的 ControlValueAttribute 來實現此目的。TextBox 將 Text 定義為包含其“控件值”的屬性。除了傳統輸入控件之外,此概念還適用於多個控件。例如,GridVIEw 將其 SelectedDataKey 揭示為“控件值”。這是一個新事物,控件開發人員從此以後應該予以考慮,以便與 ControlParameter 更好地進行集成。