本系列文章中“常式1”和“常式2”講述了利用Visual Studio.NET2003中已有的WEB自訂控制項,通過繼承或複合一些簡單控制項產生自己需要的自訂控制項。這樣的控制項製作比較簡單,但是它的執行效率相對要低一些,所以如果我們不繼承已有的控制項那麼這個控制項該怎麼做呢?
下面作者通過執行個體向大家講述這種自寫控制項的編程方法。
(常式使用C#)
本常式實現一個TextBox,該TextBox對於輸入的字串進行檢驗,將半形單引號替換為全形單引號(半形單引號導致資料庫錯誤)。
控制項首先要繼承所有控制項的基類:System.Web.UI.Control,實現兩個介面:IStateManager(實現ViewState),IPostBackDataHandler(處理回傳資料),然後可以仿照System.Web.UI.WebControls.TextBox編寫一些常用的屬性和方法。因篇幅限制,本例只實現Text屬性。
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace Demo
{
/// <summary>
/// WebCustomControl1 的摘要說明。
/// </summary>
像前兩個例子一樣,先處理一下控制項設計時屬性。
[DefaultProperty("Text"),
ToolboxData("<{0}:DemoTextBox runat=server></{0}:DemoTextBox>")]
public class DemoTextBox : System.Web.UI.Control,IStateManager,IPostBackDataHandler
{
private StateBag _state;
private bool _marked;
下面就是我們要實現的屬性:Text
[Bindable(true),
Category("Appearance"),
DefaultValue("")]
public string Text
{
get
{
string _text = (string) ViewState["Text"];
return _text==null?"":_text;
}
set
{
string text = "";
text = value;
text = text.Replace("'","’");
ViewState["Text"] = text;
}
}
為了能實現檢視狀態就必須實現IStateManager介面
object IStateManager.SaveViewState()
{
object _stateState = null;
if( _state != null )
_stateState = ((IStateManager)_state).SaveViewState();
if ( _stateState == null )
return null;
return _stateState;
}
void IStateManager.TrackViewState()
{
_marked = true;
if( _state != null )
((IStateManager)_state).TrackViewState();
}
void IStateManager.LoadViewState( object state )
{
if( state != null )
{
object _newState = (object)state;
((IStateManager)ViewState).LoadViewState( _newState );
}
}
bool IStateManager.IsTrackingViewState
{
get
{
return _marked;
}
}
internal new StateBag ViewState //注意,這裡覆蓋基類的ViewState屬性
{
get
{
if( _state == null )
{
_state = new StateBag( true );
if( ((IStateManager)this).IsTrackingViewState )
((IStateManager)_state).TrackViewState();
}
return _state;
}
}
下面把控制項的表現輸出到頁面,其實System.Web.UI.WebControls.TextBox也是重新封裝了Input而已。
protected override void Render(HtmlTextWriter output)
{
string strOutput = "<Input name=\""+this.ClientID+"\" type=\"text\" value=\""+this.Text+"\">";
output.Write(strOutput);
}
#region IPostBackDataHandler 成員
public void RaisePostDataChangedEvent()
{
// TODO: 添加 DemoTextBox.RaisePostDataChangedEvent 實現
}
下面的方法很重要,把回傳的資料儲存。
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
// TODO: 添加 DemoTextBox.LoadPostData 實現
string presentValue = this.Text;
string postedValue = postCollection[postDataKey];
if (!presentValue.Equals(postedValue))//如果回傳資料不等於原有資料
{
this.Text = postedValue;
return true;
}
return false;
}
#endregion
}
}
好了,一個自己寫的TextBox控制項完成了,現在把編譯好的dll檔案找到並添加到工具箱中就可以拖放到頁面上了。本例實現的ViewState處理是一個示範,協助大家瞭解自己實現ViewState的方法,繼承的Control類已經實現了該處理,實際中只需要實現IPostBackDataHandler就可以了,ViewState的問題控制項自己就解決了。
控制項產生,但是當拖放到頁面上的時候它的設計時的顯示不太友好,只是一串文字。那麼如何像System.Web.UI.WebControls.TextBox那樣拖上去後顯示為一個輸入框呢?
下面我為讀者介紹改變顯示的方法。
首先在控制項項目中添加一個類檔案,命名為:Designer.cs。然後修改為如下代碼:
using System;
using System.Web.UI.Design;
using System.ComponentModel;
namespace Demo
{
/// <summary>
/// Designer 的摘要說明。
/// </summary>
public class DemoDesigner : ControlDesigner//繼承ControlDesigner類
{
private DemoTextBox demoTextBox;//聲明一個控制項類對象
public override void Initialize(IComponent component)
{
this.demoTextBox = (DemoTextBox)component;
base.Initialize(component);
}
//重載函數GetDesignTimeHtml()
public override string GetDesignTimeHtml()
{
string _html = "";
_html = "<Input type=\"text\" value="+this.demoTextBox.Text+">";
return _html;
}
}
}
設計器完成了,但是還不能使控制項擁有設計時顯示,還要在控制項類的屬性代碼中添加Designer("Demo.DemoDesigner"),使控制項和設計器關聯。屬性代碼如下所示:
[DefaultProperty("Text"),
Designer("Demo.DemoDesigner"),
ToolboxData("<{0}:DemoTextBox runat=server></{0}:DemoTextBox>")]
現在這個控制項基本完成了,編譯後再拖到頁面上你會發現顯示的樣子與TextBox一樣了。
以這個代碼為架構,讀者可以按自己的需要擴充控制項的屬性。