开发高级 Web 部件
?????? 通過用戶控件實現 Web 部件相當容易,但是也有一些弊端:
- 受限的重用:如果不手動復制 .ascx 文件到其他 Web 應用程序的目錄下,就不能動態添加這些控件到其他 Web 應用程序的頁面中。
- 受限的個性化:用戶控件的個性化僅限于常用的屬性,如標題、標題 URL 等。你不能獲取用戶控件的自定義屬性,該屬性保存在個性化存儲里,只有從 Web 部件派生而來的類才能有這類功能。
- 更好的呈現和行為控制:當使用自定義服務器控件時,你對呈現過程有更好的控制,而且可以更加動態的生成用戶界面。
?????? 因此,有時候需要將高級 Web 部件實現為從 System.Web.UI.WebControls.WebParts.WebPart 派生而來的服務器控件。
?
?????? 創建一個 Web 部件的步驟如下:
1. 開始之前,創建強類型的 DataSet
?????? 在開始深入到開發 Web 部件的細節之前,必須為方便的訪問存在數據庫中的數據添加幾個特別的組件,這些組件將用來完成本文的代碼示例。右擊項目添加一個 DataSet,命名為 CustomerSet,拖曳 Customer 和 CustomerNotes 表至 DataSet 的設計界面上。如下圖:
?????? 兩個強類型的 DataSet 擴展了 DataSet 類并提供了強類型的表適配器。右擊 CustomerNotes 表,添加一個查詢用于獲取某位客戶的注解,該查詢有一個參數 CustomerId。
?????? 總之,應該總是在開始創建實際的用戶界面組件之前創建業務層和數據訪問層,當然,業務層和數據訪問層是可以跨不同應用程序的,就像這個強類型的 DataSet 一樣。
?
2. 自定義 WebPart 的骨架
?????? 創建一個繼承自 WebPart 的自定義類。引入命名空間 System.Web.UI.WebControls.WebParts 可以方便的訪問 Web Parts Framework。給 Web 部件添加一些屬性。對類里的每一個屬性過程,都可以指定這個屬性是對每個用戶個性化還是被所有用戶共享,以及這個屬性能否被用戶訪問。
namespace Apress.WebParts.Samples { public class CustomerNotesPart : WebPart { public CustomerNotesPart() { } ? [WebBrowsable(true)] [Personalizable(PersonalizationScope.User)] public string Customer { get; set; } } }?????? WebBrowsable 特性指定了這個屬性對終端用戶可見。Personalizable 特性指定了該屬性的個性范圍是基于每個用戶的。
?
3. 初始化 Web 部件
?????? 你可以選擇性的創建子控件,就像創建一個組合 Web 部件時所做的那樣。如果不想在 RenderContents()方法內使用預置的控件,你可以自己呈現 Web 部件。但是,使用組合控件可以讓事情變得簡單很多,因為你不必擔心 HTML 細節。
?????? 要創建控件,必須覆蓋 CreateChildControls()方法,如下所示:
private TextBox NewNoteText; private Button InsertNewNote; private GridView CustomerNotesGrid; ? protected override void CreateChildControls() { NewNoteText = new TextBox(); ? InsertNewNote = new Button(); InsertNewNote.Text = "Insert..."; InsertNewNote.Click += InsertNewNote_Click; ? CustomerNotesGrid = new GridView(); CustomerNotesGrid.HeaderStyle.BackColor = System.Drawing.Color.LightBlue; CustomerNotesGrid.RowStyle.BackColor = System.Drawing.Color.LightGreen; CustomerNotesGrid.AlternatingRowStyle.BackColor = System.Drawing.Color.LightGray; CustomerNotesGrid.AllowPaging = true; CustomerNotesGrid.PageSize = 5; CustomerNotesGrid.PageIndexChanging += CustomerNotesGrid_PageIndexChanging; ? Controls.Add(NewNoteText); Controls.Add(InsertNewNote); Controls.Add(CustomerNotesGrid); } ? void CustomerNotesGrid_PageIndexChanging(object sender, GridViewPageEventArgs e) { // Insert Page Change Logic // ... } ? void InsertNewNote_Click(object sender, EventArgs e) { // Insert new note here // ... }?
4. 加載數據和處理事件??????
?????? 這個 Web 部件的生命周期里的下一個階段是加載階段。此時,可以連接到數據庫加載數據到控件里。要完成這些,必須覆蓋 OnInit()和 OnLoad()方法,或者捕獲 Web 部件的 Init 和 Load 事件。兩種做法效果相同。但是當覆蓋 OnLoad() 時,需要調用 Base.OnLoad()以便基類的加載功能也被執行。因此,有必要建立一次事件處理程序并捕獲自定義控件的事件,以便你不會忘記這點:
// Add some method into the control's constructor public CustomerNotesPart() { this.Init += new EventHandler(CustomerNotesPart_Init); this.Load += new EventHandler(CustomerNotesPart_Load); this.PreRender += new EventHandler(CustomerNotesPart_PreRender); }?
?????? 現在,先編寫從數據庫中加載數據的功能,然后在事件里調用它。引入 CustomerSetTableAdapters 命名空間編碼會簡短一些,這個命名空間是用原來設計的強類型的 DataSet 創建的。
void BindGrid() { // 確定服務器控件是否包含子控件。如果不包含,則創建子控件。 ? CustomerNotesTableAdapter adapter = new CustomerNotesTableAdapter(); if (Customer.Equals(string.Empty)) { CustomerNotesGrid.DataSource = adapter.GetData(); } else { CustomerNotesGrid.DataSource = adapter.GetDataByCustomer(Customer); } } ? void CustomerNotesPart_Load(object sender, EventArgs e) { // Initialize web part properties this.Title = "Customer Notes"; this.TitleIconImageUrl = "NotesImage.jpg"; } ? void CustomerNotesPart_Init(object sender, EventArgs e) { // Don't try to load data in design mode if (!this.DesignMode) { BindGrid(); } }????? 一定要記得調用 EnsureChildControls()方法,因為你并不確定 ASP.NET 什么時候實際調用 CreateChildControls()來創建子控件,ASP.NET 會在需要的時候創建這些控件。因此,你需要確保在這個方法里的控件可用。
?
?????? 現在,數據已經加載到網格了。在生命周期的下一階段,ASP.NET 運行庫處理事件。你的自定義 Web 部件必須捕獲之前添加的提交新注解到數據庫的 InertNewNote 按鈕的事件和改變頁面的 CustomerNotesGrid 的事件:
void CustomerNotesGrid_PageIndexChanging(object sender, GridViewPageEventArgs e) { CustomerNotesGrid.PageIndex = e.NewPageIndex; } ? void InsertNewNote_Click(object sender, EventArgs e) { CustomerNotesTableAdapter adapter = new CustomerNotesTableAdapter(); ? // if NoteID is an identity column, you needn't insert it. adapter.Insert(Customer, DateTime.Now, NewNoteText.Text); ? // Refresh the grid with the new row as well BindGrid(); }?
?????? 最后,必須在代碼的另一個地方也加載數據到 GridView。一旦有人修改了 Customer 屬性的值,Web 部件就要顯示與這個新選擇的客戶相關的信息:
private string _Customer; [WebBrowsable(true)] [Personalizable(PersonalizationScope.User)] public string Customer { get { return _Customer; } set { _Customer = value; if (!this.DesignMode) { EnsureChildControls(); CustomerNotesGrid.PageIndex = 0; CustomerNotesGrid.SelectedIndex = -1; BindGrid(); } } }?
5. 最后的呈現
?????? 到此為止,已經初始化了 Web 部件,創建了控件,編寫了加載數據的代碼,并且捕獲了控件事件。那么,該呈現 Web 部件了。在呈現之前,你可以在控件上設置影響最后呈現的最后的屬性。比如,如果用戶還沒有初始化 Customer 屬性,你應該禁用 InsertNewNote 按鈕。
?????? GridView 現在可以創建必需的 HTML 控件來顯示其所綁定的數據了(數據的加載之前的事件中已經完成了)。要完成這些,需要調用 DataBind()方法:
void CustomerNotesPart_PreRender(object sender, EventArgs e) { if (Customer.Equals(string.Empty)) { InsertNewNote.Enabled = false; } else { InsertNewNote.Enabled = true; } ? CustomerNotesGrid.DataBind(); }?
?????? 在 RenderContents()里,可以創建 HTML 代碼來設置 Web 部件的布局。如果不覆蓋這個方法,Web 部件將按照控件在 CreateChildControls()方法里被插入到 Web 部件的 Controls 集合中的順序來自動呈現。所以,你要覆蓋這個方法來創建一個更好的,美觀的,基于表的布局:
protected override void RenderContents(System.Web.UI.HtmlTextWriter writer) { writer.Write("<table>"); ? writer.Write("<tr>"); writer.Write("<td>"); NewNoteText.RenderControl(writer); InsertNewNote.RenderControl(writer); writer.Write(""); writer.Write("</tr>"); writer.Write("<tr>"); writer.Write("<td>"); CustomerNotesGrid.RenderControl(writer); writer.Write(""); writer.Write("</tr>"); ? writer.Write(""); }?
6. 更多定制步驟
?????? 如前所述,使用 IWebPart 接口,實現的自定義 Web 部件可以覆蓋屬性,如標題和描述。此外,你還可以通過為其賦值(最好在 Load 里)來為 Web 部件的其他屬性指定默認值。甚至可以覆蓋來自 Web 部件的默認屬性和方法的實現:
public override bool AllowClose { get { return false; ; } set { // Don't want this to be set. } }?
?????? 這樣,已經創建了一個 Web 部件,其中調用者不能通過外部的屬性設置來覆蓋其行為。你已經完成了對 Web 部件哪些能做、哪些不能做的定制。
?
7. 使用 Web 部件
?????? 在 Web 部件頁面的頂部使用 <%@ Register%> 指令注冊 Web 部件:
<%@ Register TagPrefix="apress" Namespace="Apress.WebParts.Samples" %>?????? 接著,在 WebPartZone 控件里使用該 Web 部件:
<asp:WebPartZone runat="server" ID="MainZone" > <zonetemplate> <uc1:Customers ID="MyCustomers" runat="server" OnLoad="MyCustomers_Load" /> <apress:CustomerNotesPart ID="MyCustomerNotes" runat="server" /> </zonetemplate> </asp:WebPartZone>?????? 現在調試前一篇的示例,效果如下:
總結
以上是生活随笔為你收集整理的开发高级 Web 部件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GLSL学习笔记
- 下一篇: hwclock: Open of /de