您所在的位置: 程序员家园 -> 家园博客 ->
 
在哪里摔倒
就在哪里自己爬起来

用户登录

查  找

最新评论

最新留言

常用网站

网易邮箱 GMAIL  

百度搜索 MSDN

霏凡软件 BT精品

影视帝国 射 手 网

电驴下载 全 库 网

友情连接

茄菲的窝 冰冰博客

枫叶飘零 玫  瑰

ACEN 云 豹 子

统  计



[cc]P70 第六章 开发复合控件
狼子 发表于 2007-6-6 14:47:00 阅读全文 | 回复(0) | 引用通告 | 编辑

P70

通常,复合控件组合了两个或者多个存在的服务器控件,其子控件可以是ASP.NET框架内置的服务器控件,也可以是自定义的服务器控件。

下面列出实现复合控件的必要步骤。

·控件类必须派生自System.Web.UI.WebControls.WebControl或者System.Web.UI.WebControls基类,并且实现System.Web.UI.INamingContainer接口。

·必须重写CreateChildControls方法来对子控件进行初始化、实例化,并将其添加到控件树中。

在这一章里,讲的是复合控件的四个东西,属性、控件呈现、事件和样式,就一个例子,做一个放在一行两列的table里的TextBox和Button组合成的服务器控件,因为事件有两种方法实现,包含法和冒泡法,所以分了两个程序,我一共按书上的做了三个控件

复合控件的属性和前面学的都一样,用的是getset访问器,就是,这里使用了一个EnsureChildControls()方法

P70

protected virtual void EnsureChildControls();确定服务器控件是否包含子控件。如果不包含,则创建子控件。该方法首先检查ChildControlsCreated属性的当前值,如果此值为假,则调用CreateChildControls方法。当ASP.NET需要确保已创建子控件时,它将调用该方法。大多数情况下,开发人员无需重写此方法。如果确实重写了此方法,可以按与其默认行为相似的方式来使用。

我做的第一个例子,CompositeControl.cs,在把做好的服务器控件拖入设计面板时,会出现

呈现控件时出错:发生了未处理的异常。未将对象引用设置到对象的实例

的错,根据上面的解释,我觉得应该是控件在一开始时,因为没有调用过CreateChildControls方法,所以子控件没有被加入控件树中,所以引用Text属性的_button就是null值,当我们使用set访问器设置Text属性时,EnsureChildControls方法会检测到子控件还没有生成,会自动调用CreateChildControls方法生成子控件,所以在设置了Text属性后,控件就可以在设计面板里正确呈现了,不会再出现null值的错误

所以我加入了构造函数,在构造函数里,直接调用ChildControlsCreated方法,就是在一开始把控件拖入设计面板时,就调用ChildControlsCreated方法把子控件加入控件树,加入构造函数后,上面的出错已经解决

复合控件的事件,我的理解就是要把对整个复合控件的事件触发,转到对里面子控件的事件触发,书上讲了两种方法:包含法、冒泡法

包含法对应的例子是也是CompositeControl.cs文件

P74
包含法的核心是,通过在子控件的事件处理程序中调用复合控件的顶层事件处理程序,以完成子控件的事件上传。在执行过程中,当引发子控件事件后,子控件的事件处理程序将自动调用相关顶层事件处理程序。

我是从例子上看着理解的,在程序里,先是加入一个事件委托

private static readonly object EventButtonClick = new object();

然后加入了一个对整个服务器控件的事件

//事件属性结构
        [Description("当点击按钮是触发")]
        public event EventHandler ButtonClick
        {
            add
            {
                Events.AddHandler(EventButtonClick, value);
            }
            remove
            {
                Events.RemoveHandler(EventButtonClick, value);
            }
        }

然后,实现这个事件

protected virtual void OnButtonClick(EventArgs e)
        {
            EventHandler buttonClickHandler = (EventHandler)Events[EventButtonClick];
            if (buttonClickHandler != null)
            {
                buttonClickHandler(this, e);
            }
        }

