Как использовать тег кнопки с 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 невозможно.
Источник
<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>
Pros
- выглядит нормально
-
Command
событие; CommandName
и CommandArgument
свойства
против
- Renders
<a>
вместо <button>
- Оказывает и полагается на навязчивый JavaScript
Источник
<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 в любом случае предоставляет атрибут 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>
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
. Тем не менее я этого не понял.