Clingingboy

<developer name=’'clingingboy">
<i:Interaction.Behaviors>
<clingingboy:madeControlBehavior />
</i:Interaction.Behaviors>
</developer>

博客园 首页 新随笔 联系 订阅 管理
  211 Posts :: 1 Stories :: 1439 Comments :: 484 Trackbacks
      上一篇:http://www.cnblogs.com/Clingingboy/archive/2006/08/01/465397.html
上次讲了在继承Control类的时候为什么需要重写Render方法

本次来介绍控件的事件处理. 我们知道Button控件有OnClick事件,DropDownList控件有SelectedIndexChanged事件.

一.回发事件和客户端回发

下面来看一个最简单的例子

按钮单击事件
1 protected void Button1_Click(object sender, EventArgs e)
2     {
3         Label1.Text = "你好: "+TextBox1.Text;
4     }

大家知道Web 服务器控件创建的按钮的类型有三种

1.Button
2.LinkButton
3.ImageButton

打开MSDN看到三个控件都继承IPostBackEventHandler接口



IPostBackEventHandler接口
专门定义了处理回发事件的方法,说白了就是onclick事件,如果自定义控件需要处理回发事件,你就需要继承IPostBackEventHandler接口,然后实现接口的RaisePostBackEvent 方法,另外一个简单的方法就是直接继承Button控件就可以了.

RaisePostBackEvent方法用于处理窗体发送给服务器时引发的事件,方法中有一个参数eventArgument 表示要传递到事件处理程序的可选事件参数的 String

下面总结处理回发事件,必须要做的步骤

(1)继承并
实现IPostBackEventHandler接口的RaisePostBackEvent方法

(2)为表单元素定义UniqueID,以与IPostBackEventHandler服务器控件的UniqueID相对应

相应实现代码如下

示例一

如果你不熟悉委托的话,可以参考一篇叫一个C#睡前故事的文章

EventArgs.Empty表示没有事件数据的事件,不要跟我以前一样认为是一个空的事件,当时就很郁闷,干什么要触发空事件呢,都是因为没看清楚Empty字段的意思,以为就为空的意思了.

EventArgs.Empty等同于EventArgs类的构造函数,等同于new EventArgs()
注意还在呈现控件的name属性加了UniqueID.
好了,现在你可以测试下了.

1protected void SuperButton1_1_Click(object sender, EventArgs e)
2    {
3        Label1.Text = "你点击了此按钮";
4    }


这样你就成功定义了一个处理回发事件的控件. 假设你在页面上多次使用这个控件,编译器将为每个事件委托实例生成一个字段。如果事件的数目很大,则一个委托一个字段的存储成本可能无法接受。.所以推荐采用另外一种优化的事件实现

EventHandlerList 类提供一个简单的委托列表来添加和删除委托,下面来看看更改后的代码,

AddHandler有两个参数事件对象和添加的委托,在OnClick事件中必须显示将委托转换为EventHandler类型

示例二

下面再来说下客户端回发事件,在HTML窗体元素中只有Button按钮和ImageButton才可以引起窗体回发.

但如LinkButton链接按钮控件要希望启动回发的话,则要依赖客户端脚本的事件机制来实现其功能.

在asp.net2.0中,button控件多了一个UseSubmitBehavior 属性,指示 Button 控件使用客户端浏览器的提交机制(客户端回发)还是 ASP.NET 回发机制,默认采用回发机制,如果设置为false的话,则需要调用GetPostBackEventReference 方法来返回 Button 的客户端回发事件

当设置UseSubmitBehavior 属性为flase时,你运行页面时,则会发现一段自动生成的javascript代码

LinkButton也一样,再看下面例子,定义了枚举,定义button按钮和链接按钮,大家在测试的时候,打开源代码就会发现不同效果
示例三


如果大家本来就学过这方面的知识,看了心里还有谱,如果没有的话,里面有些方法不熟悉的话,还是要多看看MSDN. 说通俗点,回发事件可以就理解为按钮单击事件,而按钮又分两种不同的回发事件方法,这样讲的话,更容易让人接受,而上面所讲的就是实现按钮单击事件实现的方法.