这三步,为整个复合控件添加了一个ButtonClick事件,这个事件就是顶层事件

在第四章P50 4.2.2捕获回传事件的实现那个例子里(user1/9/archives/2007/3876.html),是通过实现IPostBackEventHandler接口和在RaisePostBackEvent方法中调用OnClick方法来捕捉回传的事件的,当时那个例子,是简单的例子,那个button本身,也是在重写RenderContents方法是写的

output.Write("<input type=submit name=" + this.UniqueID + " value=请点击我 />");

这个控件不是服务器控件,所以他回传的事件,是要自己捕捉的,现在做的这个例子里,做的是复合控件,Button本身,是一个服务器控件,他自己是有一个回传的事件的,我想这个包含法就是指利用这个事件,调用上面加入的顶层事件的意思

例子里在重写CreateChildControls方法时,为子控件加入了单击事件

_button.Click += new EventHandler(this._button_Click);

加入的这个单击事件是的定义是这样的

protected void _button_Click(object sender, EventArgs e)
        {
            OnButtonClick(e);
        }

在单击子控件的时候,让这个子服务器控件自己回传事件,然后在调用顶层事件OnButtonClick

下面是CompositeControl.cs的完整代码

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;

namespace P072WebControlLibrary
{
    public class CompositeControl : WebControl,INamingContainer
    {
        //声明变量
        private Button _button;
        private TextBox _textBox;
        //定义事件委托对象
        private static readonly object EventButtonClick = new object();

        //定义属性Text,用于指定按钮上的文字
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("")]
        [Description("获取或设置显示在按钮上的文字")]
        public string Text
        {
            get
            {
                EnsureChildControls();
                return _button.Text;
            }

            set
            {
                EnsureChildControls();
                _button.Text = value;
            }
        }

        //加入构造函数,在此控件实例化的时候,就创建子控件
        //如果没有这个,Text属性如果不设置,在设计面板里会出现:呈现控件时出错:发生了未处理的异常。未将对象引用设置到对象的实例。
        public CompositeControl()
        {
            CreateChildControls();
        }

        //重写Controls属性
        public override ControlCollection Controls
        {
            get
            {
                EnsureChildControls();
                return base.Controls;
            }
        }

        //重写CreateChildControls方法,将子控件添加到复合控件中
        protected override void CreateChildControls()
        {
            Controls.Clear();
            _button = new Button();
            _textBox = new TextBox();
            _button.ID = "btn";
            //为提交按钮添加事件处理程序
            _button.Click += new EventHandler(this._button_Click);
            //把子控件加入控件树
            this.Controls.Add(_button);
            this.Controls.Add(_textBox);
        }

        //重写Render方法,呈现控件中其他的HTML代码
        protected override void Render(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Border, "0px");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "5px");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0px");
            writer.RenderBeginTag(HtmlTextWriterTag.Table);
            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            //把服务器控件的内容加入html输出流
            _textBox.RenderControl(writer);
            writer.RenderEndTag();
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _button.RenderControl(writer);
            writer.RenderEndTag();
            writer.RenderEndTag();
            writer.RenderEndTag();
        }

        //事件处理
        [Description("当点击按钮是触发")]
        public event EventHandler ButtonClick
        {
            add
            {
                Events.AddHandler(EventButtonClick, value);
            }
            remove
            {
                Events.RemoveHandler(EventButtonClick, value);
            }
        }

        protected virtual void OnButtonClick(EventArgs e)
        {
            EventHandler buttonClickHandler = (EventHandler)Events[EventButtonClick];
            if (buttonClickHandler != null)
            {
                buttonClickHandler(this, e);
            }
        }

        protected void _button_Click(object sender, EventArgs e)
        {
            OnButtonClick(e);
        }
    }
}

p78

