ASP.NET 2.0 正式版中callback的一些变化+使用示例

简介:

可能你觉得callback很弱,AJAX才够强。其实网上大多数callback的示例代码都是不太正确的(包括MSDN)。这里提供了一种不同的使用callback的方法。只用 很少的javascript 就实现了一个联级下拉框。你会发现: 轻量级的callback其实也很好用。


在这里我有两个DropDownList,ddlCategory和ddlProduct。要求ddlCategory变化后ddlProduct无刷新的填充新的项目。

要使用Callback首先要继承 ICallbackEventHandler 接口: 
public  partial  class  Callback : PageICallbackEventHandler
或: 
<% @ Implements Interface = " System.Web.UI.ICallbackEventHandler "   %>

正式版的 ICallbackEventHandler 要实现以下两个方法: 
string  GetCallbackResult () 
void  RaiseCallbackEvent ( string  eventArgument)

eventArgument 现在改为由 RaiseCallbackEvent 接收,而不是由 GetCallbackResult 直接接受了。目的是为了让你可以在 RaiseCallbackEvent 中做一些初始化操作,这点在编写支持callback的控件时特别有用,有兴趣的话你可以参考 GridView DetailView RaiseCallbackEvent 的代码。在这里我只使用最简单的方式,把 eventArgument 存到一个私有成员中: 
private   string  _callbackEventArgument; 

protected   virtual   void  RaiseCallbackEvent( string  eventArgument) 

      
this ._callbackEventArgument  =  eventArgument; 
}

在客户端触发callback需要使用到 GetCallbackEventReference ,正式版中的 GetCallbackEventReference 位于 Page.ClientScript 下。 ClientScrip t是2.0中 Page 的一个新增成员,专门用于处理客户端教本(javascript),它是一个实例化的ClientScriptManager。 
< script  type ="text/javascript" >  
   
function  CallServer(arg, context) 
   {
 
        
<%= ClientScript.GetCallbackEventReference(this"arg""ReceiveServerData""context")%> ; 
    } 

    
function  ReceiveServerData(result, context) 
    { 
        ...
 
    } 
</ script >

     protected   void  Page_Load( object  sender, EventArgs e) 
    { 
        ddlCategory.Attributes.Add(
" onchange " " CallServer(....) " );  
    }

在这里我使用了一个javascript函数 CallServer 来包装callback的触发,当然你也可把它直接挂到onchange或其他客户端事件上。不过用一个函数来包装的话,可以很方便的在callback前后做一些其他操作,下面我就会用到。 

不知道你有没有发觉,我们传给callback两个参数: arg context ,但是 RaiseCallbackEvent 只得到一个(arg),另一个参数 context 会给原封不动的传给 ReceiveServerDate 。这个 context 到底有什么用呢?甚至连MSDN里的代码也没有很正确的使用这个参数。可能你觉得callback很弱,只传入一个string(arg)传出一个string(result),还要编写大量的javascript代码才能实现想要的功能。其实,只要正确使用上面那个 context 参数就可以用很少的javascript实现很理想的功能。 


首先,我们拆分一下 arg ,把我们要调用的服务端方法放进去: 
ddlCategory.Attributes.Add( " onchange " " CallServer('FillProduct|'+this.value, ...) " );

然后用反射在服务器端调用这个方法(FillProduct): 
     public   string  GetCallbackResult() 
    { 
        
string [] parts  =  _callbackEventArgument.Split( ' | ' ); 

         return  this.GetType().GetMethod(parts[0]).Invoke(thisnew objcet[]{parts[1]}) 
    }

我们来看看FillProduct会返回些什么: 
     public   string  FillProduct( string  categoryID) 
    { 
        ddlCategory.SelectedValue 
=  categoryID; 
        ddlProduct.DataBind(); 

        StringWriter writer1 = new StringWriter(CultureInfo.InvariantCulture); 
        HtmlTextWriter writer2 
= new HtmlTextWriter(writer1); 

        ddlProduct.RenderControl(writer2); 
        writer2.Flush(); 
        writer2.Close(); 
        return writer1.ToString(); 
    }

