05 Aug 2007

自定义控件实现广告内容后期加载,以及NamingContainer层次的应用

自定义控件:广告内容后期加载。以及NamingContainer层次的应用
网站上的广告内容可能会因加载过慢而导致整个网页加载过慢
我们可以考虑将广告内全部放在网页最底部,等整个页面加载完毕後
再采用javascript使其显示,考虑给控件一个TargetContainerID标识
控件广告内容将要被显示的容器ID,然后从控件自己所处的 开始向上查找该ID
所指定的控件,(我们只向上找而没有向下找,并且没有处理某层次的
子NamingContainer,所以不一定能够找到,这里没有考虑从Page对象向下递归查找
主要考虑为了提高性能),如果没有找到,则考虑给用户一个事件让用户自己处理TargetContainer

下面的自定义控件对此实现了封装:

//------------------------------------------------------------------------------ // // Copyright (c) www.github.com All rights reserved. // //------------------------------------------------------------------------------ namespace HBZ.Ads.Controls { using System; using System.ComponentModel; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using HBZ.Ads; using System.Collections.Generic; using System.Text; [Designer( typeof( HBZ.Ads.Controls.AdRotatorDesigner ) )] public class AdRotator : WebControl { private Dictionary findControlHelperCacheList = new Dictionary( ); private readonly string scriptFormat = " var {0}_target = document.getElementById(\"{0}\"); var {1}_base = document.getElementById(\"{1}\"); if ({0}_target) {0}_target.innerHTML = {1}_base.innerHTML; if ({0}_target) {1}_base.innerHTML = \"\"; "; private static readonly object eventObj = new object( ); public event FindTargetContainerEventHandler FindTargetContainer { add { Events.AddHandler( eventObj , value ); } remove { Events.RemoveHandler( eventObj , value ); } } public AdRotator( ) : base( HtmlTextWriterTag.Span ) { } [TypeConverter( typeof( ValidatedControlConverter ) )] [DefaultValue( "" ) , Category( "Data" ) , Description( "广告位后期加载后显示的位置的控件ID" )] [IDReferenceProperty] public string TargetContainerID { get { return (string)this.ViewState[ "TargetContainerID" ] ?? string.Empty; } set { this.ViewState[ "TargetContainerID" ] = value; } } [Bindable( true ) , Category( "Data" ) , DefaultValue( "" ) , Description( "广告位的默认内容" )] public string DefaultContent { get { return (string)this.ViewState[ "DefaultContent" ] ?? "广告位招租"; } set { this.ViewState[ "DefaultContent" ] = value; } } protected override void OnPreRender( EventArgs e ) { base.OnPreRender( e ); if ( !string.IsNullOrEmpty( TargetContainerID ) ) { this.Style.Add( HtmlTextWriterStyle.Display , "none" ); ClientScriptManager cs = Page.ClientScript; if ( !cs.IsStartupScriptRegistered( this.ClientID ) ) { cs.RegisterStartupScript( this.GetType( ) , this.ClientID , GetLazyLoadingScript( ) , true ); } } } protected virtual string GetLazyLoadingScript( ) { Control target = FindControlHelper( TargetContainerID ); if ( target == null ) { throw new TargetContainerNotFoundException( ); } StringBuilder sb = new StringBuilder( ); sb.AppendFormat( scriptFormat , target.ClientID , this.ClientID ); return sb.ToString( ); } protected virtual void RenderAdvertisement( Advertisement ad , HtmlTextWriter writer ) { // 广告内容 } protected override void RenderContents( HtmlTextWriter writer ) { // call RenderAdvertisement method } protected Control FindControlHelper( string id ) { Control c = null; if ( findControlHelperCacheList.ContainsKey( id ) ) { c = findControlHelperCacheList[ id ]; } else { c = base.FindControl( id ); // 注意:我们从自己开始向上沿NamingContainer层次查找 Control nc = NamingContainer; while ( ( null == c ) && ( null != nc ) ) { c = nc.FindControl( id ); nc = nc.NamingContainer; } if ( null == c ) { // 因为我们是从自己开始向上沿NamingContainer层次查找,而没有向下找, // 并且没有找每一层NamingContainer内的其他NamingContainer, // 所以这种查找有可能出现没有找到id对应的控件 // 当没此时有找到时,激发FindTargetContainer事件交给用户自己设定Target Container Control FindTargetContainerEventArgs args = new FindTargetContainerEventArgs( id ); OnFindTargetContainer( args ); c = args.Control; } if ( null != c ) { findControlHelperCacheList[ id ] = c; } } return c; } protected virtual void OnFindTargetContainer( FindTargetContainerEventArgs e ) { FindTargetContainerEventHandler hander = Events[ eventObj ] as FindTargetContainerEventHandler; if ( hander != null ) { hander( this , e ); } } } public delegate void FindTargetContainerEventHandler( object src , FindTargetContainerEventArgs e ); public class FindTargetContainerEventArgs : EventArgs { private string controlID; private Control control; public FindTargetContainerEventArgs( string controlId ) { controlID = controlId; } public string ControlID { get { return controlID; } } public Control Control { get { return control; } set { control = value; } } } public class TargetContainerNotFoundException : Exception { string exceptionMessage = string.Empty; public TargetContainerNotFoundException( ) : this( "TargetContainerID所指定的控件没有找到!您或许应该处理一下FindTargetContainer事件" ) { } public TargetContainerNotFoundException( string message ) : base( message ) { this.exceptionMessage = message; } public override string Message { get { if ( exceptionMessage != null ) { return exceptionMessage; } return base.Message; } } } }

