65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在 ASP.NET 中使用 JavaScript 向服务器发回数据

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (6投票s)

2009年9月8日

CDDL

4分钟阅读

viewsIcon

48649

downloadIcon

382

本文介绍如何在不依赖内置函数 __doPostBack 的情况下,创建向服务器发回数据的机制。

引言

我和你们中的许多人一样,不时需要在 ASP.NET 中使用 JavaScript 创建一个回发(postback)。当我在网上搜索解决方案时,我发现许多帖子提倡使用 __doPostBack,这是微软创建的一个用于向服务器发回数据的 JavaScript 函数。然而,这有几个问题,包括:

  1. 微软未对其进行文档记录,因此它可能不在未来版本中受支持。
  2. 它会跳过 ASP.NET 验证器上的客户端验证。
  3. 它无法与 ASP.NET AJAX 中的 UpdatePanel 一起使用。

如果你愿意复制微软的代码并编写更多可能不受微软未来版本支持的代码,那么你可以比较轻松地绕过列表中的第二和第三项。但是,.NET 框架中有一个鲜为人知的名为“PostBackOptions”的对象值得讨论。它使用起来有点麻烦,所以我将其包装在一个自定义控件中,以限制使用该对象所需的手动编码量。

Using the Code

控件属性

  • CausesValidation - 就像按钮一样,这指定调用此函数是否会导致页面进行验证。
  • ValidationGroup - 就像按钮一样,这指定了一个可选的验证组。
  • FunctionName - 这是将被调用以向服务器发回数据的 JavaScript 函数。
  • JavaScriptNamespaces - 这是一个命名空间列表,有助于防止函数名冲突。
  • Postback - 这是一个委托,允许你在调用回发代码时运行代码。
  • PostbackButton - 这是一个用户看不到的私有按钮,对于 PostBackOptions 工作是必需的。

JavaScriptNamespaces

在我们开始编写代码之前,我可能需要解释一些非传统的 JavaScript 用法。编写可维护、可读的代码对每种语言的每个程序员都很重要,编写 JavaScript 也不例外。对可维护 JavaScript 的完整解释超出了本文的范围,但我强烈建议在代码中模拟命名空间,以防止名称冲突并更好地组织函数。我在这里也是这样做的,所以可能需要一些解释。为了实现这一点,这是我为控件编写的代码:

public class JavaScriptPostback : CompositeControl
{
    //Property declarations here
    //CompositeControl overrides here

    protected override void Render(HtmlTextWriter writer)
    {
        //Code to create a PostBackOptions object called "options" should go here

        String functionName = JavaScriptNamespaces.Equals(String.Empty) ? 
          FunctionName : String.Concat(JavaScriptNamespaces, ".", FunctionName);
        writer.Write("<script type='text/javascript'>");
        AddNamespaces(JavaScriptNamespaces, writer);
        writer.Write(String.Format("{0} = function() {1}", functionName, "{"));
        writer.Write(this.Page.ClientScript.GetPostBackEventReference(options));
        writer.Write("};");
        writer.Write("</script>");
    }

    private void AddNamespaces(String originalNamespaces, 
                 System.Web.UI.HtmlTextWriter output)
    {
        StringBuilder namespaces = new StringBuilder();
        if (!originalNamespaces.Equals(String.Empty))
        {
            String[] namespaceList = originalNamespaces.Split('.');
            foreach (String nspace in namespaceList)
            {
                if (namespaces.Length > 0)
                    namespaces.Append(".");

                namespaces.Append(nspace);

                output.WriteLine(String.Concat("if (typeof(", 
                    namespaces.ToString(), ") === 'undefined') { ", 
                    namespaces.ToString(), " = {}; }"));
            }
        }
    }
}

如果我将“MyCompany.Custom”用作我的 JavaScriptNamespaces,并将“PostBack”用作我的 FunctionName,那么这段 JavaScript 将在页面上渲染:

<script type='text/javascript'>
if (typeof(MyCompany) === 'undefined')
    MyCompany = {};
if (typeof(MyCompany.Custom) === 'undefined')
    MyCompany.Custom = {};
if (typeof(MyCompany.Custom.PostBack) === 'undefined')
    MyCompany.Custom.PostBack = function()
     { /* code generated by PostBackOptions would go here */ };
</script>

这里发生了什么?我检查一个名为“MyCompany”的对象是否存在,如果不存在,我将其创建一个(空)对象作为其他“命名空间”和函数的容器。对于“MyCompany.Custom”也是如此。在检查“MyCompany.Custom.PostBack”是否存在后,我创建并分配一个“匿名函数”给该对象。为了调用该函数,你只需调用“MyCompany.Custom.PostBack()”。有关更详细的解释,请参考 David Flanagan 的 JavaScript 书籍《JavaScript: A Definitive Guide》,由 O'Reilly 出版。这是我见过的为数不多的几本描述如何使用该语言而不是简单地告诉你它有什么可用的书籍之一。

CompositeControl

自定义控件继承自 CompositeControl,因为按钮和一些自定义 JavaScript 都是控件的一部分。我本可以使该控件继承自 Button 类,并重写 Render() 来同时渲染必要的 JavaScript,但这样控件将有许多对用户无用的属性,所以我选择了 CompositeControl。有关 CompositeControl 的更多信息,请参阅微软的文档。

PostBackOptions

这是 Render() 中剩余的代码:

protected override void Render(HtmlTextWriter writer)
{
    base.Render(writer);
    PostBackOptions options = new PostBackOptions(PostbackButton);
    options.PerformValidation = this.CausesValidation;
    options.ValidationGroup = this.ValidationGroup;
    this.Page.ClientScript.RegisterForEventValidation(options);
}

使用该类几乎不需要什么。它需要知道你正在使用哪些验证选项,以及代码

this.Page.ClientScript.GetPostBackEventReference(options);

返回一个将向服务器发回数据的 JavaScript 函数。

需要注意的是,调用 base.Render(writer) 并非绝对必要。我这样做的原因是为了在页面上渲染按钮,这允许 UpdatePanel 找到该控件。

处理回发

现在我们需要指定当应用程序控件返回服务器时会发生什么。我们可以通过创建一个委托来实现这一点。

public event EventHandler Postback;
void PostbackButton_Click(object sender, EventArgs e)
{
    if (Postback != null)
        Postback(sender, e);
}

当我们创建 PostBackOptions 对象时,我们会传入一个私有的 PostbackButton,它是 Button 对象的实例。PostBackOptions 对象需要它来为 JavaScript 代码提供上下文。由于我们将此按钮隐藏起来,不对程序员公开,因此我们需要找到另一种方法将事件信息传递给他们。一种方法是创建我们自己的委托,并在调用 PostbackButton 委托时调用我们的委托。为了处理此事件,编写类似于按钮点击事件的代码:

protected void Page_Load(object sender, EventArgs e)
{
    MyPostBackControl.Postback += new EventHandler(MyPostBackControl_PostBack);
}

void MyPostBackControl_PostBack(object sender, EventArgs e)
{
    //Write your code here
}

就是这样。正如你所看到的,源代码可在本文顶部下载。该项目本身是用 Visual Studio 2008 编写的,但你应该能够轻松地在 2005 项目中使用这些代码。如果你有任何问题、评论或改进意见,请随时与我联系。

© . All rights reserved.