二.数据回发事件

好了,接着再讲数据回发.跟上面讲的事件回发有点不同.

下面也举一个简单的例子,看下图,有两个DropDownList,一个开启AutoPostBack,一个没有开启,再接着看下面简单的代码,第一个DropDownList,改变下拉框值时,label没显示,按确定按钮后则显示label,第二个DropDownList改变下拉框值时就显示了label,因为开启了AutoPostBack.这个大家都明白吧.



 protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
    
{
        Label2.Text 
= "你选择了:  " + DropDownList1.SelectedItem.Text;
    }

    
protected void DropDownList2_SelectedIndexChanged(object sender, EventArgs e)
    
{
        Label1.Text 
= "你选择了:  " + DropDownList2.SelectedItem.Text;
    }


以上实现的原理就是在SelectedIndexChanged事件里,判断旧值和新值的比较(比较数据),如果发生变化,则引发事件,数据回发就是实现这样的事件.再重新整理一下思路,明白何时会引发SelectedIndexChanged事件

在选择下拉框值时,如果选的值跟原来的值相同,则不触发事件,如果选的值跟原来的值不同的话则触发SelectedIndexChanged事件(还是旧值和新值的比较).

打开MSDN文档查看DropDownList 类,则发现其继承了 IPostBackDataHandler 接口,我的意思就是说想要实现Change这样的事件,就要继承其接口.看看MSDN对此接口的定义

IPostBackDataHandler 接口
定义 ASP.NET 服务器控件为自动加载回发数据而必须实现的方法。

LoadPostData 方法  根据服务器控件的状态由于回发而发生更改做出判断是否调用RaisePostDataChangedEvent 方法,返回true则调用(就是旧值和新值不同的时候)

RaisePostDataChangedEvent 方法用于引发任何更改事件

以下的例子实现了如同textbox的TextChanged事件

postDataKey表示控件内部数据的关键值,postCollection表示所有传入名称值的集合,其采用索引的方式来访问

 


上面实现的方法如同button的onclick事件,其实不然,而是通过回发数据的新旧数据进行判断,我在示例代码中加了另外一个例子,这里就不列出了,大家可以下载后再去看,看了就明白不是button的onclick事件了.

本次主要讲了三个基础的事件处理

(1)捕获回发事件
(2)用于回调的客户端脚本
(3)处理回发数据

以下两个接口需要你慢慢的熟悉和使用

IPostBackEventHandler接口和IPostBackDataHandler 接口.

想到Button按钮就要想到IPostBackEventHandler接口,想要textbox,dropdownlist一些change事件则要想要IPostBackDataHandler 接口,如果结合起来,再自己思考的话,会明白的更深刻.

可能很多地方我也没表达清楚,跟别人讲的很多重复了,但还要拿出来分享下,这样也可以提高自己.最后还望大家如果看到有什么错误,请指出.

参考文章:

ASP.NET2.0服务器控件开发之实现事件

ASP.NET2.0服务器控件之捕获回传事件

posted on 2006-08-02 21:03 Clingingboy 阅读(23991) 评论(73)  编辑 收藏 网摘 所属分类: B Asp.net组件开发

Feedback

#1楼 2006-08-03 06:39 aspnetx      
期待楼主更多优秀的文章发布
  回复  引用  查看    

#2楼 2006-08-03 07:47 aspnetx      
还有您提到的
一个C#睡前故事
的链接在哪里?能否让大家都看一下这篇文章

  回复  引用  查看    

#3楼[楼主] 2006-08-03 08:38 Clingingboy      
@aspnetx

百度一下就可以了
一个C#睡前故事

  回复  引用  查看    

#4楼 2006-08-03 12:47 criyc[未注册用户]
能否把这些放在一个系列中或分类中,大家可好找点.
  回复  引用    

