۱۳۹۱/۰۲/۱۲

RadioButtonList در ASP.NET MVC


براي تهيه يك RadioButtonList نيز مي‌توان از همان نكته‌ي CheckBoxList استفاده كرد: نام عناصر radio button اضافه شده به صفحه را يكسان وارد مي‌كنيم. به اين ترتيب يك گروه تشكيل خواهد شد و زمانيكه اطلاعات اين عناصر به سرور ارسال مي‌شود، اينبار بجاي يك آرايه، تنها مقدار كنترل انتخاب شده، ارسال مي‌گردد. يك مثال:
يك پروژه جديد و خالي ASP.NET MVC را آغاز كنيد. سپس كنترلر Home و View خالي Index را نيز ايجاد نمائيد. محتويات اين دو را به نحو زير تغيير دهيد:

@{
    ViewBag.Title = "Index";
}
<h2>
    Index</h2>
<fieldset>
    <legend>HandleForm1 (Normal)</legend>
    @using (Html.BeginForm(actionName: "HandleForm1", controllerName: "Home"))
    {
        @:your favorite tech: <br />
        @Html.RadioButton(name: "tech", value: ".NET", isChecked: true) @:DOTNET <br />
        @Html.RadioButton(name: "tech", value: "JAVA", isChecked: false) @:JAVA <br />
        @Html.RadioButton(name: "tech", value: "PHP", isChecked: false) @:PHP <br />
        <input type="submit" value="Submit" />
    }
</fieldset>

using System.Collections.Generic;
using System.Web.Mvc;

namespace MvcApplication23.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult HandleForm1(string tech)
        {
            return RedirectToAction("Index");
        }
    }
}

در اينجا سه RadioButton با نامي يكسان در صفحه اضافه شده‌اند. سپس داخل متد HandleForm1 يك breakpoint قرار دهيد. اكنون برنامه را اجرا كنيد و فرم را به سرور ارسال نمائيد. پارامتر tech با value عنصر انتخابي مقدار دهي خواهد شد.

تهيه يك RadioButtonList عمومي

اطلاعات فوق را مي‌توان تبديل به يك HtmlHelper با قابليت استفاده مجدد نيز نمود:

@helper RadioButtonList(string groupName, IEnumerable<System.Web.Mvc.SelectListItem> items)
{
    <div class="RadioButtonList">
        @foreach (var item in items)
        {
            @item.Text
            <input type="radio" name="@groupName"
                   value="@item.Value"
                   @if (item.Selected) { <text>checked="checked"</text> }
               />
            <br />
        }
    </div>
}

براي مثال يك فايل را در مسير app_code\Helpers.cshtml ايجاد كرده و اطلاعات فوق را به آن اضافه نمائيد.
اينبار براي استفاده از آن خواهيم داشت:

using System.Collections.Generic;
using System.Web.Mvc;

namespace MvcApplication23.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            ViewBag.Tags = new[]
                            {
                                new SelectListItem { Text = ".NET", Value = "Val1", Selected = true },
                                new SelectListItem { Text = "JAVA", Value = "Val2", Selected = false },
                                new SelectListItem { Text = "PHP", Value = "Val3", Selected = false }
                            };
            return View();
        }

        [HttpPost]
        public ActionResult HandleForm2(string preferredTechnology)
        {
            return RedirectToAction("Index");
        }
    }
}

@{
    ViewBag.Title = "Index";
}
<h2>
    Index</h2>

<fieldset>
    <legend>HandleForm2 (Helper)</legend>
    @using (Html.BeginForm(actionName: "HandleForm2", controllerName: "Home"))
    {
        @:your favorite tech: <br />
        @Helpers.RadioButtonList("preferredTechnology", (SelectListItem[])ViewBag.Tags)
        <input type="submit" value="Submit" />
    }
</fieldset>

متد سفارشي تهيه شده، يك آرايه از SelectListItem ها را دريافت كرده و به صورت خودكار تبديل به RadioButtonList مي‌كند. بر اساس نام آن مي‌توان به مقدار انتخاب شده ارسالي به سرور در كنترلر مرتبط، دسترسي يافت.


تهيه يك Templated helper سفارشي

در عمل زمانيكه با مدل‌ها كار مي‌كنيم و اطلاعات برنامه قرار است Strongly typed باشند، مرسوم است ليستي از انتخاب‌ها را به صورت يك enum تعريف كنند. براي مثال مدل زير را به برنامه اضافه كنيد:

