I am trying to write a BeginForm style html helper that uses IDisposable to wrap other code. I want the helper to only render the wrapped code if a certain condition is met (e.g. user is in a certain role).
I thought that I could simply switch the context.Writer in the Begin method and switch it back in the Dispose method. The code below compiles and runs but the wrapped content gets rendered in all cases. If I step through it, the wrapped content is not written to the new StringWriter and therefore not within my control.
public static IDisposable BeginSecure(this HtmlHelper html, ...)
{
return new SecureSection(html.ViewContext, ...);
}
private class SecureSection : IDisposable
{
private readonly ViewContext _context;
private readonly TextWriter _writer;
public SecureSection(ViewContext context, ...)
{
_context = context;
_writer = context.Writer;
context.Writer = new StringWriter();
}
public void Dispose()
{
if (condition here)
{
_writer.Write(_context.Writer);
}
_context.Writer = _writer;
}
}
Is what I am trying to do possible with html helpers?
I know that declarative html helpers in razor would probably work but would prefer standard html helper approach if possible, given the app_code limitation of razor helpers in MVC3.
Actually you can conditionally hide content with a BeginForm-like structure. It only involves messing with the internal StringBuilder a bit:
public class Restricted: IDisposable
{
public bool Allow { get; set; }
private StringBuilder _stringBuilderBackup;
private StringBuilder _stringBuilder;
private readonly HtmlHelper _htmlHelper;
/// <summary>
/// Initializes a new instance of the <see cref="Restricted"/> class.
/// </summary>
public Restricted(HtmlHelper htmlHelper, bool allow)
{
Allow = allow;
_htmlHelper = htmlHelper;
if(!allow) BackupCurrentContent();
}
private void BackupCurrentContent()
{
// make backup of current buffered content
_stringBuilder = ((StringWriter)_htmlHelper.ViewContext.Writer).GetStringBuilder();
_stringBuilderBackup = new StringBuilder().Append(_stringBuilder);
}
private void DenyContent()
{
// restore buffered content backup (destroying any buffered content since Restricted object initialization)
_stringBuilder.Length = 0;
_stringBuilder.Append(_stringBuilderBackup);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if(!Allow)
DenyContent();
}
}
Then you just need to make an HtmlHelper that makes an instance of the above object
public static class RestrictedHelper
{
public static Restricted RestrictedContent(this HtmlHelper htmlHelper, bool allow)
{
return new Restricted(htmlHelper, allow);
}
}
Usage is as follows:
@using (var restricted = Html.Restricted(true))
{
<p>This will show up</p>
}
@using (var restricted = Html.Restricted(false))
{
<p>This won't</p>
}
Advantages:
Tested with ASP.Net MVC 4
You can't conditionally render the body contents of a helper method returning IDisposable
. It will always render. You could use this style of helpers when you want to wrap the body of the using
block with some custom markup such as the BeginForm
helper does with the <form>
element.
You could use a templated Razor delegate
instead:
public static class HtmlExtensions
{
public static HelperResult Secure(this HtmlHelper html, Func<object, HelperResult> template)
{
return new HelperResult(writer =>
{
if (condition here)
{
template(null).WriteTo(writer);
}
});
}
}
and then:
@Html.Secure(
@<div>
You will see this text only if some condition is met
</div>
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With