您的位置:首页 > 编程语言 > ASP

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

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

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

要使用Callback首先要继承ICallbackEventHandler接口:

public partial class Callback : Page, ICallbackEventHandler
或:

<%@ 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两个参数: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(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)
{
[b]context.innerHTML [/b]= "Loading

"
;
<%= ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context")%>;
}

function ReceiveServerData(result, context)
{
[b]context.innerHTML [/b]= 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并没有被更新。
更新:
或者可以把ddlProduct的EnableViewState改为False,然后就可以用ddlProduct.SelectedValue取值了。但是这样做在复杂的页面中可能会引起一些错误,要更具具体情况谨慎处理。

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

对_callbackEventArgument稍加处理,以便调用任意多个参数的方法(FillProduct只有一个参数)

提取了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)");


}








ICallbackEventHandler Members#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)






{


_callbackEventArgument = 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();


}


}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