Asp MVC 4 создает пользовательский метод html helper, аналогичный Html.BeginForm
У меня есть следующий html:
<div data-bind="stopBindings">
<div data-viewId="languageList" data-bind="with: viewModel">
<table>
<tr>
<td ><label for="availableLanguages">Available Languages:</label></td>
</tr>
<table>
</div>
</div>
Я хочу создать пользовательский html-помощник и использовать его так (аналогично Html.BeginForm
)
@Html.BeginView()
{
<table>
<tr>
<td ><label for="availableLanguages">Available Languages:</label></td>
</tr>
</table>
}
Я начал использовать свой вспомогательный метод
public static class BeginViewHelper
{
public static MvcHtmlString BeginView(this HtmlHelper helper, string viewId)
{
var parentDiv = new TagBuilder("div");
parentDiv.MergeAttribute("data-bind", "preventBinding: true");
return new MvcHtmlString();
}
}
Я читал, как сделать базовый html-помощник, но примеры, которые я видел, не дают мне информации, как это сделать в моем случае. Я очень новичок в asp mvc, и каждая помощь будет с благодарностью.
ОБНОВЛЕНИЕ 2:
Очевидно, что я что-то упускаю. Я нахожу это на мой взгляд:
@Html.BeginView()
{
<table>
<tr>
<td ><label >test</label></td>
</tr>
</table>
}
Все кажется прекрасным, что у него даже есть intellisense. Но вывод в браузере следующий:
Omega.UI.WebMvc.Helpers.BeginViewHelper+MyView {
test
}
Это мой вспомогательный метод:
namespace Omega.UI.WebMvc.Helpers
{
public static class BeginViewHelper
{
public static IDisposable BeginView(this HtmlHelper helper)
{
helper.ViewContext.Writer.Write("<div data-bind=\"preventBinding: true\">");
helper.ViewContext.Writer.Write("<div data-viewId=\"test\">");
return new MyView(helper);
}
class MyView : IDisposable
{
private HtmlHelper _helper;
public MyView(HtmlHelper helper)
{
this._helper = helper;
}
public void Dispose()
{
this._helper.ViewContext.Writer.Write("</div>");
this._helper.ViewContext.Writer.Write("</div>");
}
}
}
}
и я зарегистрировал пространство имен в ~/Views/web.config
<add namespace="Omega.UI.WebMvc.Helpers" />
Ответы
Ответ 1
Вы не можете вернуть MvcHtmlString. Вместо этого вы должны написать html для писателя и вернуть класс, который реализует IDisposable и во время вызова Dispose будет писать закрывающую часть вашего HTML.
public static class BeginViewHelper
{
public static IDisposable BeginView(this HtmlHelper helper, string viewId)
{
helper.ViewContext.Writer.Write(string.Format("<div id='{0}'>", viewId));
return new MyView(helper);
}
class MyView : IDisposable
{
private HtmlHelper helper;
public MyView(HtmlHelper helper)
{
this.helper = helper;
}
public void Dispose()
{
this.helper.ViewContext.Writer.Write("</div>");
}
}
}
Если у вас более сложная структура, вы можете попробовать использовать TagBuilder:
TagBuilder tb = new TagBuilder("div");
helper.ViewContext.Writer.Write(tb.ToString(TagRenderMode.StartTag));
Ответ 2
Slawek имеет правильный ответ, но я думал, что добавлю к нему свой опыт.
Я хотел создать помощник для отображения виджетов на странице (почти как виджеты jQuery с заголовком и частью контента). Что-то вроде:
@using (Html.BeginWidget("Widget Title", 3 /* columnWidth */))
{
@* Widget Contents *@
}
Источник MVC использует что-то похожее на то, что опубликовал Slawek, но мне кажется, что размещение начального тега в вспомогательном и конечном теге в фактическом объекте не было "аккуратным", и оно не беспокоило место corect. Должен ли я изменить внешний вид, теперь я делаю это в двух местах, вместо того, что я чувствовал, было одно логическое место. Поэтому я придумал следующее:
/// <summary>
/// Widget container
/// </summary>
/// <remarks>
/// We make it IDIsposable so we can use it like Html.BeginForm and when the @using(){} block has ended,
/// the end of the widget content is output.
/// </remarks>
public class HtmlWidget : IDisposable
{
#region CTor
// store some references for ease of use
private readonly ViewContext viewContext;
private readonly System.IO.TextWriter textWriter;
/// <summary>
/// Initialize the box by passing it the view context (so we can
/// reference the stream writer) Then call the BeginWidget method
/// to begin the output of the widget
/// </summary>
/// <param name="viewContext">Reference to the viewcontext</param>
/// <param name="title">Title of the widget</param>
/// <param name="columnWidth">Width of the widget (column layout)</param>
public HtmlWidget(ViewContext viewContext, String title, Int32 columnWidth = 6)
{
if (viewContext == null)
throw new ArgumentNullException("viewContext");
if (String.IsNullOrWhiteSpace(title))
throw new ArgumentNullException("title");
if (columnWidth < 1 || columnWidth > 12)
throw new ArgumentOutOfRangeException("columnWidth", "Value must be from 1-12");
this.viewContext = viewContext;
this.textWriter = this.viewContext.Writer;
this.BeginWidget(title, columnWidth);
}
#endregion
#region Widget rendering
/// <summary>
/// Outputs the opening HTML for the widget
/// </summary>
/// <param name="title">Title of the widget</param>
/// <param name="columnWidth">Widget width (columns layout)</param>
protected virtual void BeginWidget(String title, Int32 columnWidth)
{
title = HttpUtility.HtmlDecode(title);
var html = new System.Text.StringBuilder();
html.AppendFormat("<div class=\"box grid_{0}\">", columnWidth).AppendLine();
html.AppendFormat("<div class=\"box-head\">{0}</div>", title).AppendLine();
html.Append("<div class=\"box-content\">").AppendLine();
this.textWriter.WriteLine(html.ToString());
}
/// <summary>
/// Outputs the closing HTML for the widget
/// </summary>
protected virtual void EndWidget()
{
this.textWriter.WriteLine("</div></div>");
}
#endregion
#region IDisposable
private Boolean isDisposed;
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
public virtual void Dispose(Boolean disposing)
{
if (!this.isDisposed)
{
this.isDisposed = true;
this.EndWidget();
this.textWriter.Flush();
}
}
#endregion
}
Затем это делает наш помощник немного более понятным (и без кода UI в двух местах):
public static HtmlWidget BeginWidget(this HtmlHelper htmlHelper, String title, Int32 columnWidth = 12)
{
return new HtmlWidget(htmlHelper.ViewContext, title, columnWidth);
}
Затем мы можем использовать его, как я сделал в верхней части этого сообщения.
Ответ 3
Метод BeginForm
asp.net mvc возвращает экземпляр IDisposable
класса MvcForm
. Если вы заглянете в код asv.net mvc на codeplex, вы можете проверить, как команда asp.net mvc разработала эту функцию.
Взгляните на тезисы ссылок:
Класс MvcForm (IDisposable)
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/8b17c2c49f88#src/System.Web.Mvc/Html/MvcForm.cs
Расширения форм (для html-помощника)
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/8b17c2c49f88#src/System.Web.Mvc/Html/FormExtensions.cs