I have the following 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>
I want to make a custom html helper and use it like this (similar to Html.BeginForm
)
@Html.BeginView()
{
<table>
<tr>
<td ><label for="availableLanguages">Available Languages:</label></td>
</tr>
</table>
}
I started making my helper method
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();
}
}
I read how to make basic html helper but the examples I saw does not give me information how to make it in my case. I am very new to asp mvc and every help will be greatly appreciated.
UPDATE 2:
Obviously I am missing something. I am calling this in my view:
@Html.BeginView()
{
<table>
<tr>
<td ><label >test</label></td>
</tr>
</table>
}
Everything seems fine it even has intellisense. But the output in the browser is the following:
Omega.UI.WebMvc.Helpers.BeginViewHelper+MyView {
test
}
This is my helper method:
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>");
}
}
}
}
and I have registered the namespace in ~/Views/web.config
<add namespace="Omega.UI.WebMvc.Helpers" />
You can't return MvcHtmlString. Instead of that you should write html to the writer and return class that implement IDisposable and during invoking Dispose will write closing part of your 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>");
}
}
}
If you have more complex structure you can try to use TagBuilder:
TagBuilder tb = new TagBuilder("div");
helper.ViewContext.Writer.Write(tb.ToString(TagRenderMode.StartTag));
Slawek has the correct answer, but I thought I would add to it with my experience.
I wanted to create a helper to display widgets on a page (almost like jQuery's widgets with the title bar and a content portion). Something to the effect of:
@using (Html.BeginWidget("Widget Title", 3 /* columnWidth */))
{
@* Widget Contents *@
}
The MVC source uses something similar to what Slawek posted, but I feel like placing the start tag in the helper and end tag in the actual object wasn't "tidy", nor did it keep concerns in the corect place. Should I want to change the appearance, I'm now doing so in two places instead of what I felt was one logical place. So I came up with the following:
/// <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's 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
}
Then, that makes our helper a little more clear (and no UI code in two places):
public static HtmlWidget BeginWidget(this HtmlHelper htmlHelper, String title, Int32 columnWidth = 12)
{
return new HtmlWidget(htmlHelper.ViewContext, title, columnWidth);
}
Then we can use it as I've done at the top of this post.
The BeginForm
method of asp.net mvc returns a IDisposable
instance of the MvcForm
class. If you look inside the asp.net mvc code on codeplex, you can check how the asp.net mvc team has developed this feature.
Take a look at theses links:
MvcForm class (IDisposable)
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/8b17c2c49f88#src/System.Web.Mvc/Html/MvcForm.cs
Forms Extensions (for html helper)
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/8b17c2c49f88#src/System.Web.Mvc/Html/FormExtensions.cs