如何在ASP.NET中使用按钮标记
本文关键字:按钮 ASP NET | 更新日期: 2023-09-27 17:48:52
我想在ASP.NET网站中使用较新的<button>
标记,它允许使用CSS样式的文本并在按钮中嵌入图形。asp:Button控件呈现为<input type="button">
,是否有任何方法可以使预先存在的控件呈现为<button>
?
据我所知,当按钮位于<form>
中时,IE发布按钮的标记而不是值属性是不兼容的,但在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,而不是值。因此,这方面的任何实现都需要重写AddAttributesToRender方法,以便正确地呈现值属性,并为IE提供某种变通方法,这样它就不会完全破坏回发。
如果要利用数据绑定控件的CommandName/CommandArgument属性,则IE问题可能无法解决。希望有人能提出一个解决办法。
我在渲染方面取得了进展:
ModernButton.cs
这将呈现为具有正确值的正确html <button>
,但它不适用于ASP.Net PostBack系统。我已经编写了一些需要提供Command
事件的内容,但它不会启动。
当将这个按钮与常规asp:button并排检查时,除了我需要的差异之外,它们看起来是一样的。所以我不确定在这种情况下ASP.Net是如何连接Command
事件的。
另外一个问题是,嵌套的服务器控件没有被呈现(正如您在ParseChildren(false)属性中看到的那样)。在渲染期间将文本html文本注入控件非常容易,但是如何允许对嵌套服务器控件的支持呢?
尽管你说使用[button runat="server"]不是一个足够好的解决方案,但值得一提的是,很多.NET程序员都害怕使用"原生"HTML标记。。。
用途:
<button id="btnSubmit" runat="server" class="myButton"
onserverclick="btnSubmit_Click">Hello</button>
这通常非常好,我的团队中每个人都很开心。
这是一个老问题,但对于那些仍然需要维护ASP.NET Web Forms应用程序的不幸的人来说,我在尝试将引导符号包含在内置按钮控件中时亲自经历了这一过程。
根据Bootstrap文档,所需的标记如下:
<button class="btn btn-default">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
Search
</button>
我需要这个标记由服务器控件呈现,所以我开始寻找选项。
按钮
这将是第一个合乎逻辑的步骤,但正如这个问题所解释的那样,Button呈现的是<input>
元素而不是<button>
,因此不可能添加内部HTML。
LinkButton(归功于Tsvetomir Tsonev的回答)
来源
<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('uxSearch','')">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
Search
</a>
优点
- 看起来不错
Command
事件;CommandName
和CommandArgument
的性质
缺点
- 渲染
<a>
而不是<button>
- 渲染并依赖于突兀的JavaScript
HtmlButton(归功于Philippe的回答)
来源
<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>
优点
- 看起来不错
- 呈现正确的
<button>
元素
缺点
- 无
Command
事件;没有CommandName
或CommandArgument
属性 - 呈现并依赖于突兀的JavaScript来处理其
ServerClick
事件
在这一点上,很明显,没有一个内置控件看起来合适,所以下一个合乎逻辑的步骤是尝试修改它们以实现所需的功能。
自定义控制(归功于Dan Herbert的回答)
注意:这是基于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
是<button>
元素的有效属性,所以我决定为它包含一个属性
来源
<%@ 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>
优点
- 看起来不错
- 呈现适当的
<button>
元素 - CCD_ 37事件;CCD_ 38和CCD_
- 不呈现或依赖于突兀的JavaScript
缺点
- 无(除了不是内置控件之外)
我无意中发现你的问题是在寻找同样的东西。我最终使用Reflector来了解ASP.NET Button
控件的实际呈现方式。事实证明,改变真的很容易。
实际上,它只是覆盖Button
类的TagName
和TagKey
属性。完成后,您只需要确保手动渲染按钮的内容,因为原始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中的控件。我还没有测试过,所以它可能已经有效了,但我怀疑它是否有效。我认为,您需要为要在PostBacks中处理的子控件添加一些ViewState管理代码,但这对您来说可能不是问题。添加ViewState支持应该不是很难,尽管如果需要,可以很容易地添加。
您可以创建一个新控件,从Button继承,并覆盖呈现方法,或者使用.browser文件覆盖网站中的所有按钮,类似于CSS Friendly的东西用于TreeView控件等。
您可以使用Button.UseSubmitBehavior属性,如本文中关于将ASP.NET按钮控件呈现为按钮的讨论所述。
编辑:对不起。。这就是我午休时略读问题的收获。有没有理由不直接使用<button runat="服务器">标记还是HtmlButton?
我会使用LinkButton,并根据需要使用CSS进行样式设置。
我一整天都在为此而挣扎——我的自定义<button/>
生成的控件无法工作:
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、IE8、Firefox、Safari)中一切正常。
解决方法是破解name
属性。不过我还没弄明白。