【问题标题】:How can I use the button tag with ASP.NET?如何在 ASP.NET 中使用按钮标记?
【发布时间】:2010-09-16 07:02:46
【问题描述】:

我想在 ASP.NET 网站中使用较新的 <button> 标记,除其他外,它允许 CSS 样式的文本并在按钮内嵌入图形。 asp:Button 控件呈现为<input type="button">,有什么方法可以使预先存在的控件呈现为<button>

根据我的阅读,当按钮位于<form> 内时,IE 发布按钮的标记而不是 value 属性不兼容,但在 ASP.NET 中,它将使用 onclick 事件触发 __doPostBack无论如何,所以我认为这不会是一个问题。

我有什么理由不应该使用它吗?如果没有,您将如何使用 asp:Button 或基于它的新服务器控件来支持它?如果可以避免,我宁愿不编写自己的服务器控件。


起初<button runat="server"> 解决方案有效,但我立即遇到了需要具有CommandName 属性的情况,而HtmlButton 控件没有。看来我毕竟需要创建一个从 Button 继承的控件。

我需要做什么才能覆盖渲染方法并使其渲染我想要的内容?


更新

DanHerbert 的回复让我有兴趣再次找到解决此问题的方法,因此我花了更多时间来解决这个问题。

首先,重载 TagName 有一种更简单的方法:

public ModernButton() : base(HtmlTextWriterTag.Button)
{
}

Dan 的解决方案的问题在于标签的 innerhtml 被放入 value 属性中,这会导致回发时出现验证错误。一个相关的问题是,即使您正确呈现 value 属性,IE 对 <button> 标记的脑死实现仍然会发布 innerhtml 而不是 value。因此,任何实现都需要重写 AddAttributesToRender 方法以正确呈现 value 属性,并为 IE 提供某种解决方法,以免完全搞砸回发。

如果您想利用数据绑定控件的 CommandName/CommandArgument 属性,IE 问题可能无法解决。希望有人可以为此提出解决方法。

我在渲染上取得了进展:

ModernButton.cs

这会呈现为具有正确值的正确 html <button>,但它不适用于 ASP.Net PostBack 系统。我已经写了一些我需要提供Command 事件的内容,但它不会触发。

当与常规 asp:Button 并排检查此按钮时,除了我需要的差异外,它们看起来相同。所以我不确定在这种情况下 ASP.Net 是如何连接Command 事件的。

另外一个问题是,嵌套的服务器控件没有被渲染(正如你可以看到的 ParseChildren(false) 属性)。在渲染期间将文字 html 文本注入控件非常容易,但是如何支持嵌套的服务器控件?