冒泡法也称“事件冒泡”,其核心是合用ASP.NET框架提供的事件上传机制。这种机制允许子控件将事件沿其包含层次结构向上传播到合适的位置引发,并且允许将事件处理程序附加到原始控件以及公开冒泡的事件的控件上。

冒泡法的实现,使用Control基类中专门用于事件上传的两个方法OnBubbleEvent和RaiseBubbleEvent。

OnBubbleEvent方法用于确定子控件的事件是否沿复合控件层次结构向上传递,如果事件已被处理,则为true,否则为false,默认值为false。RaiseBubbleEvent方法 用于将所有事件源及其信息分配给控件的父级,并且不能被重写。若要处理或引发冒泡的事件,控件必须重写OnBubbleEvent方法。

这个例子的文件是WebCustomControlP79.cs,例子源码在P79,在这个例子里,同样是先为整个复合控件加入ButtonClick事件,使用EventButtonClick做事件委托,用OnButtonClick()方法实现

然后,这个例子在重写CreateChildControls()时,没有为子控件添加单击事件,而是给子控件设置CommandName属性

_button.CommandName = "ButtonClick";

最后,这个例子重写了OnBubbleEvent()方法,在这个方法里,引发OnButtonClick()方法

下面是WebCustomControlP79.cs文件的完整源码

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;

namespace P072WebControlLibrary
{
    public class WebCustomControlP79 : WebControl, INamingContainer
    {
        //声明变量
        private Button _button;
        private TextBox _textBox;
        //定义事件委托对象
        private static readonly object EventButtonClick = new object();

        //定义属性Text,用于指定按钮上的文字
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("")]
        [Description("获取或设置显示在按钮上的文字")]
        public string Text
        {
            get
            {
                EnsureChildControls();
                return _button.Text;
            }

            set
            {
                EnsureChildControls();
                _button.Text = value;
            }
        }

        //重写Controls属性
        public override ControlCollection Controls
        {
            get
            {
                EnsureChildControls();
                return base.Controls;
            }
        }

        //重写CreateChildControls方法,将子控件添加到复合控件中
        protected override void CreateChildControls()
        {
            Controls.Clear();
            _button = new Button();
            _textBox = new TextBox();
            _button.ID = "btn";
            //给子控件设置CommandName属性
            _button.CommandName = "ButtonClick";
            this.Controls.Add(_button);
            this.Controls.Add(_textBox);
        }

        //重写Render方法,呈现控件中其他的HTML代码
        protected override void Render(HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Border, "0px");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "5px");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0px");
            writer.RenderBeginTag(HtmlTextWriterTag.Table);
            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            //把服务器控件的内容加入html输出流
            _textBox.RenderControl(writer);
            writer.RenderEndTag();
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _button.RenderControl(writer);
            writer.RenderEndTag();
            writer.RenderEndTag();
            writer.RenderEndTag();
        }

        //事件处理
        [Description("当点击按钮是触发")]
        public event EventHandler ButtonClick
        {
            add
            {
                Events.AddHandler(EventButtonClick, value);
            }
            remove
            {
                Events.RemoveHandler(EventButtonClick, value);
            }
        }

        protected virtual void OnButtonClick(EventArgs e)
        {
            EventHandler buttonClickHandler = (EventHandler)Events[EventButtonClick];
            if (buttonClickHandler != null)
            {
                buttonClickHandler(this, e);
            }
        }

        protected override bool OnBubbleEvent(object sender, EventArgs e)
        {
            bool handled = false;
            if (e is CommandEventArgs)
            {
                CommandEventArgs ce = (CommandEventArgs)e;
                if (ce.CommandName == "ButtonClick")
                {
                    OnButtonClick(EventArgs.Empty);
                    handled = true;
                }
            }
            return handled;
        }
    }
}

