ASP.NET Webサイトで新しい<button>
タグを使用して、CSSスタイルのテキストを許可し、ボタン内にグラフィックを埋め込むことができます。 asp:Buttonコントロールは<input type="button">
としてレンダリングされますが、既存のコントロールを<button>
にレンダリングする方法はありますか?
私が読んだことから、ボタンが<form>
内にあるときにvalue属性の代わりにボタンのマークアップを投稿するIEとの非互換性がありますが、ASP.NETではとにかく__doPostBackを起動するためにonclickイベントを使用しているので、これが問題になるとは思いません。
これを使用しない理由はありますか?そうでない場合、asp:Button、またはそれに基づいた新しいサーバーコントロールでどのようにサポートしますか?回避できる場合は、独自のサーバーコントロールを記述しないことをお勧めします。
最初は<button runat="server">
ソリューションが機能しましたが、HtmlButtonコントロールにはないCommandNameプロパティが必要な状況にすぐに遭遇しました。結局、Buttonから継承されたコントロールを作成する必要があるようです。
Renderメソッドをオーバーライドし、必要なものをレンダリングするために何をする必要がありますか?
[〜#〜] update [〜#〜]
DanHerbertの返事は、この問題の解決策を見つけることに興味を持ったので、もう少し時間をかけて作業しました。
まず、TagNameをオーバーロードするはるかに簡単な方法があります。
public ModernButton() : base(HtmlTextWriterTag.Button)
{
}
Danのソリューションの現状の問題は、タグのinnerhtmlがvalueプロパティに配置されているため、ポストバック時に検証エラーが発生することです。関連する問題は、valueプロパティを正しくレンダリングしても、IEの<button>
タグのブレインデッド実装は、とにかく値の代わりにinnerhtmlをポストすることです。そのため、valueプロパティを正しくレンダリングするために、この実装はAddAttributesToRenderメソッドをオーバーライドする必要があります。また、IEのポストバックを完全に台無しにしないように何らかの回避策を提供します。
IE問題は、データバインドされたコントロールのCommandName/CommandArgumentプロパティを利用したい場合、乗り越えられない可能性があります。
私はレンダリングを進歩させました:
これは正しい値を持つ適切なhtml <button>
としてレンダリングされますが、ASP.Net PostBackシステムでは機能しません。 Command
イベントを提供するために必要なものをいくつか書きましたが、起動しません。
このボタンを通常のasp:Buttonと並べて調べると、必要な違い以外は同じように見えます。この場合、ASP.NetがCommand
イベントをどのように結び付けているのかわかりません。
追加の問題は、ネストされたサーバーコントロールがレンダリングされないことです(ParseChildren(false)属性で確認できます)。レンダリング中にリテラルHTMLテキストをコントロールに挿入するのは非常に簡単ですが、ネストされたサーバーコントロールのサポートをどのように許可しますか?
これは古い質問ですが、ASP.NET Webフォームアプリケーションを維持する必要がある幸運な私たちにとっては、組み込みのボタンの中にBootstrapグリフを含めるようにしながら、コントロール。
Bootstrap documentationによると、望ましいマークアップは次のとおりです。
<button class="btn btn-default">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
Search
</button>
サーバーコントロールによってこのマークアップをレンダリングする必要があったため、オプションを見つけることにしました。
これは最初の論理ステップですが、この質問で説明しているように、Buttonは<input>
ではなく<button>
要素をレンダリングするため、内部HTMLを追加することはできません。
ソース
<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>
をレンダリングしますソース
<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
プロパティなしServerClick
イベントを処理するために、邪魔なJavaScriptをレンダリングして依存しますこの時点で、組み込みのコントロールはどれも適切ではないことは明らかであるため、次の論理ステップは、目的の機能を実現するためにそれらを変更することです。
注:これはダンのコードに基づいているため、すべてのクレジットは彼に与えられます。
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
プロパティを追加します(以下を参照)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>
要素をレンダリングしますCommand
イベント; CommandName
およびCommandArgument
プロパティ短所
[button runat = "server"]を使用するだけでは十分な解決策ではないと言いますが、それを言及することは重要です。多くの.NETプログラマーは「ネイティブ」HTMLタグの使用を恐れています...
つかいます:
<button id="btnSubmit" runat="server" class="myButton"
onserverclick="btnSubmit_Click">Hello</button>
これは通常、完璧に機能し、私のチームでは誰もが幸せです。
私はあなたの質問に同じものを探してつまずいた。最終的に Reflector を使用して、ASP.NET Button
コントロールが実際にレンダリングされる方法を見つけました。本当に簡単に変更できることがわかりました。
実際には、TagName
クラスのTagKey
およびButton
プロパティをオーバーライドするだけです。元のButton
クラスにはレンダリングするコンテンツがなかったため、ボタンのコンテンツを手動でレンダリングする必要があります。そうしないと、コントロールはテキストなしのボタンをレンダリングします。 tコンテンツをレンダリングします。
更新:
継承を介して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);
}
}
このコントロールを使用するには、いくつかのオプションがあります。 1つは、コントロールを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から継承する新しいコントロールを作成し、renderメソッドをオーバーライドするか、.browserファイルを使用して、サイト内のすべてのボタンをオーバーライドできます。これは、CSS FriendlyのものがTreeViewコントロールなどで機能する方法と同様です。
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、IE 8、Firefox、Safari)。
解決策は、そのname
属性をハックすることです。それでも私はそれを理解していません。