#5楼[楼主] 2006-08-03 13:38 Clingingboy      
等我写的稍微多点的时候,再进行分类
  回复  引用  查看    

#6楼 2006-08-05 18:38 锦瑟[未注册用户]
请问那些生成的<input>为什么不用id作为uniqueid的标识,而用name?
  回复  引用    

#7楼[楼主] 2006-08-05 21:20 Clingingboy      
id无法达到回传的目的,你可以把name改为id试一下,改了以后出不了效果.具体区别还是百度吧.
  回复  引用  查看    

#8楼 2006-09-17 22:00 panrong[未注册用户]
Page.VerifyRenderingInServerForm(this); 是什么意思?
  回复  引用    

#9楼 2006-09-18 13:43 林肯公园      
output.Write("<INPUT TYPE=submit name=" + this.UniqueID +
" Value='确定' />");

如果改为 Type=Button 就不行了。
是不是一定要是submit才能触发那个click事件?

  回复  引用  查看    

#10楼[楼主] 2006-09-18 19:10 Clingingboy      
@panrong
VerifyRenderingInServerForm方法为确认服务器控件包含在HtmlForm里面,我们知道asp.net页面的form只有一个.
@林肯公园

改为 Type=Button是可以的,请看下面这段话

在asp.net2.0中,button控件多了一个UseSubmitBehavior 属性,指示 Button 控件使用客户端浏览器的提交机制(客户端回发)还是 ASP.NET 回发机制,默认采用回发机制,如果设置为false的话,则需要调用GetPostBackEventReference 方法来返回 Button 的客户端回发事件

  回复  引用  查看    

#11楼 2006-09-18 21:11 林肯公园      
@Clingingboy
哦,2.0里面有的,那就好了.
之前我还在想怎么设计double click事件响应呢.
我现在还在用1.1,是否1.1里就不行了呢

  回复  引用  查看    

#12楼[楼主] 2006-09-18 21:55 Clingingboy      
@林肯公园
可以的,其实其实现方法跟LinkButton实现的形式是一样的

看示例三

  回复  引用  查看    

#13楼 2006-09-20 11:13 飞飞[未注册用户]
在net1.1中, 怎么OnTextChanged 事件触发不了
  回复  引用    

#14楼[楼主] 2006-09-20 11:29 Clingingboy      
@飞飞
我上面的示例代码是在.net2.0环境下运行的,稍微改下就可以在1.1下运行了

  回复  引用  查看    

还行,讲的基本清楚,支持了
  回复  引用    

#16楼 2006-11-21 10:34 gggg[未注册用户]
protected virtual void OnTextChanged(EventArgs e)
{
if (TextChanged != null)
TextChanged(this, e);
}

这个有什么用
直接
public void RaisePostDataChangedEvent()
{
TextChanged(this, EventArgs.Empty);
}
就可以

  回复  引用    

output.Write("<INPUT TYPE=submit name=" + this.UniqueID +
" Value='确定' />");

注意Value前要有个空格,否则这个控件无法回传数据。无解??
吧name 和Value挑换,也不行,最后终于验证,name="+this.UniqueID 后必须带一空格

  回复  引用    

@gggg
如果直接寫成
public void RaisePostDataChangedEvent()
{
TextChanged(this, EventArgs.Empty);
}
那麼page中如果沒有需要在意TextChanged 事件時,就會error,所以通常會多加這個判斷

  回复  引用    

#19楼 2007-01-19 14:45 Yee[未注册用户]
请教一个问题,如果更改控件的一个属性,比如,复合控件中下拉列表的个数,可以通过设置属性来实现,在外部调用页面如何实现,好像IPostBackDataHandler并不能办到
  回复  引用    

建议使用string.format来格式化字符串

如:writer.Write("\" value=\"" + this.Text + "\" />");
改成 write.Write(string.Format("\" value=\" {0}\" />",this.Text));

  回复  引用    

#21楼[楼主] 2007-04-28 13:31 Clingingboy      
@monkchen
谢谢,以后注意

  回复  引用  查看    