这一章讲的最后一个问题就是6.4 复合控件的样式,在这一节里,讲了怎么给子控件添加样式,从例子上看好像很简单,就是添加变量,设置样式属性,在重写Render方法时,加入样式的处理,最后就是重写三个方法:LoadViewState、SaveViewState、TrackViewState,在重写这三个方法的时候,要为基类样式和每一个子控件的样式做设置

下面是WebCustomControlP82.cs的完整源码

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;

namespace P072WebControlLibrary
{
    [DefaultEvent("Button")]
    [DefaultProperty("Text")]
    public class WebCustomControlP82 : WebControl, INamingContainer
    {
        //声明变量
        private Button _button;
        private TextBox _textBox;
        //定义事件委托对象
        private static readonly object EventButtonClick = new object();
        //定义两个样式
        private Style _buttonStyle;
        private Style _textBoxStyle;

        //定义属性Text,用于指定按钮上的文字
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("")]
        [Description("获取或设置显示在按钮上的文字")]
        public string Text
        {
            get
            {
                EnsureChildControls();
                return _button.Text;
            }

            set
            {
                EnsureChildControls();
                _button.Text = value;
            }
        }

        //定义ButtonStyle属性
        [
        Category("Style"),
        Description("设置Button的样式属性"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerDefaultProperty)
        ]
        public virtual Style ButtonStyle
        {
            get
            {
                if (_buttonStyle == null)
                {
                    _buttonStyle = new Style();
                    if (IsTrackingViewState)
                    {
                        ((IStateManager)_buttonStyle).TrackViewState();
                    }
                }
                return _buttonStyle;
            }
        }

        //定义TextStyle属性
        [
        Category("Style"),
        Description("设置TextBox的样式属性"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerDefaultProperty)
        ]
        public virtual Style TextBoxStyle
        {
            get
            {
                if (_textBoxStyle == null)
                {
                    _textBoxStyle = new Style();
                    if (IsTrackingViewState)
                    {
                        ((IStateManager)_textBoxStyle).TrackViewState();
                    }
                }
                return _textBoxStyle;
            }
        }

        //重写Controls属性
        public override ControlCollection Controls
        {
            get
            {
                EnsureChildControls();
                return base.Controls;
            }
        }

        //重写CreateChildControls方法,将子控件添加到复合控件中
        protected override void CreateChildControls()
        {
            Controls.Clear();
            _button = new Button();
            _textBox = new TextBox();
            _button.ID = "btn";
            //给子控件设置CommandName属性
            _button.CommandName = "ButtonClick";
            this.Controls.Add(_button);
            this.Controls.Add(_textBox);
        }

