学习制作服务器控件(学习中,请高手指教)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Security.Permissions;

namespace ServerControl
{
    /// <summary>
    /// 如果用户名出现在用户浏览器发送的标题中,WelcomeLabel 就会将用户名追加到该文本字符串。
    /// </summary>
    //[AspNetHostingPermission(SecurityAction.Demand,Level=AspNetHostingPermissionLevel.Minimal)]
    //[AspNetHostingPermission(SecurityAction.InheritanceDemand,Level=AspNetHostingPermissionLevel.Minimal)]
    [ParseChildren(true,"Text")]
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:ServerControlXin runat=\"server\"></{0}:ServerControlXin>")]
    public class ServerControlXin : WebControl
    {
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("第一个控件")]
        [Description("服务器控件")]
        [Localizable(true)]
        public string Text
        {
            get
            {
                string s = (String)ViewState["Text"];
                return ((s == null) ? string.Empty : s);
            }

            set
            {
                ViewState["Text"] = value;
            }
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            //  对Text经行编码
            writer.WriteEncodedText(Text);
            
            //  用HemlTextWriter中的Write方法将内容写入流
            writer.Write("欣,忘不了的,是你眸光如水,浅溺着我一世的欢悲。。。");

            writer.Write(Text);
        }
    }
}

Tip:
1. [ParseChildren(true,"Text")]和ToolboxData标记需配合使用,不然即使在ToolboxData中定义了默认样式,也不能显示为双标记。
2.在将生成的dll文件添加到Bin和工具箱之后,如果从新生成了项目,则需要重新引用添加工具箱中,不然又是单标记,原因不知道。
3.[ParseChildren(false,"Text")]为false时,标记之间的文本不显示


ViewState:
工作原理:当处理请求后,页面框架会收集所有空间树的状态并创建一个对象图,ViewState字典也在其中,然后把对象图序列化为一个字符串作为
一个隐含变量发到客户端,当浏览器将网页回传给服务器时,页面框架读取隐藏信息并进行并行化,然后再一一对应的写入控件中的ViewState字典中。

可以存储的类型:ViewState中可以存储Int32,Boolean,String,Unit和Color,还为Arrary和ArraryList和以上类型的HashTable的对象而优化。如果不是
以上类型,则通过类型转换器进行转换成一个字符串,如果没有转换器,.net框架会用二进制进行转化,但代价很昂贵。

简单属性:可以很容易的转换为文本型(字符串)表达式的属性。
复杂属性:非数值类型和非字符串的引用类型和集合类型。
声明持久性:声明性语法。

Control中的Rendering方法:
Control中按顺序调用三个方法:
每个页面都有一个控件树,为了生成控件树,页面将建一个HtmlText类的实例,这个实例中封装了一个响应流,然后将这个对象传递给RenderControl方法,
这个方法将验证控件的Visible的值,来判断是否要显示,要显示的话,则继续按顺序调用其他两个方法,以递归的方式创建所有Visible为true的控件。

        public void RenderControl(HtmlTextWriter writer)
        {
            if (Visible)
            {
                Render(writer);
            }
        }

        protected virtual void Render(HtmlTextWriter writer)
        {
            RenderChildren(writer);
        }

        protected virtual void RenderChildren(HtmlTextWriter writer)
        {
            foreach (Control c in Controls)
            {
                c.RenderControl();
            }
        }


控件的生命周期:个人理解,当服务器控件在提交后,会回到初始状态,在服务器回发之后再进行恢复
1.实例化:页面或另一个控件通过调用它的构造器将它实例化,页面根据.aspx页面的声明语法建造了控件树,后续的步骤只有在控件加入到这个控件树中才会发生。
2.初始化:控件树中的页面和所有控件通过默认方式来调用OnInit方法。
3.开始跟踪视图状态:此阶段发生在初始化的末尾,页面自动调用控件的TrackViewState方法,该方法确保在这个生命周期结束的时候,使用ViewState保存的属性
会保存在控件的视图状态中。
4.加载视图状态(仅用于回传过程):在该阶段,页面框架自动加载ViewState字段,一般情况下不必在这里实现逻辑。
5.加载回传数据选项(仅用于回传过程,可选项):只有控件实现了IPostBackDataHandler接口参与了回传数据时,才会发生。对应方法LoadPostData。
6.加载:在此阶段以前,控件已经回到了上一个生命周期的最后状态,对应的重载方法——OnLoad,如果仅要实现初始化时的逻辑,那么if(IsPostBack==false){........}
7.引发修改事件(仅用于回传过程,可选项):只有控件实现了IPostBackDataHandler接口参与了回传数据时,才会发生。控件状态由回发而引起改变,如:TextBox。
8.引发回传事件(仅用于回传过程,可选项):只有控件实现了IPostBackDataHandler接口参与了回传数据时,才会发生,通过实现接口的RaiscPostBackEvent方法,
实现逻辑,目的,把客户端事件映射到服务器端事件。如:Button。
9.预生成(PreRender):在此阶段中,通过执行OnPreRender方法,将执行在生成控件之前的所需的任何操作。
10.保存视图状态:如果要自定义状态管理,则在此阶段重载SaveViewState方法,且只对EnableViewState为true的有效,在此阶段后,控件的任何改变将不做保存。
11.生成(Render):在输出流上通过重载Control的Render方法或者WebControl的Rendering方法中的一个绘制界面
12.卸载(UnLond):此阶段中,页面通过调用Page_UnLoad,来进行清除工作。在控件树中的页面和控件都会默认引发UnLoad事件。
13.释放(Dispose):重载Dispose方法释放占用的控件。