假设你在页面上多次使用这个控件,编译器将为每个事件委托实例生成一个字段。
-----------------------------------
此句对委托集合解读有误,参考msdn2005: ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_fxfund/html/30047cba-e2fd-41c6-b9ca-2ad7a49003db.htm
里面有准确的说明“要在一个类中实现多个事件属性,该类必须在内部存储和维护为每个事件定义的委托。”, 注意不是楼主所说的“多次使用这个控件”,应该注意到
// 声明Click事件委托
private static readonly object ClickKey = new object();

这里声明的是一个静态成员,静态成员的作用域是类而不是类的实例,所以“假设你在页面上多次使用这个控件”应该改为“假如你的控件有多个事件”。

  回复  引用    

#23楼[楼主] 2007-04-28 16:19 Clingingboy      
@monkchen
再次感谢,我看看,等下更正.谢谢指教

  回复  引用  查看    

楼主在线啊,太好了。我还有疑问,正想和你探讨探讨:

1.据我理解,private static readonly object ClickKey = new object();声明的是一类事件(即单击事件),结合msdn的示例代码来看应该没错,后者声明mousedown和mouseup两类事件,那么在委托集合添加元素的时候Events.AddHandler(ClickKey, value); 这里的clickkey是指单击事件(强调不是this,即不是类的实例),value才是真正的事件处理句柄;问题在这里:this指针如何与事件处理句柄关联? 可惜查看EventHandlerList的代码只有这些:
using System;
using System.Reflection;

namespace System.ComponentModel
{
// 摘要:
// 提供一个简单的委托列表。无法继承此类。
public sealed class EventHandlerList : IDisposable
{
// 摘要:
// 初始化 System.ComponentModel.EventHandlerList 类的新实例。
public EventHandlerList();

// 摘要:
// 获取或设置指定对象的委托。
//
// 参数:
// key:
// 要在列表中查找的对象。
//
// 返回结果:
// 指定键的委托;如果委托不存在,则为null。
public Delegate this[object key] { get; set; }

// 摘要:
// 将委托添加到列表。
//
// 参数:
// value:
// 要添加到列表的委托。
//
// key:
// 拥有该事件的对象。
public void AddHandler(object key, Delegate value);
//
// 摘要:
// 向当前列表添加委托的列表。
//
// 参数:
// listToAddFrom:
// 要添加的列表。
public void AddHandlers(EventHandlerList listToAddFrom);
//
// 摘要:
// 处置委托列表。
public void Dispose();
//
// 摘要:
// 从列表中将委托移除。
//
// 参数:
// value:
// 要从列表中移除的委托。
//
// key:
// 拥有该事件的对象。
public void RemoveHandler(object key, Delegate value);
}
}
//---------------------------------------------------------------
没有实现代码(或者我不知道如何细看?)!

2.EventHandler clickEventDelegate =
(EventHandler)Events[ClickKey];
//这句就更难理解了,假设我第一点的理解是对的话,那么这个索引器Events[ClickKey]返回的应该是clickkey(即单击事件)的处理句柄的集合。 大家看入列的时候是二维的:Events.AddHandler(ClickKey, value);出列的时候只有一维:Events[ClickKey]; 那推断返回值只能是个集合/数组,这个很匪夷所思。

参考http://ajax.asp.net/docs/ClientReference/Sys/EventHandlerListClass/default.aspx
里面有这句:
getHandler Method
Returns a single method that can be invoked to call all handlers sequentially for the specified event. 这个意思跟我的想法差不多,不过这个是AJAX的,跟此处讨论的是否是同个EventHandlerListClass我也说不好。

疑问尚未解决,还在查找资料,哪位能解惑请赐教!




  回复  引用    

问题解决了:
http://www.koders.com/csharp/fid88CE6C62BF10456C6120A25D35104D4818312698.aspx
// ---------通过设置断点查看Events的数据结构,判断此链接代码应该是伪代码,不是真正的.net代码---------

