Я хотел бы использовать новый тег <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.
Что мне нужно сделать, чтобы переопределить метод рендеринга и заставить его рендерить то, что я хочу?
ОБНОВИТЬ
Ответ Дэна Херберта заставил меня снова заинтересоваться решением этой проблемы, поэтому я потратил немного времени на работу над этим.
Во-первых, есть гораздо более простой способ перегрузить TagName:
public ModernButton() : base(HtmlTextWriterTag.Button)
{
}
Проблема с решением Дэна в его нынешнем виде заключается в том, что innerhtml тега помещается в свойство value, что вызывает ошибку проверки при обратной передаче. Связанная с этим проблема заключается в том, что даже если вы правильно визуализируете свойство value, глупая реализация IE тега <button> в любом случае отправляет innerhtml вместо значения. Итак, любая реализация этого должна переопределить метод AddAttributesToRender, чтобы правильно отобразить свойство value, а также предоставить какой-то обходной путь для IE, чтобы он не полностью испортил обратную передачу.
Проблема IE может оказаться непреодолимой, если вы хотите воспользоваться преимуществами свойств CommandName / CommandArgument для элемента управления с привязкой к данным. Надеюсь, кто-то может предложить обходной путь для этого.
Я добился прогресса в рендеринге:
Это отображается как правильный HTML <button> с правильным значением, но не работает с системой ASP.Net PostBack. Я написал кое-что из того, что мне нужно для предоставления события Command, но оно не срабатывает.
При проверке этой кнопки рядом с обычной кнопкой asp: они выглядят одинаково, за исключением тех различий, которые мне нужны. Поэтому я не уверен, как ASP.Net подключает событие Command в этом случае.
Дополнительная проблема заключается в том, что вложенные серверные элементы управления не отображаются (как вы можете видеть с помощью атрибута ParseChildren (false)). Во время рендеринга довольно легко ввести буквальный HTML-текст в элемент управления, но как разрешить поддержку вложенных серверных элементов управления?





