After working on the login usercontrol, I decided that I wanted to add similar functionality to the navigation sections of the site for accounts/roles that didn’t get ads displayed. So I needed a standard way to put dynamic sections into a page.
I liked the syntax used by the form HtmlHelper methods, so decided to base it off of the SimpleForm class and ended up with this
//Based on the SimpleForm class in MvcToolkit
//Wish I believed I would have ever thought to use
//IDisposable to do this
public class SimpleWrappingTag : IDisposable
{ protected string _beginTagFormat = "<{0} {1}>"; protected string _attributeFormat = "{0}="{1}" "; bool _startTagRendered = false; bool _endTagRendered = false; protected IHttpContext _context; protected object _htmlAttributes = null; //Not letting this be modified outside the class for now //might change later public string TagName { get; protected set; } //Subclassed controls can put their class specific info here public SimpleWrappingTag(string tagName) { this.TagName = tagName; } public SimpleWrappingTag(string tagName, IHttpContext context , object htmlAttributes) { this.TagName = tagName; this._context = context; this._htmlAttributes = htmlAttributes; } /// <summary> /// Creates a StringBuilder and passes it to an overloaded method. /// Override this method for attributes that should be first in tag. /// </summary> /// <returns></returns> protected virtual StringBuilder BuildAttributeList() { return BuildAttributeList(new StringBuilder()); } protected virtual StringBuilder BuildAttributeList(StringBuilder sb) { sb.Append(_htmlAttributes.ToAttributeList()); return sb; } //Method orginally from SimpleForm, but changed to work with //BuildAttributeList instead of BuildFormOpenTag public void WriteStartTag() { if (!this._startTagRendered) { string formTag = String.Format("<{0} {1}>", TagName , BuildAttributeList()); this._context.Response.Write(formTag); } } public void WriteEndTag() { if (!this._endTagRendered) this._context.Response.Write(String.Format("</{0}>", TagName)); } public void Dispose() { WriteEndTag(); } }
Next I made some trivial, but needed extension methods for the HtmlHelper class.
public static class DevExamplesExtensions { public static IDisposable HtmlTag(this HtmlHelper helper, string tagName) { return helper.HtmlTag(tagName, null); } public static IDisposable HtmlTag(this HtmlHelper helper , string tagName, object htmlAttributes) { SimpleWrappingTag tag = new SimpleWrappingTag(tagName , helper.ViewContext.HttpContext, htmlAttributes); tag.WriteStartTag(); return tag; } }
And while I’m satisfied with how this turned out, though I’m hoping to be able to delete the code at some point, I don’t like how the code I wrote to implement an Ajax.UpdatePanel method turned out. So I’m not going to post it at the moment.
But it was going to look something like
public static UpdatePanel(this AjaxHelper helper, string panelId, ….)
{
helper.ViewContext.HttpContext.Write(“update panel js….”);
IDisposable tag = new HtmlHelper(helper.ViewContext.HttpContext)
.HtmlTag(“div”,new { id=panelId});
}
Below is a list of things I couldn’t think of a good solution for at the moment
- I didn’t like the fact that I was putting the actual JavaScript into the UpdatePanel extension method. Rob Conery said something on the forums about using services to provide the view with site-wide info that seemed interesting, but not sure of a good way to implement this would be.
It would be nice to have service providers kind of like you can use in Windows Workflow Foundation or VS control designers, that would let me add a IScriptService to do the actual JavaScript coding based on the script library I wanted to use.
Maybe I should just create a script provider for this.
- Having to pass in a list of “triggering” controls felt kind of clunky. I’m trying to think of the best way to allow me to specify that a Html.ActionLink should register itself with the “UpdatePanel”.
Most of what I think of for this, basically requires me to implement my own versions of the stuff already implemented in the MvcToolkit.
- I couldn’t come up with a clean way to have the panel in the Usercontrol, but only have it be done if the usercontrol is called from a page instead of as a standalone view. I guess I could have the controller tell it this, but ideally they would just kind of know.
I think I may have already thought of the solution #3, and a workable #2. But I am really hesitant to do anything with #1 (outside the provider maybe) since I doubt I would do it close enough that I wouldn’t need to remove it all to move over to their way, should they implement something like service providers.
I think I’m going to put this aside for a while on another part of the site, this just is a “nice to have” feature, and get some of the other pieces that I actually need to have for the site done.
* 4 actually, but I said how I wished I could implement an interface using an anonymous type.