http://www.evget.com/article/read_1699.aspx
// ---------此页解决我上述的两个疑问------------

  回复  引用    

#26楼[楼主] 2007-04-28 21:15 Clingingboy      
@monkchen
看来还是深入的探讨才能真正了解

  回复  引用  查看    

@Clingingboy
呵呵,是啊,通过解决问题我收获了不少,当然,你的帖子也让我受益匪浅。谢谢!

  回复  引用    

#28楼[楼主] 2007-04-28 23:00 Clingingboy      
@monkchen
客气,大家多多交流才能共同提高

  回复  引用  查看    

学习了
  回复  引用    

#30楼 2007-09-04 15:30 PGG[未注册用户]
@Clingingboy
@songnanlu
kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk

  回复  引用    

#31楼 2007-09-19 16:43 loveNer[未注册用户]
向楼主致敬,期望一直写下去
  回复  引用    

#32楼 2007-09-21 18:20 少年狂      
谢谢楼主
  回复  引用  查看    

如果在示例二中:我想加两个按钮,同时都具有事件处理,应该如何去写呢???
呈现控件的方式和下面Render里面的一样.呈现两个按钮
protected override void Render(HtmlTextWriter output)
{
output.Write("<INPUT TYPE=submit name=" + this.UniqueID +
" Value='确定' />");

}

  回复  引用    

#34楼 2007-12-16 22:47 阿鹏      
再谢谢楼主
  回复  引用  查看    

#35楼 2008-01-22 11:22 匿名[未注册用户]
good ,仰慕你
  回复  引用    

楼主厉害!monkchen也很不错!
  回复  引用    

#37楼 2008-03-05 16:13 Gram      
學習中
  回复  引用  查看    

#38楼 2008-03-26 16:29 wawa[未注册用户]
--引用--------------------------------------------------
aspnetx: 期待楼主更多优秀的文章发布
--------------------------------------------------------
事实上

  回复  引用    

如果分页不用PagedDataSource ,自已写
页的索引从0开始 怎么设置当前页呢?
自已写了一个,但是在页面之间跳转的时候出问题
请楼主或哪个朋友指点一下

  回复  引用    

#40楼 2008-05-29 11:57 yww325[未注册用户]
当设置UseSubmitBehavior 属性为flase时,你运行页面时,则会发现一段自动生成的javascript代码
--这代码的来源正是使用GetPostBackEventReference的吧。
如果自己写,就是类似msdn的例子里的:
Attributes.Add("onclick", cs.GetPostBackEventReference(mycontrol, b.ID.ToString()));


  回复  引用    

#41楼 2008-06-05 12:33 diken[未注册用户]

TextChanged ()
这个好像不执行到的,我拷了你上面的代码,运行的时候发现好像没触发事件,
是不是控件还要加个AutoPostBack属性呀

  回复  引用    

#42楼[楼主] 2008-06-05 13:20 Clingingboy      
@diken
这个事件是等你改变文本以后,然后PostBack才能触发的,请确认开启视图状态

  回复  引用  查看    

#43楼 2008-06-05 15:31 diken[未注册用户]
谢谢.博主
  回复  引用    

#44楼 2008-06-05 16:40 diken[未注册用户]
我把代码运行的时候,在button的click事件里记一个label取textbox的值,
要点两次才执行一次,什么原因呢

  回复  引用    

#45楼 2008-06-05 16:49 diken[未注册用户]
我在page_load里用另一个label取同一文本框的值,就点击时都取到,
在按钮里就要点两次才取到一次,如果textbox每次都在变的话button下的label就根本取不到, textbox是webControl的,不是自定义那个

  回复  引用    

#46楼 2008-06-13 13:26 和尚释然      
好文章顶一下!

  回复  引用  查看    