你可以看到,我把需要更新的ddlProduct整个重新Render后传回来了,也就是说要用新生成的ddlProduct的HTML替换原来的ddlProduct的HTML。怎么做到这一点呢? context 参数要出马了:

<script type="text/javascript"> 
   
function CallServer(arg, context
   { 
        context.innerHTML 
= "Loadingdot.gif"; 
        
<%= ClientScript.GetCallbackEventReference(this"arg""ReceiveServerData""context")%>
    } 

    
function ReceiveServerData(result, context
    { 
        context.innerHTML 
= result; 
    } 
</script
..... 
<asp:DropDownList ID="ddlCategory" runat="server"  DataSourceID="SqlDataSource1"  
    DataTextField
="CategoryName" DataValueField="CategoryID" AppendDataBoundItems="True" >  
    
<asp:ListItem Value="">- Select Category -</asp:ListItem> 
</asp:DropDownList>  
<span id="_span1">  
    
<asp:DropDownList ID="ddlProduct" runat="server"  DataSourceID="SqlDataSource2"  
          DataTextField
="ProductName" AppendDataBoundItems="True">  
          
<asp:ListItem Value="">- Select Product -</asp:ListItem>  
    
</asp:DropDownList> 
</span>


     protected   void  Page_Load( object  sender, EventArgs e) 
    { 
        ddlCategory.Attributes.Add(
" onchange " " CallServer('FillProduct|'+this.value, _span1) " );  
    }

原来我把要更新的ddlProduct放在_span1里,context就是用来传递这个_span1的。只要用新生成的HTML填充这个_span1,ddlProduct的更新就OK了。这样我们就很轻松的完成了一个无刷新的联级下拉框。 

需要注意的是,要在页面提交(postback)后取得这个ddlProduct的值,需要使用
Request.Form[ddlProduct.UniqueID]
因为viewstate并没有被更新。

在下面的完整代码里我还做了如下两件事,以提高代码的复用性:

  1. 对_callbackEventArgument稍加处理,以便调用任意多个参数的方法(FillProduct只有一个参数)
  2. 提取了FillProduct中Rander Control的那部分,以便于其他方法也可以使用。(这项操作用VS2005的Refactor很容易就完成了

其实你可以把这两部分整理到一个CallbackHelp类中,这样复用性就更高了。
Enjoy it.

<% @ Page Language = " C# "  AutoEventWireup = " true "  CodeFile = " Callback.aspx.cs "  Inherits = " Callback "   %>  

<! 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" >  

< script  type ="text/javascript" >  
   
function  CallServer(arg, context) 
   { 
        context.innerHTML 
=   " Loadingdot.gif "
        
<%=  ClientScript.GetCallbackEventReference( this " arg " " ReceiveServerData " " context " %>
    } 

    
function  ReceiveServerData(result, context) 
    { 
        context.innerHTML 
=  result; 
    } 
</ script >  

< head  runat ="server" >  
    
< title > Callback </ title >  
</ head >  
< body >  
    
< form  id ="form1"  runat ="server" >  
        
< div >  
            
< asp:SqlDataSource  ID ="SqlDataSource1"  runat ="server"   
                ConnectionString
="<%$ ConnectionStrings:NORTHWNDConnectionString1 %>"  
                SelectCommand
="SELECT [CategoryID], [CategoryName] FROM [Categories]"
            </
asp:SqlDataSource >  
            
< asp:SqlDataSource  ID ="SqlDataSource2"  runat ="server"   
                ConnectionString
="<%$ ConnectionStrings:NORTHWNDConnectionString1 %>"  
                SelectCommand
="SELECT [ProductID], [ProductName] FROM [Products] WHERE ([CategoryID] = @CategoryID)" >  
                
< SelectParameters >  
                    
< asp:ControlParameter  ControlID ="ddlCategory"  Name ="CategoryID"  
                          PropertyName
="SelectedValue"   />  
                
</ SelectParameters >  
            
</ asp:SqlDataSource >  
            
< div  id ="_div1"  runat ="server" >  
                
< asp:DropDownList  ID ="ddlCategory"  runat ="server"   
                    DataSourceID
="SqlDataSource1"  DataTextField ="CategoryName"  
                    DataValueField
="CategoryID"  AppendDataBoundItems ="True" >  
                    
< asp:ListItem  Value ="" > - Select Category - </ asp:ListItem >  
                
</ asp:DropDownList >  
                
< span  id ="_span1" >  
                    
< asp:DropDownList  ID ="ddlProduct"  runat ="server"   
                        DataSourceID
="SqlDataSource2"  DataTextField ="ProductName"  
                        AppendDataBoundItems
="True" >  
                        
< asp:ListItem  Value ="" > - Select Product - </ asp:ListItem >  
                    
</ asp:DropDownList >  
                
</ span >< span  id ="_span2" >  
                    
< asp:Button  ID ="btnBuy"  runat ="server"  Text ="Buy"  Enabled ="false"  OnClick ="btnBuy_Click"   />  
                    
< br  />  
                
</ span >  
            
</ div >  
            
< asp:Label  ID ="Label1"  runat ="server" ></ asp:Label >  
        
</ div >  
    
</ form >  
</ body >  
</ html >

None.gif using  System; 
None.gif
using  System.IO
None.gif
using  System.Collections; 
None.gif
using  System.Globalization
None.gif
using  System.Reflection
None.gif
using  System.Web; 
None.gif
using  System.Web.UI; 
None.gif 
None.gif
public  partial  class  Callback : Page, ICallbackEventHandler 
ExpandedBlockStart.gif

InBlock.gif    
private string _callbackEventArgument; 
InBlock.gif 
InBlock.gif    
protected void Page_Load(object sender, EventArgs e) 
ExpandedSubBlockStart.gif    

InBlock.gif        ddlCategory.Attributes.Add(
"onchange""CallServer('FillProduct|'+this.value,_span1)"); 
InBlock.gif        ddlProduct.Attributes.Add(
"onchange""CallServer('ShowBuy|'+this.value,_span2)"); 
ExpandedSubBlockEnd.gif    }
 
InBlock.gif 
InBlock.gif 
ExpandedSubBlockStart.gif    
#region ICallbackEventHandler Members 
InBlock.gif 
InBlock.gif    
public string GetCallbackResult() 
ExpandedSubBlockStart.gif    

InBlock.gif        
string[] parts = _callbackEventArgument.Split('|'); 
InBlock.gif        
object[] args = null
InBlock.gif        
string result = ""
InBlock.gif 
InBlock.gif        
if (parts.Length > 1
ExpandedSubBlockStart.gif        

InBlock.gif            args 
= new object[parts.Length - 1]; 
InBlock.gif            Array.Copy(parts, 
1, args, 0, args.Length); 
ExpandedSubBlockEnd.gif        }
 
InBlock.gif 
InBlock.gif        MethodInfo method 
= this.GetType().GetMethod(parts[0]); 
InBlock.gif 
InBlock.gif        
if (method != null
ExpandedSubBlockStart.gif        

InBlock.gif            result 
= (string)method.Invoke(this, args); 
ExpandedSubBlockEnd.gif        }
 
InBlock.gif 
InBlock.gif        
return result; 
ExpandedSubBlockEnd.gif    }
 
InBlock.gif 
InBlock.gif    
public void RaiseCallbackEvent(string eventArgument) 
ExpandedSubBlockStart.gif    

InBlock.gif        _eventArgument 
= eventArgument; 
ExpandedSubBlockEnd.gif    }
 
InBlock.gif 
InBlock.gif    
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) 
ExpandedSubBlockStart.gif    