        //重写Render方法,呈现控件中其他的HTML代码
        protected override void Render(HtmlTextWriter writer)
        {
            //把添加的样式加到html输出流
            AddAttributesToRender(writer);
            if (_textBoxStyle != null)
            {
                _textBox.ApplyStyle(TextBoxStyle);
            }
            if (_buttonStyle != null)
            {
                _button.ApplyStyle(ButtonStyle);
            }
            //输出控件
            writer.AddAttribute(HtmlTextWriterAttribute.Border, "0px");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "5px");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0px");
            writer.RenderBeginTag(HtmlTextWriterTag.Table);
            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            //把服务器控件的内容加入html输出流
            _textBox.RenderControl(writer);
            writer.RenderEndTag();
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            _button.RenderControl(writer);
            writer.RenderEndTag();
            writer.RenderEndTag();
            writer.RenderEndTag();
        }

        //事件处理
        [Description("当点击按钮是触发")]
        public event EventHandler ButtonClick
        {
            add
            {
                Events.AddHandler(EventButtonClick, value);
            }
            remove
            {
                Events.RemoveHandler(EventButtonClick, value);
            }
        }
        //定义OnButtonClick
        protected virtual void OnButtonClick(EventArgs e)
        {
            EventHandler buttonClickHandler = (EventHandler)Events[EventButtonClick];
            if (buttonClickHandler != null)
            {
                buttonClickHandler(this, e);
            }
        }
        //重写OnBubbleEvent,调用引发冒泡的事件的OnEventName方法
        protected override bool OnBubbleEvent(object sender, EventArgs e)
        {
            bool handled = false;
            if (e is CommandEventArgs)
            {
                CommandEventArgs ce = (CommandEventArgs)e;
                if (ce.CommandName == "ButtonClick")
                {
                    OnButtonClick(EventArgs.Empty);
                    handled = true;
                }
            }
            return handled;
        }

        //复杂样式属性的状态管理,重写三个相关方法
        protected override void LoadViewState(object savedState)
        {
            if (savedState == null)
            {
                base.LoadViewState(null);
                return;
            }
            if (savedState != null)
            {
                object[] myState = (object[])savedState;
                if (myState.Length != 3)
                {
                    //只有三个样式:基类的样式、_textBoxStyle、_buttonStyle
                    throw new ArgumentException("无效的ViewState");
                }
                base.LoadViewState(myState[0]);
                if (myState[1] != null)
                {
                    ((IStateManager)TextBoxStyle).LoadViewState(myState[1]);
                }
                if (myState[2] != null)
                {
                    ((IStateManager)ButtonStyle).LoadViewState(myState[2]);
                }
            }
        }
        protected override object SaveViewState()
        {
            object[] myState = new object[3];
            myState[0] = base.SaveViewState();
            myState[1] = (_textBoxStyle != null) ? ((IStateManager)_textBoxStyle).SaveViewState() : null;
            myState[2]=(_buttonStyle!=null)?((IStateManager)_buttonStyle).SaveViewState():null;
            for(int i=0;i<3;i++)
            {
                if(myState[i]!=null)
                {
                    return myState;
                }
            }
            return null;
        }
        protected override void TrackViewState()
        {
            base.TrackViewState();
            if (_buttonStyle != null)
            {
                ((IStateManager)_buttonStyle).TrackViewState();
            }
            if (_textBoxStyle != null)
            {
                ((IStateManager)_textBoxStyle).TrackViewState();
            }
        }
    }
}

上面三个自定义服务器控件的调用测试文件是这个P72_CompositeControl.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="P72_CompositeControl.aspx.cs" Inherits="P72_CompositeControl" %>
<%@ Register Assembly="P072WebControlLibrary" Namespace="P072WebControlLibrary" TagPrefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>无标题页</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <cc1:CompositeControl ID="CompositeControl1" runat="server" OnButtonClick="CompositeControl1_ButtonClick" Text="提交" />
        <br />
        &nbsp;<asp:Label ID="Label1" runat="server"></asp:Label><br />
        <br />
        下面这个是P79的例子,冒泡法上传事件<br />
        <cc1:WebCustomControlP79 ID="WebCustomControlP79_1" runat="server" OnButtonClick="WebCustomControlP79_1_ButtonClick"
            Text="提交" />
        <br />
        <br />
        下面是P82的例子,复杂样式<br />
        &nbsp;<cc1:WebCustomControlP82 ID="WebCustomControlP82_1" runat="server" ButtonStyle-Height="24px"
            ButtonStyle-Width="84px" Text="提交" TextBoxStyle-BorderWidth="1px" TextBoxStyle-Height="24px"
            TextBoxStyle-Width="198px">
            <ButtonStyle Height="24px" Width="84px" />
            <TextBoxStyle BorderWidth="1px" Height="24px" Width="198px" />
        </cc1:WebCustomControlP82>
    </div>
    </form>
</body>
</html>

P72_CompositeControl.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class P72_CompositeControl : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void CompositeControl1_ButtonClick(object sender, EventArgs e)
    {
        Label1.Text = "包含法上传事件";
    }
    protected void WebCustomControlP79_1_ButtonClick(object sender, EventArgs e)
    {
        Label1.Text = "冒泡法上传事件";
    }
}

发表评论:

    昵称:
    密码:
    主页:
    标题:
Powered by Oblog.