23 Jul 2007

在自定义控件中实现ICallbackEventHandler接口不经过回发而实现客户端回掉

 

在自定义控件中实现ICallbackEventHandler接口不经过回发而实现客户端回掉 
Asp.Net2.0中新增了ICallbackEventHandler接口,用于指示控件可以作器的回事件的目
MSDN中的描述:

实现 ICallbackEventHandler 接口的控件的示例包括 GridViewDetailsView TreeView 控件。当回事件以实现 ICallbackEventHandler 接口的控件标时,将把事件量作参数传递 RaiseCallbackEvent 方法以事件,并且GetCallbackResult 方法返回回

ICallbackEventHandler成员有:

 

 

名称

 


 

 

 

GetCallbackResult

 

返回以控件的回事件的果。

 

 

 

RaiseCallbackEvent

 

理以控件的回事件。

 

 
如下代码实现一个不经过回发而实现客户端回掉的CheckBox



//------------------------------------------------------------------------------<br />// <copyright company="Meibo Wu www.github.com"><br />// Copyright (c) www.github.com All rights reserved.<br />// </copyright><br />//------------------------------------------------------------------------------<br />using System;<br />using System.Drawing;<br />using System.Collections;<br />using System.ComponentModel;<br />using System.Web;<br />using System.Web.UI;<br />using System.Web.UI.WebControls;<br />namespace HBZ<br />{<br />/// <summary><br />/// A Asynchronous AutoPostback Checkbox Control<br />/// </summary><br />[DefaultEvent("CheckedChanged")]<br />[ControlValueProperty("Checked")]<br />[DefaultProperty("Text")]<br />public class AsynchronousCheckBox : WebControl, INamingContainer, ICallbackEventHandler<br />{<br />#region Delegates<br />/// <summary><br />/// The delegate for the checked changed event<br />/// </summary><br />///<br /><span name="sender" class="mceItemParam"></span></param><br />///<br /><span name="e" class="mceItemParam"></span></param><br />public delegate void CheckedChangedEventHander(object sender, CheckChangedEventArgs e);<br />#endregion<br />#region Events<br />private static readonly object eventCheckedChanged;<br />/// <summary><br />/// The checked changed event.<br />/// </summary><br />public event CheckedChangedEventHander CheckedChanged<br />{<br />add<br />{<br />Events.AddHandler(eventCheckedChanged, value);<br />}<br />remove<br />{<br />Events.RemoveHandler(eventCheckedChanged, value);<br />}<br />}<br />#endregion<br />#region Constructors<br />/// <summary><br />/// Static Constructor<br />/// </summary><br />static AsynchronousCheckBox()<br />{<br />eventCheckedChanged = new object();<br />}<br />/// <summary><br />/// Constructor<br />/// </summary><br />public AsynchronousCheckBox()<br />: base(HtmlTextWriterTag.Input)<br />{<br />}<br />#endregion<br />#region Properties<br />/// <summary><br />/// Gets or sets a value indicating whether the Lable Text<br />/// </summary><br />[Description("Gets or sets a value indicating whether the Lable Text")]<br />public virtual string Text<br />{<br />get<br />{<br />return (string)ViewState["Text"];<br />}<br />set<br />{<br />this.ViewState["Text"] = value;<br />}<br />}<br />/// <summary><br />/// Gets or sets a value indicating whether the 'Client CallBack Script Name'<br />/// </summary><br />[Description("Gets or sets a value indicating whether the 'Client CallBack Script function Name'")]<br />public string ClientCallBackScript<br />{<br />get<br />{<br />object o = ViewState["ClientCallBackScript"];<br />return o == null ? "null" : o.ToString();<br />}<br />set<br />{<br />ViewState["ClientCallBackScript"] = value;<br />}<br />}<br />/// <summary><br />/// Gets or sets a value indicating whether the checkbox 's checked<br />/// </summary><br />[Description("Gets or sets a value indicating whether the checkbox 's checked")]<br />public bool Checked<br />{<br />get<br />{<br />object o = ViewState["Checked"];<br />return o == null ? false : (bool)o;<br />}<br />set<br />{<br />ViewState["Checked"] = value;<br />}<br />}<br />/// <summary><br />/// Gets or sets a value indicating whether the Text 's cssClass<br />/// </summary><br />[Description("Gets or sets a value indicating whether the Text 's cssClass")]<br />public string TextCss<br />{<br />get<br />{<br />return (string)ViewState["TextCss"];<br />}<br />set<br />{<br />ViewState["TextCss"] = value;<br />}<br />}<br />/// <summary><br />/// Gets or sets a value indicating whether the Label 's position<br />/// </summary><br />public virtual TextAlign TextAlign<br />{<br />get<br />{<br />object o = ViewState["TextAlign"];<br />if (o != null)<br />{<br />return (TextAlign)o;<br />}<br />return TextAlign.Right;<br />}<br />set<br />{<br />if ((value <textalign.left) || (value > TextAlign.Right))<br />{<br />throw new ArgumentOutOfRangeException("value");<br />}<br />ViewState["TextAlign"] = value;<br />}<br />}<br />#endregion<br />#region Render Meghods<br />/// <summary><br />///<br />/// </summary><br />///<br /><span name="writer" class="mceItemParam"></span></param><br />protected override void Render(HtmlTextWriter writer)<br />{<br />if (TextAlign == TextAlign.Left)<br />{<br />RenderLabel(writer);<br />base.Render(writer);<br />}<br />else<br />{<br />base.Render(writer);<br />RenderLabel(writer);<br />}<br />}<br />/// <summary><br />/// Render Label<br />/// </summary><br />///<br /><span name="writer" class="mceItemParam"></span></param><br />private void RenderLabel(HtmlTextWriter writer)<br />{<br />if (string.IsNullOrEmpty(Text))<br />{<br />return;<br />}<br />writer.Write("<label");<br />writer.WriteAttribute("for", ClientID);<br />if (!string.IsNullOrEmpty(TextCss))<br />{<br />writer.WriteAttribute("class", TextCss);<br />}<br />writer.Write(">");<br />writer.Write(Text);<br />writer.WriteEndTag("label");<br />}<br />/// <summary><br />/// Override the AddAttributesToRender method<br />/// </summary><br />///<br /><span name="writer" class="mceItemParam"></span></param><br />protected override void AddAttributesToRender(HtmlTextWriter writer)<br />{<br />if (base.Page == null)<br />{<br />base.Page.VerifyRenderingInServerForm(this);<br />}<br />string callbackReference<br />= Page.ClientScript.GetCallbackEventReference(this, "this.checked", ClientCallBackScript, null);<br />writer.AddAttribute(HtmlTextWriterAttribute.Onclick, callbackReference);<br />writer.AddAttribute(HtmlTextWriterAttribute.Type, "checkbox");<br />if (Checked)<br />{<br />writer.AddAttribute(HtmlTextWriterAttribute.Checked, "checked");<br />}<br />if (!Enabled)<br />{<br />writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled");<br />}<br />if (!string.IsNullOrEmpty(ToolTip))<br />{<br />writer.AddAttribute(HtmlTextWriterAttribute.Title, ToolTip);<br />}<br />base.AddAttributesToRender(writer);<br />}<br />#endregion<br />#region On Checked Changed<br />/// <summary><br />/// Invoke the check changed event.<br />/// </summary><br />///<br /><span name="sender" class="mceItemParam"></span></param><br />///<br /><span name="e" class="mceItemParam"></span></param><br />protected virtual void OnCheckedChanged(object sender, CheckChangedEventArgs e)<br />{<br />CheckedChangedEventHander hander = Events[eventCheckedChanged] as CheckedChangedEventHander;<br />if (hander != null)<br />{<br />Checked = e.Checked;<br />hander(this, e);<br />}<br />}<br />#endregion<br />#region ICallbackEventHandler Members<br />/// <summary><br />/// Get the result of a client side callback.<br />/// </summary><br />/// <returns>The callback result string.</returns><br />public string GetCallbackResult()<br />{<br />return Checked.ToString();<br />}<br />/// <summary><br />/// Raise the client callback event<br />/// </summary><br />///<br /><span name="eventArgument" class="mceItemParam"></span>The event arguments.</param><br />public void RaiseCallbackEvent(string eventArgument)<br />{<br />bool isChecked = Boolean.Parse(eventArgument);<br />CheckChangedEventArgs args = new CheckChangedEventArgs(isChecked);<br />OnCheckedChanged(this, args);<br />}<br />#endregion<br />}<br />}<br />//------------------------------------------------------------------------------<br />// <copyright company="Meibo Wu www.github.com"><br />// Copyright (c) www.github.com All rights reserved.<br />// </copyright><br />//------------------------------------------------------------------------------<br />using System;<br />using System.Drawing;<br />using System.Collections;<br />using System.ComponentModel;<br />using System.Web;<br />using System.Web.UI;<br />using System.Web.UI.WebControls;<br />namespace HBZ<br />{<br />/// <summary><br />///<br />/// </summary><br />public class CheckChangedEventArgs:EventArgs<br />{<br />/// <summary><br />///<br />/// </summary><br />///<br /><span name="_isChecked" class="mceItemParam"></span></param><br />public CheckChangedEventArgs(bool _isChecked)<br />{<br />isChecked = _isChecked;<br />}<br />private bool isChecked = false;<br />/// <summary><br />///<br />/// </summary><br />public bool Checked<br />{<br />get<br />{<br />return isChecked;<br />}<br />}<br />}<br />}<br />
         http://www.github.com/private/5504/default.aspx