InBlock.gif        
this.RaiseCallbackEvent(eventArgument); 
ExpandedSubBlockEnd.gif    }
 
InBlock.gif 
InBlock.gif    
string ICallbackEventHandler.GetCallbackResult() 
ExpandedSubBlockStart.gif    

InBlock.gif        
return this.GetCallbackResult(); 
ExpandedSubBlockEnd.gif    }
 
InBlock.gif 
ExpandedSubBlockEnd.gif    
#endregion
 
InBlock.gif 
InBlock.gif 
InBlock.gif    
public string FillProduct(string categoryID) 
ExpandedSubBlockStart.gif    

InBlock.gif        ddlCategory.SelectedValue 
= categoryID; 
InBlock.gif        ddlProduct.DataBind(); 
InBlock.gif 
InBlock.gif        
return RenderControl(ddlProduct); 
ExpandedSubBlockEnd.gif    }
 
InBlock.gif 
InBlock.gif    
public string ShowBuy(string ProductID) 
ExpandedSubBlockStart.gif    

InBlock.gif        btnBuy.Enabled 
= !string.IsNullOrEmpty(ProductID); 
InBlock.gif 
InBlock.gif        
return RenderControl(btnBuy); 
ExpandedSubBlockEnd.gif    }
 
InBlock.gif 
InBlock.gif    
protected void btnBuy_Click(object sender, EventArgs e) 
ExpandedSubBlockStart.gif    

InBlock.gif        _div1.Visible 
= false
InBlock.gif        Label1.Text 
= "Buy: " + Request.Form[ddlProduct.UniqueID]; 
ExpandedSubBlockEnd.gif    }
 
InBlock.gif 
InBlock.gif    
private string RenderControl(Control control) 
ExpandedSubBlockStart.gif    

InBlock.gif        StringWriter writer1 
= new StringWriter(CultureInfo.InvariantCulture); 
InBlock.gif        HtmlTextWriter writer2 
= new HtmlTextWriter(writer1); 
InBlock.gif 
InBlock.gif        control.RenderControl(writer2); 
InBlock.gif        writer2.Flush(); 
InBlock.gif        writer2.Close(); 
InBlock.gif 
InBlock.gif        
return writer1.ToString(); 
ExpandedSubBlockEnd.gif    }
 
ExpandedBlockEnd.gif}
 None.gif



本文转自高海东博客园博客,原文链接:http://www.cnblogs.com/ghd258/archive/2005/11/27/285386.html,如需转载请自行联系原作者
相关文章
|
5月前
|
开发框架 前端开发 JavaScript
盘点72个ASP.NET Core源码Net爱好者不容错过
盘点72个ASP.NET Core源码Net爱好者不容错过
150 0
|
5月前
|
开发框架 .NET
ASP.NET Core NET7 增加session的方法
ASP.NET Core NET7 增加session的方法
80 0
|
存储 开发框架 前端开发
asp.net与asp.net优缺点及示例
asp.net与asp.net优缺点及示例
|
2月前
|
API
【Azure 媒体服务】Media Service的编码示例 -- 创建缩略图子画面的.NET代码调试问题
【Azure 媒体服务】Media Service的编码示例 -- 创建缩略图子画面的.NET代码调试问题
|
2月前
|
XML API 图形学
【Azure Developer】.Net 简单示例 "文字动图显示" Typing to SVG
【Azure Developer】.Net 简单示例 "文字动图显示" Typing to SVG
【Azure Developer】.Net 简单示例 "文字动图显示" Typing to SVG
|
2月前
|
开发框架 JSON .NET
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
|
2月前
|
开发框架 .NET 数据库连接
ASP.NET Core 标识(Identity)框架系列(一):如何使用 ASP.NET Core 标识(Identity)框架创建用户和角色?
ASP.NET Core 标识(Identity)框架系列(一):如何使用 ASP.NET Core 标识(Identity)框架创建用户和角色?
|
4月前
|
XML API 图形学
.Net 简单示例 "文字动图显示" Typing to SVG “
该文描述了一个.NET API的实现过程,该API能将输入的文字转换成SVG动态图。首先,作者展示了示例网站(&lt;https://readme-typing-svg.demolab.com/&gt;)的功能,它能将文字转化为可自定义样式的SVG动画。接着分析了示例URL的响应,发现其内容类型为`image/svg+xml`,主要由SVG、path、animate和text元素组成。通过创建一个.NET Core Web API项目,作者设置了响应内容类型为`image/svg+xml`,并将示例URL的SVG内容直接输出,成功实现了相同效果。
|
5月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
|
5月前
|
SQL 开发框架 JavaScript
分享33个ASP.NET电子商务源码和40个ASP.NET控件组件源码,总有一款适合您
分享33个ASP.NET电子商务源码和40个ASP.NET控件组件源码,总有一款适合您
73 0