web-dev-qa-db-ja.com

ASP.NETでボタンタグを使用するにはどうすればよいですか?

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プロパティを利用したい場合、乗り越えられない可能性があります。

私はレンダリングを進歩させました:

ModernButton.cs

これは正しい値を持つ適切なhtml <button>としてレンダリングされますが、ASP.Net PostBackシステムでは機能しません。 Commandイベントを提供するために必要なものをいくつか書きましたが、起動しません。

このボタンを通常のasp:Buttonと並べて調べると、必要な違い以外は同じように見えます。この場合、ASP.NetがCommandイベントをどのように結び付けているのかわかりません。

追加の問題は、ネストされたサーバーコントロールがレンダリングされないことです(ParseChildren(false)属性で確認できます)。レンダリング中にリテラルHTMLテキストをコントロールに挿入するのは非常に簡単ですが、ネストされたサーバーコントロールのサポートをどのように許可しますか?

62
Adam Lassek

これは古い質問ですが、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を追加することはできません。

LinkBut​​ton(クレジット 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(&#39;uxSearch&#39;,&#39;&#39;)">
    <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
    Search
</a>

長所

  • よさそうだ
  • Commandイベント; CommandNameおよびCommandArgumentプロパティ

短所

  • <a>の代わりに<button>をレンダリングします
  • レンダリングし、邪魔な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>

長所

  • よさそうだ
  • 適切な<button>要素をレンダリングします

短所

  • Commandイベントなし; CommandNameまたはCommandArgumentプロパティなし
  • ServerClickイベントを処理するために、邪魔なJavaScriptをレンダリングして依存します

この時点で、組み込みのコントロールはどれも適切ではないことは明らかであるため、次の論理ステップは、目的の機能を実現するためにそれらを変更することです。

カスタムコントロール( Dan Herbert's answer

注:これはダンのコードに基づいているため、すべてのクレジットは彼に与えられます。

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>要素をレンダリングします
  • Commandイベント; CommandNameおよびCommandArgumentプロパティ
  • 目立たないJavaScriptをレンダリングしたり、依存したりしません

短所

  • なし(組み込みコントロールでないこと以外)
36
Daniel Liuzzi

[button runat = "server"]を使用するだけでは十分な解決策ではないと言いますが、それを言及することは重要です。多くの.NETプログラマーは「ネイティブ」HTMLタグの使用を恐れています...

つかいます:

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

これは通常、完璧に機能し、私のチームでは誰もが幸せです。

42
Philippe

私はあなたの質問に同じものを探してつまずいた。最終的に 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のサポートを追加するのはそれほど難しくありませんが、必要に応じて簡単に追加できます。

29
Dan Herbert

Buttonから継承する新しいコントロールを作成し、renderメソッドをオーバーライドするか、.browserファイルを使用して、サイト内のすべてのボタンをオーバーライドできます。これは、CSS FriendlyのものがTreeViewコントロールなどで機能する方法と同様です。

6
Steven Robbins

LinkBut​​tonを使用して、必要に応じてCSSでスタイルを設定します。

2

私はこれに一日中苦労してきました-私のカスタム<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属性をハックすることです。それでも私はそれを理解していません。

1
David Andersson