引言 在 Web 開發人員的最常見任務之中
有一項任務是他們要反復執行的
建立更新數據庫表的簡單窗體
我們將創建一個列表頁面和一個窗體頁面
列表頁面中以表格形式顯示記錄
窗體頁面中帶有用於各個數據庫字段的適當的窗體控件
許多開發人員還使用表示數據庫表的業務對象將代碼組織到分為多層的設計中
如果以業務對象 (Document) 來表示數據庫表 (Documents)
許多窗體的代碼看上去將如下所示
<script runat=
server
>
protected void Page_Load(Object Src
EventArgs E) {
if (!IsPostBack) {
Document document =
Documents
GetDocument(Request
QueryString[
DocumentID
]);
Title
Text = document
Title;
Active
Checked = document
Active;
CreatedDate
Text = document
CreatedDate
ToString();
AuthorID
FindByValue(document
AuthorID
ToString())
Selected =
true;
//
等等
HtmlBody
Text = document
HtmlBody;
}
}
protected void SaveButton_Click(Object Src
EventArgs E) {
Document document =
Documents
GetDocument(Request
QueryString[
DocumentID
]);
document
Title = Title
Text;
document
Active = Active
Checked;
document
CreatedDate = Convert
ToDateTime(CreatedDate
Text);
document
AuthorID = Convert
ToInt
(AuthorID
SelectedItem
Value);
//
等等
document
HtmlBody = HtmlBody
Text;
Documents
Update(document);
}
</script>
簡化和縮短窗體代碼 在以上代碼中
對每個控件進行顯式轉換
並將其設置為窗體控件的正確屬性
根據屬性和窗體控件的數量
這部分代碼可能會變長並難以管理
代碼還應包含類型轉換的錯誤更正和 ListControl
這將進一步增加復雜性
即使窗體是由代碼生成工具(例如 Eric J
Smith 的優秀的 CodeSmith)生成的
當需要任何自定義邏輯關系時
很容易引入錯誤
使用反射
可以僅使用單行代碼便將業務對象的所有屬性綁定到相應的窗體控件
從而減少代碼的行數並增強可讀性
完成反射系統的建立後
以上代碼將簡化為
protected void Page_Load(Object Src
EventArgs E) {
if (!IsPostBack) {
Document document =
Documents
GetDocument(Request
QueryString[
DocumentID
]);
FormBinding
BindObjectToControls(document);
}
}
protected void Save_Click(Object Src
EventArgs E) {
Document document =
Documents
GetDocument(Request
QueryString[
DocumentID
]);
FormBinding
BindControlsToObject(document);
Documents
Update(document);
}
此代碼可用於所有標准的 ASP
NET 控件(TextBox
DropDownList
CheckBox 等)和許多第三方控件(例如 Free TextBox 和 Calendar Popup)
無論有多少業務對象屬性和窗體控件
這一行代碼都能提供所需的全部功能
只要窗體控件的 ID 與業務對象屬性名相匹配
開始從反射中檢索屬性列表 首先
我們需要檢查業務對象的屬性
並查找與業務對象屬性名具有相同 ID 的 ASP
NET 控件
以下代碼構成了綁定查找的基礎
public class FormBinding {
public static void BindObjectToControls(object obj
Control container) {
if (obj == null) return;
Type objType = obj
GetType();
PropertyInfo[] objPropertiesArray =
objType
GetProperties();
foreach (PropertyInfo objProperty in objPropertiesArray) {
Control control =
container
FindControl(objProperty
Name);
if (control != null) {
// 處理控件
}
}
}
}
在以上代碼中
方法 BindObjectsToControls 接受了業務對象 obj 和一個容器控件
容器控件通常是當前 Web 窗體的 Page 對象
如果所用版本是會在運行時更改控件嵌套順序的 ASP
NET
x MasterPages
您將需要指定窗體控件所在的 Content 控件
這是在 ASP
NET
x 中
FindControl 方法對嵌套控件和命名容器的處理方式導致的
在以上代碼中
我們獲取了業務對象的 Type
然後使用該 Type 來獲取 PropertyInfo 對象的數組
每個 PropertyInfo 對象都包含關於業務對象屬性以及從業務對象獲取和設置值的能力的信息
我們使用 foreach 循環檢查具有與業務對象屬性名 (PropertyInfo
Name) 對應的 ID 屬性的 ASP
NET 控件的容器
如果找到控件
則嘗試將屬性值綁定到該控件
將對象屬性值綁定到控件 過程中的大部分操作是在此階段執行的
我們需要用對象的屬性值來填充找到的控件
一種實現方法是為每種控件類型創建一個 if
else 語句
派生自 ListControl(DropDownList
RadioButtonList
CheckBoxList 和 ListBox)的所有控件都具有可以統一訪問的公用接口
所以可以將它們編組在一起
如果找到的控件是 ListControl
我們可以將其作為 ListControl 進行轉換
然後設置選定項
Control control = container
FindControl(objProperty
Name);
if (control != null) {
if (control is ListControl) {
ListControl listControl = (ListControl) control;
string propertyValue = objProperty
GetValue(obj
null)
ToString();
ListItem listItem =
listControl
Items
FindByValue(propertyValue);
if (listItem != null) listItem
Selected = true;
} else {
// 處理其他控件類型
}
}
不幸的是
其他控件類型並不從父類中派生
以下幾個公用控件都具有
Text 字符串屬性
TextBox
Literal 和 Label
但該屬性不是從公用父類中派生出來的
所以需要分別轉換每種控件類型
我們還需要轉換其他控件類型
例如 Calendar 控件
以便使用適當的屬性(在 Calendar 的例子中
是 SelectedDate 屬性)
要包含所有標准的 ASP
NET 窗體控件
並訪問窗體控件的正確屬性並不需要太多的代碼行
if (control is ListControl) {
ListControl listControl = (ListControl) control;
string propertyValue = objProperty
GetValue(obj
null)
ToString();
ListItem listItem = listControl
Items
FindByValue(propertyValue);
if (listItem != null) listItem
Selected = true;
} else if (control is CheckBox) {
if (objProperty
PropertyType == typeof(bool))
((CheckBox) control)
Checked = (bool)
objProperty
GetValue(obj
null);
} else if (control is Calendar) {
if (objProperty
PropertyType == typeof(DateTime))
((Calendar) control)
SelectedDate = (DateTime)
objProperty
GetValue(obj
null);
} else if (control is TextBox) {
((TextBox) control)
Text = objProperty
GetValue(obj
null)
ToString();
} else if (control is Literal)(
//
等等
還可用於標簽等屬性
}
此方法完整地涵蓋了標准的 ASP
NET
x 控件
從這個角度來看
我們擁有了功能齊全的 BindObjectToControls 方法
但在起作用的同時
此方法的應用范圍會受到限制
因為它僅考慮內置的 ASP
NET
x 控件
如果要支持新的 ASP
NET
控件
或者要使用任何第三方控件
我們必須在 FormBinding 項目中引用控件的程序集
並將控件類型添加到 if
else 列表
此問題的解決方案是第二次使用反射
以查看各個控件的屬性
並找出控件是否具有與業務對象的屬性類型對應的屬性類型
用已知屬性設置未知控件的值 如上所述
有些控件共享字符串屬性
Text
大多數窗體控件以實質相同的方式使用此屬性
該屬性用於獲取和設置用戶輸入的數據
有大量控件還使用了其他一些公用屬性和屬性類型
以下是這些屬性中的一些
稱為
SelectedDate 的 DateTime 屬性
它在許多日歷和日期選取器控件中使用
稱為
Checked 的布爾屬性
它在布爾型控件中使用
稱為
Value 的字符串屬性
它常見於隱藏控件
這四個屬性(string Text
string Value
bool Checked 和 DateTime SelectedDate)是最常見的控件屬性
如果可以將系統設計成無論何種控件類型
都綁定到這些屬性
那麼我們的綁定方法將適用於使用那四個屬性的任何控件
在以下代碼中
我們將第二次使用反射(這一次是對窗體控件使用
而不是對業務對象使用)
以確定它是否具有任何常用屬性
如果有
則嘗試將業務對象的屬性值設置為控件的屬性
作為示例
我們將對整個 PropertyInfo 數組進行迭代
並查找稱為
Text 的字符串屬性
如果控件具有該屬性
則將數據從業務對象發送到該控件的屬性
if (control is ListControl) {
//
} else {
// 獲取控件的類型和屬性
//
Type controlType = control
GetType();
PropertyInfo[] controlPropertiesArray =
controlType
GetProperties();
// 查找
Text 屬性
//
foreach (Pro
From:http://tw.wingwit.com/Article/program/net/201311/13927.html