01 Dec 2006

在写自定义控件时,如果控件某个属性允许被验证控件验证,需要注意微软在 WebUIValidation.js内的实现。

微软在WebUIValidation.js(System.Web的资源文件内自己找工具看吧)
内有如下代码:

function ValidatorGetValue(id) {
    
var control;
    control 
= document.getElementById(id);
    
if (typeof(control.value) == "string") {
        
return control.value;
    }
    
return ValidatorGetValueRecursive(control);
}
function ValidatorGetValueRecursive(control)
{
    
if (typeof(control.value) == "string" && (control.type != "radio" || control.checked == true)) {
        
return control.value;
    }
    
var i, val;
    
for (i = 0; i<control.childNodes.length; i++) {
        val 
= ValidatorGetValueRecursive(control.childNodes[i]);
        
if (val != ""return val;
    }
    
return "";
}


当自定义控件为符合控件,比如两个或多个DropDownList组合而成
而有一个Value属性来返回这几个DropDownList的返回选项,并且设置了ValidationPropertyAttribute去验证某个属性,如:
[ValidationPropertyAttribute( "Value" )]
当使用验证控件验证的时候,由于上面脚本在控件没有客户端value的时候返回的为第一个value不为空的ChildNode的value
值,这样用来验证,可能不是我们所需要的。
比如我需要非空验证控件来验证我的自定义控件所有的DropDownList都必须被选择时候,如果第一个有了选择,则该脚本认为验证通过了,叶面可以PostBack然而,在服务器端Page.IsValidata属性是false的。
我用了一个非常ugly的方法,在控件外层放一个父容器(可以通过override TagKey 来指定)span
在每个DropDownList的客户端onchange的时候 判断 如果 任一个没有选择,那么置span的value = ‘’;
当全部选择的时候再将span.value = 实际的值 ,这样就不会出现客户端验证通过,然而服务器端验证没有通过才出来ErrorMessage的情况了。

但是总觉得 这样非常的ugly,如果有其他朋友也遇到过这样的问题,欢迎讨论。

30 Apr 2004

可以弹出确认对话框的自定义Web服务器控件ConfirmButton

经常在论坛里看到类似这样的问题:“如何在点击删除按钮的时候弹出个确认删除对话框”。

下面我们来自己写一个这样的自定义Web服务器控件!

思路如下:

继承System.Web.UI.WebControls.Button控件

增加一个属性“ConfirmMessage”来表示弹出确认框上面的提示信息。

在服务器控件呈现在页面之前把一段javascript写到页面

内容如下:

<script language="JavaScript">

<!--

function _doAspxBoyConfirm()

{

return confirm("你确认删除/保存吗??")

}

//-->

</script>

查一下msdn中对于Control.OnPreRender 方法的描述

可以得到“此方法通知服务器控件在保存视图状态和呈现内容之前,执行任何必要的预呈现步骤”.

所以我们只要在OnPreRender方法内 Page.RegisterClientScriptBlock把这段javascript发送到客户端,并且给Button. Attributes属性内添加一个“onclick”的客户端属性对应值为: "return _doAspxBoyConfirm()”.

详细情况可以查阅

ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpref/html/frlrfsystemwebuiwebcontrolswebcontrolclassattributestopic.htm

这样一个具有ConFirm功能的Button就基本上建立起来了。

新建一个测试该控件的工程

在工具箱上点右键选择“添加/移除项”,点击浏览选择编译好的dll文件,点击确定,你会发现ConFirmButton已经添加到工具箱内了

将其托到一个Aspx页面内 在属性设置内给ConfirmMessage值为你要的弹出框内容比如“确定删除吗?”,按F5运行。

当点该按钮时会弹出一个confirm对话框询问“确定删除吗?,如果点击确定则执行buttonButton_Click事件,如果点击取消则不执行。

你可以查看他生成的html代码,以加深对该控件工作原理的理解

完整的代码如下:

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.ComponentModel;

using System.Text;

 

namespace AspxBoy.Com.ConfirmButton

{

       /// <summary>

       /// Button点击时会弹出一个对话框要求确认

       /// </summary>

       public class ConfirmButton : System.Web.UI.WebControls.Button

       {

              private string _confirmMessage;

              /// <summary>

              /// 当客户端点击此Button时弹出的提示消息筐的内容

              /// </summary>

              public string ConfirmMessage

              {

                     get

                     {

                            return _confirmMessage;

                     }

 

                     set

                     {

                            _confirmMessage = value;

                     }

              }

 

              protected override void OnPreRender(System.EventArgs e)

              {

                     StringBuilder sb = new StringBuilder();

                     sb.Append("<script language=\"JavaScript\">");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("<!--");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("/*--------------------------------------------");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("ControlName:\t\tAspxBoy.Com.ConfirmButton");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("AuthorName:\t\t\tHuobazi,WuMeibo");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("CopyRight:\t\t\twww.github.Com");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("---------------------------------------------*/");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("function _doAspxBoyConfirm()");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("{");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("return confirm(\"");

                     sb.Append(ConfirmMessage);

                     sb.Append("\")");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("}");

                     sb.Append(System.Environment.NewLine);

                     sb.Append("//-->");

                     sb.Append("</script>");

                     Page.RegisterClientScriptBlock("_doAspxBoyConfirm",sb.ToString());

                     this.Attributes.Add("onclick","return _doAspxBoyConfirm()");

                     base.OnPreRender(e);

              }

              public override void RenderBeginTag(HtmlTextWriter writer)

              {

                     writer.WriteLine();

                     writer.Write("<!-------------------");

                     writer.Write("AspxBoy.Com.ConfirmButton Start");

                     writer.Write("\tAuthorName: \tHuobazi");

                     writer.WriteLine(" --------------------->");

                     writer.Write("<!-------------------- ");

                     writer.Write("Copyright:2004 Huobazi(www.github.com)");

                     writer.Write(" ---------------------");

                     writer.WriteLine(">");

                     base.RenderBeginTag(writer);

              }

              public override void RenderEndTag(HtmlTextWriter writer)

              {

                     base.RenderEndTag(writer);

                     writer.WriteLine();

                     writer.Write("<!------------------------------- ");

                     writer.Write("AspxBoy.Com.ConfirmButton  End");

                     writer.Write(" --------------------------------");

                     writer.WriteLine(">");

                     writer.WriteLine();

              }

       }

}