Showing posts with label Asp.Net MVC3. Show all posts
Showing posts with label Asp.Net MVC3. Show all posts

Sunday, June 12, 2011

MVC3 RadioButtonList Helper

An MVC 3 example which uses the Radio Button helper twice on a page to create 3 radio buttons with the first radio button helper and 2 radio buttons with the 2nd helper.  Both have validation to ensure 1 option is selected. And if the form fails validation the chosen radio option is preselected when the form is reshown.

The radio buttons can be shown on the page horizontally or vertically.

The Html Helper Method

Essentially all this helper does is loop round a list that you provide it and for each item in the list creates a html radio button and a label for it.

I created a new class with the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Linq.Expressions;
using System.Text;

namespace MVC3_RadioButtonList_Helper_Sample
{
    public static class HtmlExtensions
    {
        public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TProperty>> expression,
            IEnumerable<SelectListItem> listOfValues)
        {
            var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            var sb = new StringBuilder();

            if (listOfValues != null)
            {
                // Create a radio button for each item in the list
                foreach (SelectListItem item in listOfValues)
                {
                    // Generate an id to be given to the radio button field
                    var id = string.Format("{0}_{1}", metaData.PropertyName, item.Value);

                    // Create and populate a radio button using the existing html helpers
                    var label = htmlHelper.Label(id, HttpUtility.HtmlEncode(item.Text));
                    var radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id }).ToHtmlString();

                    // Create the html string that will be returned to the client
                    // e.g. <input data-val="true" data-val-required="You must select an option" id="TestRadio_1" name="TestRadio" type="radio" value="1" /><label for="TestRadio_1">Line1</label>
                    sb.AppendFormat("<div class=\"RadioButton\">{0}{1}</div>", radio, label);
                }
            }

            return MvcHtmlString.Create(sb.ToString());
        }
    }
}

I’ve added a div around the radio button and it’s label with a class of ‘RadioButton’, this will let you in CSS position the radio button either vertically or horizontally.  The default is vertically:

InitialPage

But you can switch this to horizontal with a bit of CSS

.RadioButton { float:left; }

InitialPageAsHorizontal

The rest of this article shows how to use the RadioButtonForSelectList helper, you reference it in a view like this: @Html.RadioButtonForSelectList(m => m.TestRadio, Model.TestRadioList)

The Model

For this example I just created an empty MVC3 application and added this model.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace MVC3_RadioButtonList_Helper_Sample.Models
{
    public class IndexViewModel
    {
        public IEnumerable<SelectListItem> TestRadioList { get; set; }
        public IEnumerable<SelectListItem> TestRadioList2 { get; set; }

        [Required(ErrorMessage = "You must select an option for TestRadio")]
        public String TestRadio { get; set; }

        [Required(ErrorMessage = "You must select an option for TestRadio2")]
        public String TestRadio2 { get; set; }
    }

    public class aTest
    {
        public Int32 ID { get; set; }
        public String Name { get; set; }
    }
}

The Controller Action

I added the following to my controller to populate the model with 2 different lists one for each of the radio button helpers.  The first radio button help also has a default value set so the middle option (‘Line2’) will be pre-selected.

I also changed the Index HttpPost method so that if validation fails the same list of radio buttons is added to the model (obviously I have duplicated the list creation code, in a live example you should create a common function).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVC3_RadioButtonList_Helper_Sample.Models;

namespace MVC3_RadioButtonList_Helper_Sample.Controllers
{
    public class TestController : Controller
    {
        //
        // GET: /Test/
        public ActionResult Index()
        {
            List<aTest> list = new List<aTest>();
            list.Add(new aTest() { ID = 1, Name = "Line1" });
            list.Add(new aTest() { ID = 2, Name = "Line2" });
            list.Add(new aTest() { ID = 3, Name = "Line3" });
            SelectList sl = new SelectList(list, "ID", "Name");

            List<aTest> list2 = new List<aTest>();
            list2.Add(new aTest() { ID = 1, Name = "test1" });
            list2.Add(new aTest() { ID = 2, Name = "test2" });
            SelectList sl2 = new SelectList(list2, "ID", "Name");

            var model = new IndexViewModel();
            model.TestRadioList = sl;
            model.TestRadioList2 = sl2;
           
            model.TestRadio = "2";  // Set a default value for the first radio button helper

            return View(model);
        }

        [HttpPost]
        public ActionResult Index(IndexViewModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                ModelState.AddModelError("", "Always force an error to be raised so we can test the postback sets the radio buttons to their last values.");
            }

            // If we got this far, something failed, redisplay form
            List<aTest> list = new List<aTest>();
            list.Add(new aTest() { ID = 1, Name = "Line1" });
            list.Add(new aTest() { ID = 2, Name = "Line2" });
            list.Add(new aTest() { ID = 3, Name = "Line3" });
            SelectList sl = new SelectList(list, "ID", "Name");

            List<aTest> list2 = new List<aTest>();
            list2.Add(new aTest() { ID = 1, Name = "test1" });
            list2.Add(new aTest() { ID = 2, Name = "test2" });
            SelectList sl2 = new SelectList(list2, "ID", "Name");

            model.TestRadioList = sl;
            model.TestRadioList2 = sl2;

            return View(model);
        }
    }
}

The View

To test the html helper I added a view that is bound to the IndexViewModel.

I added a using statement so the helper method would be found by intelisence @using MVC3_RadioButtonList_Helper_Sample

I added the new helper RadioButtonForSelectList and passed in the TestRadio property from the IndexViewModel which is where the selected result will be put (the radio button ID value e.g. 2).

And the TestRadioList which is a list of SelectListItems for which a radio button will be created for each item in the list.

@Html.RadioButtonForSelectList(m => m.TestRadio, Model.TestRadioList)
@Html.ValidationMessageFor(m => m.TestRadio)

So the error message (as specified in the model ‘TestRadio’ property is shown when no radio button is selected I added a validation message helper.

I then added a 2nd radio button helper to the form like this, which is bound to a different list and it’s return value will be in TestRadio2.

@Html.RadioButtonForSelectList(m => m.TestRadio2, Model.TestRadioList2)
@Html.ValidationMessageFor(m => m.TestRadio2)

The form below shows the validation in action:

ValidationAtWork

 

Here is the source code.