【问题讨论】:

    标签: c# asp.net html htmlbutton


    【解决方案1】:

    虽然您说使用 [button runat="server"] 不是一个足够好的解决方案,但重要的是要提一下 - 许多 .NET 程序员害怕使用“本机”HTML 标记...

    用途:

    <button id="btnSubmit" runat="server" class="myButton" 
        onserverclick="btnSubmit_Click">Hello</button>
    

    这通常工作得很好,我的团队中的每个人都很开心。

    【讨论】:

    • 是的,虽然它不等同于 asp:button,但它确实有效,因为你失去了很多功能。在我的情况下,该按钮位于数据绑定控件内,因此在没有 CommandName 和 CommandArgument 的情况下,它的实用性非常有限。
    • 您可以在此类控件上添加自定义属性并轻松检索代码中的值。
    • 这似乎破坏了 Causevalidation="false"
    • 请注意,如果 JavaScript 被禁用 (source),此解决方案(很像 LinkButton)将不起作用。
    • @niico 我的问题也是如此。希望在 ASP.NET 网络表单中支持原生 HTML5 按钮。
    【解决方案2】:

    这是一个老问题,但对于我们这些不幸仍然不得不维护 ASP.NET Web 窗体应用程序的人来说,我在尝试将 Bootstrap 字形包含在内置按钮控件中时自己解决了这个问题。

    根据 Bootstrap 文档,所需的标记如下:

    <button class="btn btn-default">
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
        Search
    </button>
    

    我需要这个标记由服务器控件呈现,所以我开始寻找选项。

    按钮

    这将是第一个合乎逻辑的步骤,但是——正如这个问题所解释的——Button 呈现一个 &lt;input&gt; 元素而不是 &lt;button&gt;,因此无法添加内部 HTML。

    LinkBut​​ton(归功于Tsvetomir Tsonev's answer

    来源

    <asp:LinkButton runat="server" ID="uxSearch" CssClass="btn btn-default">
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
        Search
    </asp:LinkButton>
    

    输出

    <a id="uxSearch" class="btn btn-default" href="javascript:__doPostBack(&#39;uxSearch&#39;,&#39;&#39;)">
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
        Search
    </a>
    

    优点

    • 看起来不错
    • Command事件; CommandNameCommandArgument 属性

    缺点

    • 呈现&lt;a&gt; 而不是&lt;button&gt;
    • 呈现并依赖于突兀的 JavaScript

    HtmlButton(归功于Philippe's answer

    来源

    <button runat="server" id="uxSearch" class="btn btn-default">
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
        Search
    </button>
    

    结果

    <button onclick="__doPostBack('uxSearch','')" id="uxSearch" class="btn btn-default">
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
        Search
    </button>
    

    优点

    • 看起来不错
    • 正确渲染&lt;button&gt;元素

    缺点

    • 没有Command 事件;没有CommandNameCommandArgument 属性
    • 呈现并依赖于突兀的 JavaScript 来处理其 ServerClick 事件

    在这一点上,显然没有任何内置控件适合,因此下一步合乎逻辑的步骤是尝试修改它们以实现所需的功能。

    自定义控件(归功于Dan Herbert's answer

    注意:这是基于 Dan 的代码,因此所有功劳归于他。

    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    namespace ModernControls
    {
        [ParseChildren]
        public class ModernButton : Button
        {
            public new string Text
            {
                get { return (string)ViewState["NewText"] ?? ""; }
                set { ViewState["NewText"] = value; }
            }
    
            public string Value
            {
                get { return base.Text; }
                set { base.Text = value; }
            }
    
            protected override HtmlTextWriterTag TagKey
            {
                get { return HtmlTextWriterTag.Button; }
            }
    
            protected override void AddParsedSubObject(object obj)
            {
                var literal = obj as LiteralControl;
                if (literal == null) return;
                Text = literal.Text;
            }
    
            protected override void RenderContents(HtmlTextWriter writer)
            {
                writer.Write(Text);
            }
        }
    }
    

    我已将类精简到最低限度,并对其进行了重构,以用尽可能少的代码实现相同的功能。我还添加了一些改进。即:

    • 去掉PersistChildren属性(好像没必要)
    • 删除TagName覆盖(似乎没有必要)
    • Text 中删除 HTML 解码(基类已经处理了这个)
    • 保持OnPreRender不变;改写AddParsedSubObject(更简单)
    • 简化RenderContents覆盖
    • 添加Value 属性(见下文)
    • 添加命名空间(以包含 @Register 指令示例)
    • 添加必要的using 指令

    Value 属性只是访问旧的Text 属性。这是因为本机 Button 控件无论如何都会呈现 value 属性(以 Text 作为其值)。由于value&lt;button&gt; 元素的有效属性,因此我决定为其添加一个属性。

    来源

    <%@ Register TagPrefix="mc" Namespace="ModernControls" %>
    
    <mc:ModernButton runat="server" ID="uxSearch" Value="Foo" CssClass="btn btn-default" >
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
        Search
    </mc:ModernButton>
    

    输出

    <button type="submit" name="uxSearch" value="Foo" id="uxSearch" class="btn btn-default">
        <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
        Search
    </button>
    

    优点

    • 看起来不错
    • 呈现正确的&lt;button&gt; 元素
    • Command事件; CommandNameCommandArgument 属性
    • 不呈现或不依赖于突兀的 JavaScript

    缺点

    • 无(不是内置控件除外)

    【讨论】:

    • 自定义控件还有一个bug:当子内容包含任何渲染表达式时,AddParsedSubObject永远不会被调用。示例:&lt;mc:ModernButton runat="server"&gt;&lt;b&gt;&lt;%= this.AnyString %&gt;&lt;/b&gt;&lt;/mc:ModernButton&gt;。但是,您可以直接在 Text 属性上使用资源 ('&lt;%$ Resources:AnyString %&gt;') 或绑定表达式 ('&lt;%# this.AnyString %&gt;'),但这会迫使您将整个内容放在一个表达式中(包括 HTML)。
    • @modiX:太好了!这解决了我的问题。我使用 Text 属性添加了一些文本和图像。
    • 据我所知,自定义控件总是会导致完整的回发,即使在 UpdatePanel 中也是如此。
    • 这个自定义控件几乎对我来说工作正常,但它不允许使用在服务器端分配值的嵌套控件(AddParsedSubObject 之前运行嵌套控件的值已更改 - 即使我将操作移到 Page_Init 中)。最后,我不得不采用 Dan Herbert 的 OnPreRenderRenderContents 方法。这些允许在服务器端更改嵌套控件。到目前为止,我已成功地将其与中继器中的 CommandName 和 CommandArgument 以及表单上的主提交按钮一起使用。谢谢大家!
    • 您可能还需要添加程序集:
    【解决方案3】:

    我偶然发现了你的问题,正在寻找同样的东西。我最终使用Reflector 来确定ASP.NET Button 控件的实际呈现方式。事实证明它真的很容易改变。

    实际上只是重写Button 类的TagNameTagKey 属性。完成此操作后,您只需要确保手动渲染按钮的内容,因为原始的 Button 类从来没有要渲染的内容,如果您不渲染,控件将渲染一个无文本按钮内容。

    更新:

    可以通过继承对 Button 控件进行一些小的修改,并且仍然可以很好地工作。此解决方案消除了为 OnCommand 实现您自己的事件处理程序的需要(尽管如果您想学习如何做到这一点,我可以向您展示如何处理)。它还解决了提交包含标记的值的问题,可能除了 IE。不过,我仍然不确定如何修复 IE 对 Button 标签的不良实现。这可能只是一个无法解决的真正技术限制......

    [ParseChildren(false)]
    [PersistChildren(true)]
    public class ModernButton : Button
    {
        protected override string TagName
        {
            get { return "button"; }
        }
    
        protected override HtmlTextWriterTag TagKey
        {
            get { return HtmlTextWriterTag.Button; }
        }
    
        // Create a new implementation of the Text property which
        // will be ignored by the parent class, giving us the freedom
        // to use this property as we please.
        public new string Text
        {
            get { return ViewState["NewText"] as string; }
            set { ViewState["NewText"] = HttpUtility.HtmlDecode(value); }
        }
    
        protected override void OnPreRender(System.EventArgs e)
        {
            base.OnPreRender(e);
            // I wasn't sure what the best way to handle 'Text' would
            // be. Text is treated as another control which gets added
            // to the end of the button's control collection in this 
            //implementation
            LiteralControl lc = new LiteralControl(this.Text);
            Controls.Add(lc);
    
            // Add a value for base.Text for the parent class
            // If the following line is omitted, the 'value' 
            // attribute will be blank upon rendering
            base.Text = UniqueID;
        }
    
        protected override void RenderContents(HtmlTextWriter writer)
        {
            RenderChildren(writer);
        }
    }
    

    要使用此控件,您有几个选择。一种是将控件直接放入 ASP 标记中。

    <uc:ModernButton runat="server" 
            ID="btnLogin" 
            OnClick="btnLogin_Click" 
            Text="Purplemonkeydishwasher">
        <img src="../someUrl/img.gif" alt="img" />
        <asp:Label ID="Label1" runat="server" Text="Login" />
    </uc:ModernButton>
    

    您还可以在代码隐藏中将控件添加到按钮的控件集合中。

    // This code probably won't work too well "as is"
    // since there is nothing being defined about these
    // controls, but you get the idea.
    btnLogin.Controls.Add(new Label());
    btnLogin.Controls.Add(new Table());
    

    我不知道这两个选项的组合效果如何,因为我没有测试过。

    目前此控件的唯一缺点是我认为它不会记住您在 PostBacks 中的控件。我没有对此进行测试,所以它可能已经工作了,但我怀疑它是否有效。您需要为子控件添加一些 ViewState 管理代码,以便跨 PostBacks 处理我认为,但这对您来说可能不是问题。添加 ViewState 支持应该不是很难做到,尽管如果需要可以轻松添加。

    【讨论】:

    • 您已经完成了一半,但使用按钮标签的一个主要原因是能够在其中添加标记,例如嵌入的图像。这似乎是最难的部分,允许内容在服务器控件中。我还没有找到有关如何执行此操作的说明。
    • 这很容易做到。我会更新我的答案来证明这一点。
    • 这让我更接近解决方案,谢谢。但是,在此之前,仍有一些主要问题需要解决。有关详细信息,请参阅我修改后的问题。
    • +1 这是最完整的答案,因为没有内置控件可以满足问题的要求。请参阅我的answer below 以获取重构版本。
    【解决方案4】:

    您可以创建一个新控件,从 Button 继承,并覆盖渲染方法,或使用 .browser 文件覆盖站点中的所有 Button,类似于 CSS 友好的东西适用于 TreeView 控件等的方式。

    【讨论】:

    • 听起来重写渲染方法可能是我需要的解决方案的一部分。你自己做过吗?能说的详细点吗?
    • 我没有为按钮做过,但我有其他控件。如果没有人超过我,当我回到我的电脑上时,我会贴上一些代码。
    【解决方案5】:

    您可以使用Button.UseSubmitBehavior 中讨论的this article about rendering an ASP.NET Button control as a button 属性。


    编辑:对不起..这就是我在午休时间略读问题的结果。您是否有理由不只使用

    【讨论】:

      【解决方案6】:

      我会使用 LinkBut​​ton 并根据需要使用 CSS 设置样式。

      【讨论】:

      • 我想要一个按钮控件,而不是超链接。我当然知道 LinkBut​​ton。
      • 它可以被设计成一个外观。例如看这里:hedgerwow.com/360/dhtml/…
      • 按钮由操作系统渲染,每一个看起来都不一样。在某些操作系统中,它们也可以由用户设置主题。如果我将按钮的主题设置为看起来像 Vista 按钮,那么它在其他任何地方都会显得不一致。
      • 如果您需要...当您提到 CSS 和图像时,我正在考虑完全重新设置按钮的样式。无论如何,只要留意 IE ;)
      【解决方案7】:

      我整天都在为此苦苦挣扎——我的自定义 &lt;button/&gt; 生成的控件不起作用:

      System.Web.HttpRequestValidationException:从客户端检测到有潜在危险的 Request.Form 值

      发生的事情是 .Net 添加了一个name 属性:

      <button type="submit" name="ctl02" value="Content" class="btn ">
         <span>Content</span>
      </button>
      

      name 属性在使用 IE 6 和 IE 7 时会引发服务器错误。在其他浏览器(Opera、IE 8、Firefox、Safari)中一切正常。

      解决方法是删除 name 属性。还是没搞清楚。

      【讨论】:

      • 名牌不是问题。请参阅asp.net/learn/whitepapers/request-validation 这是因为 IE 没有像它应该的那样发布 value 属性,而是从标签发布标记,如果您不禁用请求验证,则回发标记总是会在 ASP.Net 中引发服务器错误。
      • 使用按钮标签的最大障碍是 IE 的行为与其他一切都如此不同。您可能必须在 javascript 中更改 post 值,然后才能将它们发送到服务器,以使其行为可预测。
      • @Adam 是的,你是对的,这就是问题所在。你现在有解决方法吗?不使用 javascript。
      • 是的 -- 不要使用 IE ;) 否则,您必须操作 Post 值,而 javascript 是唯一的方法。
      • 不幸的是,我得出的结论是根本不使用按钮标签。我喜欢它,但对于我的使用来说,IE 渗透率为 50%。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-17
      • 1970-01-01
      • 2016-09-10
      相关资源
      最近更新 更多