using System.ComponentModel.DataAnnotations;

namespace MvcApplication23.Models
{
    public enum Gender
    {
        [Display(Name = "مرد")]
        Male,
        [Display(Name = "زن")]
        Female,
    }

    public class User
    {
        [ScaffoldColumn(false)]
        public int Id { set; get; }

        [Display(Name = "نام")]
        public string Name { set; get; }

        [Display(Name = "جنسيت")]
        [UIHint("EnumRadioButtonList")]
        public Gender Gender { set; get; }
    }
}

قصد داريم يك Templated helper سفارشي را به نام EnumRadioButtonList، ايجاد كنيم تا در زمان فراخواني متد Html.EditorForModel، به صورت خودكار enum تعريف شده را به صورت يك RadioButtonList نمايش دهد.
براي اين منظور فايل جديد Views\Shared\EditorTemplates\EnumRadioButtonList.cshtml را به برنامه اضافه كنيد. محتواي آن‌را به نحو زير تغيير دهيد:

@using System.ComponentModel.DataAnnotations
@using System.Globalization
@model Enum
@{
    Func<Enum, string> getDescription = enumItem =>
    {
        var type = enumItem.GetType();
        var memInfo = type.GetMember(enumItem.ToString());
        if (memInfo != null && memInfo.Any())
        {
            var attrs = memInfo[0].GetCustomAttributes(typeof(DisplayAttribute), false);
            if (attrs != null && attrs.Any())
                return ((DisplayAttribute)attrs[0]).GetName();
        }
        return enumItem.ToString();
    };

    var listItems = Enum.GetValues(Model.GetType())
                        .OfType<Enum>()
                        .Select(enumItem =>
                                    new SelectListItem()
                                    {
                                        Text = getDescription(enumItem),
                                        Value = enumItem.ToString(),
                                        Selected = enumItem.Equals(Model)
                                    });

    string prefix = ViewData.TemplateInfo.HtmlFieldPrefix;    
    ViewData.TemplateInfo.HtmlFieldPrefix = string.Empty;

    int index = 0;
    foreach (var li in listItems)
    {
        string fieldName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", prefix, index++);
    <div class="editor-radio">
        @Html.RadioButton(prefix, li.Value, li.Selected, new { @id = fieldName })
        @Html.Label(fieldName, li.Text)
    </div>
    }

    ViewData.TemplateInfo.HtmlFieldPrefix = prefix;
}

در اينجا به كمك Reflection به اطلاعات enum دريافتي دسترسي خواهيم داشت. بر اين اساس مي‌توان نام عناصر آن‌را يافت و تبديل به يك RadioButtonList كرد. البته كار به همينجا ختم نمي‌شود. در اين بين بايد دقت داشت كه ممكن است از ويژگي Display (مانند مدل نمونه فوق) بر روي تك تك عناصر يك enum نيز استفاده شود. به همين جهت اين مورد نيز بايد پردازش گردد.
نهايتا براي استفاده از اين Templated helper سفارشي، كنترلر و View برنامه را به نحو زير مي‌توان تغيير داد:

using System.Collections.Generic;
using System.Web.Mvc;
using MvcApplication23.Models;

namespace MvcApplication23.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            var user = new User { Id = 1, Name = "name 1", Gender = Gender.Male };
            return View(user);
        }

        [HttpPost]
        public ActionResult HandleForm3(User user)
        {
            return RedirectToAction("Index");
        }
    }
}

@model MvcApplication23.Models.User
@{
    ViewBag.Title = "Index";
}
<h2>
    Index</h2>
<fieldset>
    <legend>HandleForm3 (EditorForModel)</legend>
    @using (Html.BeginForm(actionName: "HandleForm3", controllerName: "Home"))
    {
        @Html.EditorForModel()
        <input type="submit" value="Submit" />
    }
</fieldset>

براي استفاده از يك templated helper سفارشي چندين روش وجود دارد:
الف) همانند مثال فوق از ويژگي UIHint استفاده شود.
ب) نام فايل را به enum.cshtml تغيير دهيم. به اين ترتيب از اين پس كليه enumها در صورت استفاده از متد Html.EditorForModel، به صورت خودكار تبديل به يك RadioButtonList مي‌شوند.
ج) متد زير نيز همين كار را انجام مي‌دهد:
@Html.EditorFor(model => model.EnumProperty, "EnumRadioButtonList")