优化事件实现:使用事件属性去替代私有域
因为1.原来的如:public event EventHandler Click这种,编译器会生成一个私有域,而且每一个事件都声明一个,及为浪费内存。2.在编译器产生的添加和移除方法
时会为线程的安全做出同位标记,也就是说在从事件中添加或删除委托时,线程会加锁,这会增加系统的开销。
事件属性的实现:
1.声明

        //  私有静态的只读事件对象
        private static readonly object EventClickNext = new object();
        //  事件属性
        public event EventHandler ClickNext
        {
            add
            {
                Events.AddHandler(EventClickNext, value);
            }
            remove
            {
                Events.RemoveHandler(EventClickPrevious, value);
            }
        }

2.使用

        /// <summary>
        /// 回传处理
        /// </summary>
        /// <param name="eventArgument"></param>
        void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
        {
            //  eventArgument接收页面_EVENTARGUMENT隐藏变量的值,
            //  就是Page.GetPostBackEventReference中的第二个参数
            if (eventArgument == "Previous")
            {
                OnPreviousClick(EventArgs.Empty);
            }
            else if (eventArgument == "Next")
            {
                OnNextClick(EventArgs.Empty);
            }
        }
        /// <summary>
        /// 前一按钮窗口
        /// </summary>
        /// <param name="eventArgument"></param>
        private void OnNextClick(EventArgs eventArgs)
        {
            EventHandler nextClick = (EventHandler)Events[EventClickNext];
            if (nextClick != null)
            {
                nextClick(this, eventArgs);
            }
        }

Tip:
在ReaderContent中和AddAttributesToReader中都可以为控件加属性/事件方法,例如:

        /// <summary>
        /// 添加客户端样式
        /// </summary>
        /// <param name="writer">输出流</param>
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            base.AddAttributesToRender(writer);
            writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "30");
            writer.AddAttribute("onmouseover", "this.style.textDecoration='underline';status='" + StatusBarText + "';");
            writer.AddAttribute("onmouseout", "this.style.textDecoration='none';status='';",false);
        }
在客户端生成:
οnmοuseοver="this.style.textDecoration='underline';status='好男儿当战死沙场,马革裹尸,搏一个封妻荫子。。。';">

在控件中直接执行js脚本,则需加上"javascript:",例如:

protected override void RenderContents(HtmlTextWriter writer)
        {
            //  CheckBox
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "checkbox");
            writer.AddAttribute(HtmlTextWriterAttribute.Onclick, "javascript:alert('AAAAA');");
            writer.RenderBeginTag(HtmlTextWriterTag.Input);
            writer.RenderEndTag();
            //  TextBox

            base.RenderContents(writer);
        }

 

复杂属性

子属性:如TextBox中的属性
例如Font属性中有Font-Size,Font-Color,Font-Name等,Font为FontInfo类型,这个类中有Size,Color,Name等属性。

public class WebControl : Control
    {
        //  FontInfo是一个类,类中有Size,Name,Color等属性
        //  进入子属性,并将子属性串行化
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        //  标记当前属性是否修改
        [NotifyParentProperty(true)]
        public FontInfo Font
        {
            
        }
}

//  类型转换器,ExpandableObjectConverter——折叠属性
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public sealed class FontInfo
    {
        //  改变时标记当前属性
        [NotifyParentProperty(true)]
        public string Name { }
    }

内部属性:如DataGrid中的HeaderStyle

        <asp:DataGrid runat="server">
            <HeaderStyle ForeColor=""/>
        </asp:DataGrid>
必备属性标识:
[ParseChildren(true)]:告诉页面解析器把控制标签内的内容解析成属性。
[PersistChildren(false)]:告诉页面解析器把内部内容理解为属性而不是子控件。
当控件继承WebControl时可省略
//  串行化内容
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        //  标记属性改变
        [NotifyParentProperty(true)]
        //  将属性持久保存为内部属性
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public virtual TableItemStyle HeaderStyle { }

CanConvertFrom:将string类型转换为xxxx。
CanConvertTo:将string类型转换为xxxx。
ConvertFrom:根据传入的string创建并返回xxxx对象实例。
ConvertTo:根据接收xxx参数,转换为string。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值