Я бы выбрал LinkButton и при необходимости стилизовал его с помощью CSS.
Его можно оформить так, чтобы он выглядел как единое целое. Вот например: hedgerwow.com/360/dhtml/…
Кнопки обрабатываются операционной системой, и каждая из них выглядит по-разному. В некоторых операционных системах они также могут быть тематическими. Если бы я сделал кнопку так, чтобы она выглядела как кнопка Vista, она бы везде выглядела неконгруэнтной.
Если это то, что вам нужно ... Я думал о полном изменении стиля кнопок, когда вы упомянули CSS и изображения. В любом случае, просто следите за IE;)
Вы можете создать новый элемент управления, унаследованный от Button, и переопределить метод рендеринга или использовать файл .browser для переопределения всех кнопок на сайте, аналогично тому, как CSS Friendly работает для элемента управления TreeView и т. д.
Похоже, что переопределение метода рендеринга может быть частью решения, которое мне нужно. Вы сами это сделали? Можете ли вы уточнить детали?
Я не делал этого для кнопки, но сделал это для других элементов управления. Я вставлю код, когда вернусь на свой компьютер, если меня никто не опередит.
Вы можете использовать свойство Button.UseSubmitBehavior, как описано в эта статья о рендеринге элемента управления ASP.NET Button как кнопки.
Я наткнулся на ваш вопрос в поисках того же самого. В итоге я использовал Отражатель, чтобы выяснить, как на самом деле отображается элемент управления ASP.NET Button. Оказывается, это действительно легко изменить.
На самом деле все сводится к переопределению свойств TagName и TagKey класса Button. После того, как вы это сделали, вам просто нужно убедиться, что вы визуализируете содержимое кнопки вручную, поскольку исходный класс Button никогда не имел содержимого для визуализации, и элемент управления будет отображать кнопку без текста, если вы не визуализируете содержимое.
Обновлять:
Можно внести несколько небольших изменений в элемент управления Button посредством наследования, и при этом он будет работать достаточно хорошо. Это решение избавляет от необходимости реализовывать собственные обработчики событий для OnCommand (хотя, если вы хотите узнать, как это сделать, я могу показать вам, как это обрабатывается). Он также устраняет проблему отправки значения с разметкой, за исключением, вероятно, IE. Я все еще не уверен, как исправить плохую реализацию тега Button в IE. Это может быть просто техническое ограничение, которое невозможно обойти ...
[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. Я не тестировал это, так что это может уже работать, но я сомневаюсь, что это так. Я думаю, вам нужно будет добавить некоторый управляющий код ViewState для обработки субэлементов в PostBacks, но, вероятно, это не проблема для вас. Добавление поддержки ViewState не должно быть очень сложным, хотя при необходимости ее можно легко добавить.
Вы на полпути, но основной причиной использования тега кнопки является возможность добавить внутри него разметку, например встроенное изображение. Кажется, это самая сложная часть - разрешить содержимое внутри серверного элемента управления. Я не нашел описания, как это сделать.
Это очень легко сделать. Я обновлю свой ответ, чтобы продемонстрировать это.
Это помогло мне приблизиться к решению, спасибо. Однако есть еще несколько важных вещей, которые необходимо решить, прежде чем это сработает. Подробнее см. Мой измененный вопрос.
+1 Это наиболее полный ответ, так как нет встроенного элемента управления, отвечающего требованиям вопроса. См. Мой ответ ниже для отредактированной версии.
Я весь день боролся с этим - мой пользовательский элемент управления, сгенерированный <button/>, не работает:
System.Web.HttpRequestValidationException: A potentially dangerous Request.Form value was detected from the client
Что происходит, так это то, что .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. До сих пор я этого не понял.
Тег имени не проблема. См. asp.net/learn/whitepapers/request-validation. Это потому, что вместо публикации атрибута value, как предполагалось, IE вместо этого отправляет разметку из тега, а отправка обратной разметки всегда будет вызывать ошибку сервера в ASP.Net, если вы не отключите проверку запроса.
Наиболее серьезным препятствием для использования тега кнопки является тот факт, что IE ведет себя совсем не так, как все остальное. Возможно, вам придется изменить значения сообщений в javascript перед их отправкой на сервер, чтобы он вел себя предсказуемо.
@ Адам Да, вы правы, вот в чем проблема. Есть ли у вас обходной путь для этого? Без использования javascript.
Да - не используйте IE;) В противном случае вам придется манипулировать значениями Post, и javascript - единственный способ сделать это.
К сожалению, я пришел к выводу, что тег кнопки не использовать вообще. Мне это нравится, но для моего использования уровень проникновения IE составляет 50%.
Хотя вы говорите, что использование [button runat = "server"] не является достаточно хорошим решением, важно упомянуть об этом - многие программисты .NET боятся использовать «родные» теги HTML ...
Использовать:
<button id = "btnSubmit" runat = "server" class = "myButton"
onserverclick = "btnSubmit_Click">Hello</button>
Обычно это работает отлично, и все в моей команде счастливы.
Да, это работает, хотя это не эквивалент asp: button, поскольку вы теряете много функциональности. В моем случае кнопка находится внутри элемента управления с привязкой к данным, поэтому она имеет очень ограниченную полезность без CommandName и CommandArgument.
Вы можете добавлять настраиваемые атрибуты в такие элементы управления и легко извлекать значения в коде.
Похоже, что это нарушает причиныvalidation = "false"
Обратите внимание, что это решение - как и LinkButton - не будет работать, если отключен JavaScript (источник).
@niico И моя проблема. Хотелось бы иметь встроенную поддержку кнопок HTML5 в веб-формах ASP.NET.
Это старый вопрос, но для тех из нас, кому не повезло, которым все еще приходится поддерживать приложения веб-форм ASP.NET, я сам прошел через это, пытаясь включить глифы 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>
Плюсы
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 или CommandArgumentServerClick.На данный момент ясно, что ни один из встроенных элементов управления не кажется подходящим, поэтому следующий логический шаг - попытаться изменить их для достижения желаемой функциональности.
ПРИМЕЧАНИЕ: Это основано на коде Дэна, поэтому вся заслуга принадлежит ему.
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 (базовый класс уже обрабатывает это)OnPreRender без изменений; вместо этого заменить AddParsedSubObject (проще)RenderContentsValue (см. Ниже)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 свойстваМинусы
В настраиваемом элементе управления все еще есть ошибка: AddParsedSubObject никогда не вызывается, если вспомогательное содержимое содержит какое-либо выражение рендеринга. Пример: <mc:ModernButton runat = "server"><b><%= this.AnyString %></b></mc:ModernButton>. Однако вы можете напрямую использовать ресурс ('<%$ Resources:AnyString %>') или выражения привязки ('<%# this.AnyString %>') в свойстве Text, но это заставит вас поместить все содержимое в одно выражение (включая HTML).
@modiX: Отлично! Это решило мою проблему. Я использовал свойство Text, чтобы добавить текст и изображение.
Насколько я могу судить, пользовательский элемент управления всегда вызывает полную обратную передачу, даже если он находится внутри UpdatePanel.
Этот настраиваемый элемент управления почти работал у меня правильно, но он не позволял правильно использовать вложенный элемент управления, значения которого были назначены на стороне сервера (AddParsedSubObject запускался до того, как значение вложенного элемента управления было изменено - даже если я переместил манипуляцию в Page_Init) . В конце концов, мне пришлось воспользоваться методами Дэна Герберта OnPreRender и RenderContents. Это позволяло изменять вложенные элементы управления на стороне сервера. Пока я успешно использую это с CommandName и CommandArgument в Repeater, а также с основной кнопкой отправки в форме. Спасибо всем!
Возможно, вам также потребуется добавить сборку: <% @ Register TagPrefix = "mc" Namespace = "ModernControls" Assembly = "myApplication"%>
Мне нужна кнопка, а не гиперссылка. Конечно, я знаю о LinkButton.