When working with ASP.NET Web Forms adding controls to a page at runtime isn’t really a big deal. But when you switch over to MVC the concept of a control evaporates and you have to get a little creative. One option is to revert to a non-dynamic solution and statically define the definable settings. But that’s no fun. The other option (and what I’ve done) is to create your very own HTML helper that will generate a form for you. That’s what I’ll be going over today.
The settings management screen for Tombatron.com is generated based on properties that exist in the “Settings” class (that aren’t marked with the “XmlIgnore” attribute). The screen is setup this way to make it easy to add an remove user configurable properties. I’ll cover the settings management for Tombatron.com in more detail in another post.
Getting Started
First thing that we need to do is create a strongly typed view in our MVC application. If you are unfamiliar with how this is done, more information can be found here. Once the strongly typed view is created you should have something similar to this (note that I’m using a master page in my project):
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Admin.Master" Inherits="System.Web.Mvc.ViewPage<TombatronWebsiteCore.Configuration.Settings>" %> <asp:Content ID="head" ContentPlaceHolderID="HeadContent" runat="server"> </asp:Content> <asp:Content ID="title" ContentPlaceHolderID="TitleContent" runat="server"> </asp:Content> <asp:Content ID="content" ContentPlaceHolderID="MainContent" runat="server"> </asp:Content>
Now we will create a controller action that will provide this view with a “Model” to work with.
public ActionResult ManageSettings() { var settings = new SettingsManager().Load(); return View(settings); }
This controller is simply instantiating a “Settings” object and returning that object with the View as the “ActionResult” of this controller action.
Next, we need to extend the “HtmlHelper” class with a helper of our own. This helper is going to use reflection to evaluate an object passed to it in order to create an HTML form for our view page.
For my project I created a folder called “Helpers” off of the root of my MVC project. Inside of the folder I created a class called “FormExtensions”. When creating an extension method, the signature is very important. The following is the shell of my helper class as well as the extension method.
using System; using System.Web.Mvc; namespace TombatronWebsite.Helpers { public static class FormExtensions { public static string GenerateForm(this HtmlHelper helper, object formSource, string action, string controller) { //Code goes here... } } }
Implementing the Extension Method
This extension method is going to evaluate the object provided in the “formSource” parameter of the “GenerateForm” method using reflection. Then using a StringBuilder object the method will generate the HTML necessary to render a proper form.
public static string GenerateForm(this HtmlHelper helper, object formSource, string action, string controller) { var sb = new StringBuilder(); sb.AppendFormat("<form action='/{0}/{1}' method='post'>\n", controller, action); foreach (var p in formSource.GetType().GetProperties()) { sb.AppendFormat("<label for='{0}'>{0}:</label>\n", p.Name); sb.AppendFormat("<input id='{0}' name='{0}' type='text' value='{1}' />\n", p.Name, p.GetValue(formSource, null).ToString()); sb.AppendLine("<br />"); } sb.AppendLine("<input id='submitForm' name='submitForm' type='submit' value='Submit' />"); sb.AppendFormat("</form>"); return sb.ToString(); }
As you can see from the above code, generating the form is pretty straight-forward. The first order of business is to define the action of the form that we are creating. In this case I have provided two parameters for the “GenerateForm” method that allow me to specify the controller I want to use and the action I want to invoke. Next is the iteration over the properties of the specified object. Notice that for presentation purposes I have included labels for each of the form fields, for my purposes this will suffice. However, as you can imagine customizing the form generator for your purposes will be fairly easy.
Using the Extension Method
To use the extension method that we created above we need to go back to the strongly typed view that we created in the beginning. On this page we are going to call the “GenerateForm” method using the Model specified for the view as well as the name of the controller and the name of the action that we expect to post to.
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Admin.Master" Inherits="System.Web.Mvc.ViewPage<TombatronWebsiteCore.Configuration.Settings>" %> <%@ Import Namespace="TombatronWebsite.Helpers" %> <asp:Content ID="head" ContentPlaceHolderID="HeadContent" runat="server"> </asp:Content> <asp:Content ID="title" ContentPlaceHolderID="TitleContent" runat="server"> Tombatron - Site Administration (Settings Management) </asp:Content> <asp:Content ID="content" ContentPlaceHolderID="MainContent" runat="server"> <%=Html.GenerateForm(Model,"UpdateSettings","Admin") %> </asp:Content>
Notice in the above code the extra “Import” statement at the top of the view, this is the namespace where my HTML helper extension was placed.
Because the names of the form fields on our view are well defined and match those of the “Settings” object itself, we are able to specify the a parameter on the “UpdateSettings” action of the “Admin” controller with the type of “Settings”. MVC will hydrate the object with like named input fields from the form submission. The following is the shell of “UpdateSettings” action I have defined in my “Admin” controller.
[AcceptVerbs(HttpVerbs.Post)] public ActionResult UpdateSettings(Settings settings) { //Save the settings. }
Going Further
Check out the following links:
Creating Custom HTML Helpers
Creating Extension Methods (MSDN)
Subscribe