#47楼 2008-07-16 23:06 Heming      
楼主 ,请问一下 下列俩种方式有什么不一样 ,好像都要刷新页面呢,没理解明白,希望指点
protected void Render(HtmlTextWriter writer)
{
base.Render(writer);
Page.VerifyRenderingInServerForm(this);

if (Display == ButtonDisplay.Button)
{
writer.Write(" writer.Write(" name=\"" + this.UniqueID + "\"");
writer.Write(" id=\"" + this.UniqueID + "\"");
writer.Write(" value=\"" + Text + "\"");
writer.Write(" />");
}
else if (Display == ButtonDisplay.Hyperlink)
{
writer.Write(" writer.Write(Page.GetPostBackClientHyperlink(this, ""));
writer.Write("\">" + Text + "
");
}

  回复  引用  查看    

@飞飞
我上面的示例代码是在.net2.0环境下运行的,稍微改下就可以在1.1下运行了

麻烦问一下怎么修改?谢谢。刚开始学,不太懂。

  回复  引用    

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Collections.Specialized;

namespace ZdyControl
{
/// <summary>
/// Textbox1 的摘要说明。
/// </summary>
[DefaultProperty("Text"),
ToolboxData("<{0}:Textbox1 runat=server></{0}:Textbox1>")]
public class Textbox1 : Control,IPostBackDataHandle
{
public event EventHandler TextChanged;
public string Text
{
get
{
object text = ViewState["Text"];
if(text == null)
return string.Empty;
else
return (string)text;
}

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

protected virtual void OnTextChanged(EventArgs e)
{
if(TextChanged != null)
TextChanged(this,e);
}




/// <summary>
/// 将此控件呈现给指定的输出参数。
/// </summary>
/// <param name="output"> 要写出到的 HTML 编写器 </param>
protected override void Render(HtmlTextWriter output)
{

output.AddAttribute(HtmlTextWriterAttribute.Type, "text");

output.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);

output.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);

output.RenderBeginTag(HtmlTextWriterTag.Input);

output.RenderEndTag();

}


#region IPostBackDataHandler 成员

public void RaisePostDataChangedEvent()
{
// TODO: 添加 Textbox1.RaisePostDataChangedEvent 实现
OnTextChanged(EventArgs.Empty);
}

public bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
// TODO: 添加 Textbox1.LoadPostData 实现
string postedValue = postCollection[postDataKey].ToString();
//检查新旧数据
if(!Text.Equals(postedValue))
{
Text = postedValue;
return true;
}
else
return false;
}

#endregion

}
}
在.net1.1中,不回发,请教一下,怎么修改?非常感谢,本人百思不得其解,望告知,不胜感激!

  回复  引用    

#50楼 2008-12-06 14:43 dfg[未注册用户]
<form>
<input type="file" />
</form>

  回复  引用    

#51楼 2008-12-06 14:46 dfg[未注册用户]
"<input type="file" />"
  回复  引用    

#52楼 2008-12-06 14:50 dfg[未注册用户]
"\<input type=\"file\" /\>"
  回复  引用    

#53楼 2008-12-06 14:51 dfg[未注册用户]
dfgdg
sdfasdf
dfgdfgd
fgdfg

  回复  引用    

#54楼 2008-12-06 14:54 dfg[未注册用户]
有一个回复里面的输入框是怎么发的?我试了几次都没成功
  回复  引用    

@Clingingboy
Button服务器控件默认采用客户端提交机制.^_^

  回复  引用    

#56楼 2009-03-19 09:38 unkown[未注册用户]
我按照你的方法去做一个控件,怎么在用的时候说它是一个服务器不认识的控件呢。我有runat,没有id,你写的控件id是怎样加上去的呢?
Thx

  回复  引用    

#57楼 2009-03-27 16:10 stromboy
如果我通過參數在里面自動生成控件,比如生成幾個DropDownList,我要在里面定義它的Change事件,我該怎么做啊?????
剛學這個,還請多多指教。

  回复  引用    

#58楼 2009-04-23 16:35 旴江      
谢谢
  回复  引用  查看    

#59楼 2009-06-09 14:20 谢小妹      
一直不是很清楚这个是做什么用的,谢谢
  回复  引用  查看    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 466180




相关文章:

相关链接: