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

本文来自  出走的影子   http://jackielin.cnblogs.com/archive/2005/11/27/callback.html

可能你觉得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的控件时特别有用,有兴趣的话你可以参考 GridViewDetailViewRaiseCallbackEvent的代码。在这里我只使用最简单的方式,把 eventArgument存到一个私有成员中:
private   string  _callbackEventArgument;

protected   virtual   void  RaiseCallbackEvent( string  eventArgument)
{
      
this ._callbackEventArgument  =  eventArgument;
}

在客户端触发callback需要使用到 GetCallbackEventReference,正式版中的 GetCallbackEventReference位于 Page.ClientScript下。 ClientScript是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两个参数: argcontext,但是 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(this, new 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.gifContractedBlock.gif
dot.gif {
InBlock.gif    
private string _callbackEventArgument;
InBlock.gif
InBlock.gif    
protected void Page_Load(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.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
ContractedSubBlock.gifExpandedSubBlockStart.gif    
ICallbackEventHandler Members#region ICallbackEventHandler Members
InBlock.gif
InBlock.gif    
public string GetCallbackResult()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.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.gifContractedSubBlock.gif        
dot.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.gifContractedSubBlock.gif        
dot.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.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        _eventArgument 
= eventArgument;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
this.RaiseCallbackEvent(eventArgument);
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
string ICallbackEventHandler.GetCallbackResult()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.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.gifContractedSubBlock.gif    
dot.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.gifContractedSubBlock.gif    
dot.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.gifContractedSubBlock.gif    
dot.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.gifContractedSubBlock.gif    
dot.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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值