I'm writing an html helper extension for MVC5.
I need to access the ViewContext's stream to edit it.
(like a textreader)
htmlHelper.ViewContext.Writer.Write("<div>");
Is there any way to access the stream; so that I can manipulate or add some html code above the last "div" tag in the example ?
I was not able to find any clue about at where I can find the stream that textwriter writes to?
It depends on what view engine you are using but if you write an html helper, it will be a lot easier. See Creating Custom HTML Helpers
Simple html helpers can be written like this:
#helper CustomDiv(var param1, var param2){
Write("something to html encode & stuff");
WriteLiteral("<div>something that is html</div>");
}
In Razor, You can also just call #Write() and it will encode the string to HTML. If you need a TextWriter, reference Context.Response.Output but this will not allow you to manipulate the HTML inline like an HtmlHelper does - it's intended more for overriding the normal handler.
or just the Output property of WebPageBase which is also a TextWriter
Related
I would like to create an extension helper with the following signature:
public static MvcHtmlString
BindMissingFor(this HtmlHelper
htmlHelper, Expression> expression )
I would like this method to reflect through the supplied expression model and look for bind-able properties that have not already been bound on the form.
The use case is I would like to have some Views that only allow the user to interact with a portion of my Model. But, I would like to persist the entire model between multiple views (a wizard).
My current solution is to use a hidden-input for each field I don't want displayed. I'll probably do the same thing with this extension method, but I would like it to do the work for me instead of copy/pasting.
Is there a way to inspect the current form for inputs/selects from within an HtmlHelper extension method?
There is no way an html helper to know what happens in other parts of your view such as inspecting other form fields unless you pass it as argument. Also it is not very clear what you mean by look for bind-able properties that have not already been bound on the form. For persisting state in a wizard you might take a look at the Html.Serialize helper currently situated in the MVC Futures assembly. The idea behind this helper is that it allows you to serialize some model object (marked as [Serializable]) as hidden field inside a form and get its value back in a controller action using the [Deserialize] attribute. Behind the scenes it uses WebForms ViewState. You can also encrypt it. It is a good way of persisting state on the client between multiple pages.
I'm trying to create a custom HTML helper and I would like to know how I can access the Model object without passing it as a parameter.
Thanks
If you are using strongly typed views which you should:
public static MvcHtmlString MyHelper<TModel>(this HtmlHelper<TModel> htmlHelper)
{
TModel model = htmlHelper.ViewData.Model;
return MvcHtmlString.Empty;
}
If you are not using strongly typed views which you shouldn't:
public static MvcHtmlString MyHelper(this HtmlHelper htmlHelper)
{
object model = htmlHelper.ViewData.Model;
return MvcHtmlString.Empty;
}
HTML helpers are a bad way to generate HTML programmatically. Web Forms is much better with code in a page class file and HTML markup in a separate file. Yes HTML helpers put some code in separate class files but you are calling code from your HTML page. Whats to stop you from writing code directly in your view page. MVC is supportive of lots of bad practices which you don't have to do but for some reason in Web Forms developers have to do bad practices because it is allowed. If you learn Web Forms well, you will develop maintainable and scalable web applications using modern object oriented patterns instead of procedural logic like HTML helpers.
The Problem
I have a very nifty menu Html helper written for WebFormViewEngine views. This engine allows your helpers to return void, and still be able to use:
#Html.Theseus
This is great for my helper, because it can then render the menu using HtmlTextWriter, that renders directly to the output stream.
In Razor views, however, the Html helpers are expected to return a value (usually MvcHtmlString) which is what gets added to the output. Small difference, big consequence.
There is a way around this, as pointed out to me by GvS (see ASP.NET MVC 2 to MVC 3: Custom Html Helpers in Razor) as follows:
If the helper returns void, then do the following:
#{Html.Theseus;}
(Essentially, you are just calling the method, rather than rendering into the view).
Whilst still neat, this is not quite the same as #Html.Theseus. So...
My code is complex but works very well, so am loath to go through major edits, ie, replacing the HtmlTextWriter with another writer. A snippet of the code goes like:
writer.AddAttribute(HtmlTextWriterAttribute.Href, n.Url);
writer.AddAttribute(HtmlTextWriterAttribute.Title, n.Description);
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.WriteEncodedText(n.Title);
writer.RenderEndTag();
// Recursion, if any
// Snip off the recursion at this level if specified by depth
// Use a negative value for depth if you want to render the entire sitemap from the starting node
if ((currentDepth < depth) || (depth < 0))
{
if (hasChildNodes)
{
// Recursive building starts here
// Open new ul tag for the child nodes
// "<ul class='ChildNodesContainer {0} Level{1}'>";
writer.AddAttribute(HtmlTextWriterAttribute.Class, "Level" + currentDepth.ToString());
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
// BuildMenuLevel calls itself here to
// recursively traverse the sitemap hierarchy,
// building the menu as I go.
// Note: this is where I increase the currentDepth variable!
BuildChildMenu(currentDepth + 1, depth, n, writer);
// Close ul tag for the child nodes
writer.RenderEndTag();
}
}
It wouldn't be fun to re write with TagBuilders. As it stands, it renders any type of menu, including the "Incremental Navigation" as described in my 4guysfromrolla article:
Implementing Incremental Navigation with ASP.NET
The Options:
I guess I could return an empty MvcHtmlString, but that is pretty much the definition of a hack...
The only alternative is to head off into the sunset and rewrite the helper using the TagBuilder to build each tag, add that to a StringBuilder, then build the next tag, etc, and then use the StringBuilder instance to create the MvcHtmlString. Seriously ugly, unless I could do something like...
The Question:
Is there a way to:
Stop the HtmlTextWriter rendering to the stream and instead use it like a StringBuilder that at the end of the process I use to create an MvcHtmlString (or HtmlString)?
Sounds unlikely, even as I write...
PS:
The great thing about the HtmlTextWriter is that you can build large quantities of tags, instead of building them one by one as with a TagBuilder.
Contrary to the responses you received for your other question Razor does not require that you return an HtmlString. The problem with your code right now is that you are writing directly to the response stream. Razor executes things inside-out which means that you can mess up the response order (see a similar question).
So in your case you could probably do this (though i haven't tested it):
public static void Theseus(this HtmlHelper html)
{
var writer = new HtmlTextWriter(html.ViewContext.Writer);
...
}
Edit (follow up to address your comments):
Html Helpers are perfectly capable of either returning a HtmlString directly or void and writing to the context writer. For example, both Html.Partial and Html.RenderPartial work fine in Razor. I think what you are confusing is the syntax required to call one version and not the other.
For example, consider an Aspx view:
<%: Html.Partial("Name") %>
<% Html.RenderPartial("Name") %>
You call each method differently. If you flip things around, things will just not work. Similarly in Razor:
#Html.Partial("Name")
#{ Html.RenderPartial("Name"); }
Now it just so happens that the syntax to use a void helper is a lot more verbose in Razor compared to Aspx. However, both work just fine. Unless you meant something else by "the issue is with a html helper not being able to return void".
By the way, if you really want to call your helper using this syntax: #Html.Theseus() you could do this:
public static IHtmlString Theseus(this HtmlHelper html)
{
var writer = new HtmlTextWriter(html.ViewContext.Writer);
...
return new HtmlString("");
}
But that's a bit of a hack.
Our designers have come up with button styles for an application which require the addition of <span> tags inside the <a> tags of our links.
In ASP.NET we implemented this by adding an App_Browsers entry for Link Buttons.
How would I go about doing this in ASP.NET MVC?
I've contemplated creating my own versions of all of the various HTML helper functions for creating ActionLinks and RouteLinks but this seems to be quite a 'brute force' way of doing things.
Is there a nice elegant way of doing it?
I know we could write some simple jQuery to do it, but we'd rather have the markup coming out of the server correctly in the first place.
Actually I think writing a new helper is exactly the way I would go. Seems to me that that's exactly what they are there for and it makes them very re-usable too.
You could always write one extension method, that takes another one (one of the built-in ones) as an argument, and wrappes the <span> around your link text before calling it. It should be quite easy to do with lambdas...
public static string SpanLink(this HtmlHelper helper,
string linkText, object args, Action<string> action)
where TController : IController
{
action("<span>" + linkText + "</span>", args);
}
And to call it:
<%= Html.SpanLink<HomeController>("link text", (s) => Html.ActionLink<HomeController>(c => c.Index(s));
(This code is typed directly into the answer field of SO - I haven't even checked it to make sure it compiles. So bear with me if it doesn't work on the first try...)
I have a partial view that should not be cached in a output cached MVC view.
Usually you write non-cached content by using Response.WriteSubstitution.
The problem is that WriteSubstitution takes as a parameter a HttpResponseSubstitutionCallback callback which looks like this:
public delegate string HttpResponseSubstitutionCallback(System.Web.HttpContext context)
This is where things get complicated since there is no easy/fun way to generate the html on the fly.
You have to do a hack like this.
So the question is: Is there an easier way to make a partial view not cached ?
See Phil Haack's article on donut caching in MVC. Phil takes advantage of the existing API to create a new HtmlHelper method to provide a callback that can render the non-cached code. His supplies an anonymous method to the helper to specify the callback. To get this to work unchanged, you'll still need to have a method that renders a partial view to a string, though I think it would be easier to do in an HtmlHelper -- just look at the source for RenderPartial and RenderPartialInternal and see what changes would be needed to write it to a MemoryStream instead of the Response -- I believe it would be the same code except you'd supply your stream instead of the Response output stream, then convert your stream to a string.
It might look like this:
<%= Html.Substitute( cb => Html.RenderToString( "Partial" ) ) %>
Phil indicates that it might make it in to ASP.NET MVC 1.0, but I think it's only available in the MvcFutures assembly.
In MVC2 we can use Html.Action to easily obtain the substituted html. Yeeeeiii
But Response.WriteSubstitotion is not working anymore. Aaaahhhh