• 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 
    = "Loading";
            
    <%= 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 
    = "Loading"
            
    <%= 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>

    using System;
    using System.IO;
    using System.Collections;
    using System.Globalization;
    using System.Reflection;
    using System.Web;
    using System.Web.UI;

    public partial class Callback : Page, ICallbackEventHandler
    {
        
    private string _callbackEventArgument;

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



        
    #region ICallbackEventHandler Members

        
    public string GetCallbackResult()
        
    {
            
    string[] parts = _callbackEventArgument.Split('|');
            
    object[] args = null;
            
    string result = "";

            
    if (parts.Length > 1)
            
    {
                args 
    = new object[parts.Length - 1];
                Array.Copy(parts, 
    1, args, 0, args.Length);
            }


            MethodInfo method 
    = this.GetType().GetMethod(parts[0]);

            
    if (method != null)
            
    {
                result 
    = (string)method.Invoke(this, args);
            }


            
    return result;
        }


        
    public void RaiseCallbackEvent(string eventArgument)
        
    {
            _eventArgument 
    = eventArgument;
        }


        
    void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
        
    {
            
    this.RaiseCallbackEvent(eventArgument);
        }


        
    string ICallbackEventHandler.GetCallbackResult()
        
    {
            
    return this.GetCallbackResult();
        }


        
    #endregion



        
    public string FillProduct(string categoryID)
        
    {
            ddlCategory.SelectedValue 
    = categoryID;
            ddlProduct.DataBind();

            
    return RenderControl(ddlProduct);
        }


        
    public string ShowBuy(string ProductID)
        
    {
            btnBuy.Enabled 
    = !string.IsNullOrEmpty(ProductID);

            
    return RenderControl(btnBuy);
        }


        
    protected void btnBuy_Click(object sender, EventArgs e)
        
    {
            _div1.Visible 
    = false;
            Label1.Text 
    = "Buy: " + Request.Form[ddlProduct.UniqueID];
        }


        
    private string RenderControl(Control control)
        
    {
            StringWriter writer1 
    = new StringWriter(CultureInfo.InvariantCulture);
            HtmlTextWriter writer2 
    = new HtmlTextWriter(writer1);

            control.RenderControl(writer2);
            writer2.Flush();
            writer2.Close();

            
    return writer1.ToString();
        }

    }
  • 相关阅读:
    jQuery基础【1】
    qTip2 精致的jQuery提示信息插件
    jquery tools 系列(三)——scrollable(2)
    JQuery常用函数及功能小结
    【转】JQ命令汇总 jQuery
    jquery tools系列(一)——tabs(选项卡/页签)
    jquery tools系列(四)——overlay
    肚子
    今天买了部数码相机NiKon S510
    《文渊阁四库全书》书目
  • 原文地址:https://www.cnblogs.com/ghd258/p/285386.html
Copyright © 2020-2023  润新知