Как использовать тег кнопки с ASP.NET?

Я хотел бы использовать новый тег <button> на веб-сайте ASP.NET, который, среди прочего, позволяет использовать текст в стиле CSS и встраивать графику внутри кнопки. Элемент управления asp: Button отображается как <input type="button">, есть ли способ сделать существующий визуализацию управления для <button>?

Из того, что я прочитал, существует несовместимость с IE, размещающая разметку кнопки вместо атрибута value, когда кнопка находится в пределах <form>, но в ASP.NET она будет использовать событие onclick для запуска __doPostBack во всяком случае, поэтому я не думаю, что это будет проблемой.

Есть ли причины, по которым я не должен использовать это? Если нет, как бы вы могли поддержать его с помощью asp: Button или нового серверного элемента управления на его основе? Я бы предпочел не писать собственный серверный контроль, если этого можно избежать.


Сначала работало решение <button runat="server">, но я сразу же столкнулся с ситуацией, когда ему нужно иметь свойство CommandName, которого не имеет элемент управления HtmlButton. Похоже, мне понадобится создать элемент управления, унаследованный от Button.

Что мне нужно сделать, чтобы переопределить метод рендеринга и сделать его рендерингом, что я хочу?


UPDATE

Ответ DanHerbert заставил меня задуматься над тем, чтобы найти решение для этого снова, поэтому я потратил еще немного времени на его работу.

Во-первых, гораздо проще перегрузить TagName:

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

Проблема с решением Dan в нем заключается в том, что innerhtml тега помещается в свойство value, что вызывает ошибку проверки при обратной передаче. Связанная с этим проблема заключается в том, что даже если вы правильно оцениваете свойство value, реализация IE braindead тега <button> помещает innerhtml вместо значения в любом случае. Таким образом, любая реализация этого требует переопределения метода AddAttributesToRender, чтобы правильно отобразить свойство value, а также обеспечить какой-то обходной путь для IE, чтобы он не полностью испортил обратную передачу.

Проблема IE может быть непреодолимой, если вы хотите использовать свойства CommandName/CommandArgument для управления базой данных. Надеюсь, кто-то может предложить обходное решение для этого.

Я добился прогресса в рендеринге:

ModernButton.cs

Это отображает правильное значение html <button> с правильным значением, но оно не работает с системой ASP.Net PostBack. Я написал некоторые из того, что мне нужно, чтобы предоставить событие Command, но оно не срабатывает.

При проверке этой кнопки бок о бок с помощью обычной кнопки asp: они выглядят одинаково, кроме различий, которые мне нужны. Поэтому я не уверен, как ASP.Net подключает событие Command в этом случае.

Дополнительная проблема заключается в том, что вложенные элементы управления сервером не отображаются (как вы можете видеть с атрибутом ParseChildren (false)). Легко вводить литеральный HTML-текст в элемент управления во время рендеринга, но как вы разрешаете поддержку вложенных элементов управления сервером?

Ответы

Ответ 1

Это старый вопрос, но для тех из нас, кто еще недостаточно повезло, чтобы поддерживать приложения ASP.NET Web Forms, я сам это сделал, пытаясь включить глифы Bootstrap внутри встроенных элементов управления кнопками.

В соответствии с документацией Bootstrap желаемая разметка выглядит следующим образом:

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

Мне нужна эта разметка для визуализации с помощью серверного элемента управления, поэтому я решил найти параметры.

Кнопка

Это был бы первый логический шаг, но, как выясняется в этом вопросе, Button отображает элемент <input> вместо <button>, поэтому добавление внутреннего HTML невозможно.

LinkButton (кредит Ответ Целомира Цонева)

Источник

<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>

Pros

  • выглядит нормально
  • Command событие; CommandName и CommandArgument свойства

против

  • Renders <a> вместо <button>
  • Оказывает и полагается на навязчивый JavaScript

HtmlButton (кредит ответ Филиппа)

Источник

<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>

Pros

  • выглядит нормально
  • Оказывает правильный элемент <button>

против

  • Нет Command событие; нет CommandName или CommandArgument свойств
  • Оказывает и полагается на навязчивый JavaScript для обработки своего события ServerClick

В этот момент ясно, что ни один из встроенных элементов управления не подходит, поэтому следующий логический шаг - попробовать и изменить их для достижения желаемой функциональности.

Пользовательский контроль (кредит ответ Дэн Герберт)

ПРИМЕЧАНИЕ. Это основано на коде Дэн, так что все его кредиты.

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 переопределить (кажется ненужным)
  • Удаление HTML-декодирования из Text (базовый класс уже обрабатывает это)
  • Оставьте OnPreRender неповрежденным; переопределить AddParsedSubObject вместо этого (проще)
  • Упростить RenderContents переопределить
  • Добавить свойство Value (см. ниже)
  • Добавьте пространство имен (чтобы включить образец директивы @Register)
  • Добавьте необходимые директивы using

Свойство Value просто обращается к старому свойству Text. Это связано с тем, что собственный элемент управления Button в любом случае предоставляет атрибут ValueText в качестве значения). Поскольку 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>

Pros

  • выглядит нормально
  • Отображает правильный элемент <button>
  • Command событие; CommandName и CommandArgument свойства
  • Не отображает или не полагается на навязчивый JavaScript

против

  • Нет (кроме встроенного управления)

Ответ 2

Хотя вы говорите, что использование кнопки [button runat = "server" ] не является достаточно хорошим решением, важно упомянуть об этом - многие программисты .NET боятся использовать "родные" HTML-теги...

Использование:

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

Это обычно отлично работает, и все довольны моей командой.

Ответ 3

Я наткнулся на ваш вопрос, ища то же самое. Я закончил использование Reflector, чтобы выяснить, как фактически выполняется управление ASP.NET Button. Это действительно очень легко изменить.

На самом деле это сводится к переопределению свойств TagName и TagKey класса Button. После того, как вы это сделали, вам просто нужно убедиться, что вы отображаете содержимое кнопки вручную, так как в исходном классе 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 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 не должно быть ужасно трудным, хотя при необходимости это можно легко добавить.

Ответ 4

Вы можете создать новый элемент управления, наследующий от Button и переопределить метод визуализации, или использовать файл .browser для переопределения всех кнопок на сайте, подобно тому, как работает CSS Friendly для элемента управления TreeView и т.д.

Ответ 5

Вы можете использовать свойство Button.UseSubmitBehavior, как описано в в этой статье об рендеринге кнопки управления ASP.NET в виде кнопки.


EDIT: Извините.. это то, что я получаю для skimming вопросов на мой перерыв на обед. Есть ли причины, по которым вы не просто использовали бы кнопку runat = "server" > тег или HtmlButton?

Ответ 6

Я бы пошел с LinkButton и подгонял его с CSS.

Ответ 7

Я изо всех сил боролся с этим - мой пользовательский <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. Тем не менее я этого не понял.