ASP.NET 2.0 正式版中callback的一些变化+使用示例-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

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的控件时特别有用,有兴趣的话你可以参考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(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.gifusing 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,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章
最新文章
相关文章