简体   繁体   English

用于创建不适用于所有枚举的枚举字段的单选按钮的编辑器模板

[英]Editor template for creating radio button for enum fields not working for all enums

I have this editor template with name RadioButtonQuestion 我有一个名为RadioButtonQuestion编辑器模板

@model Enum

@{
    // Looks for a [Display(Name="Some Name")] or a [Display(Name="Some Name", ResourceType=typeof(ResourceFile)] Attribute on your enum
    Func<Enum, string> getDescription = en =>
    {
        Type type = en.GetType();
        System.Reflection.MemberInfo[] memInfo = type.GetMember(en.ToString());

        if (memInfo != null && memInfo.Length > 0)
        {

            object[] attrs = memInfo[0].GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute),
                                                            false);

            if (attrs != null && attrs.Length > 0)
                return ((System.ComponentModel.DataAnnotations.DisplayAttribute)attrs[0]).GetName();
        }

        return en.ToString();
    };
    var listItems = Enum.GetValues(Model.GetType()).OfType<Enum>().Select(e =>
    new SelectListItem()
    {
        Text = getDescription(e),
        Value = e.ToString(),
        Selected = e.Equals(Model)
    });
    string prefix = ViewData.TemplateInfo.HtmlFieldPrefix;
    int index = 0;
    ViewData.TemplateInfo.HtmlFieldPrefix = string.Empty;

    foreach (var li in listItems)
    {
        string fieldName = string.Format(System.Globalization.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;
}

For the following model 对于以下型号

public class CreateAppointmentSelectOptions
{      
    public QuestionStart StartQuestion { get; set; }

    public QuestionEnd EndQuestion { get; set; }   
}

public enum QuestionStart
{
    [Display(Name="Repeat till common match is found")]
    RepeatTillCommonIsFound,

    [Display(Name="Repeat once")]
    RepeatOnce,    

    [Display(Name="No repeat")]
    NoRepeat

}

public enum QuestionEnd
{
    [Display(Name="Cancel Invitation")]
    CancelInvitation,

    [Display(Name="Plan with participants on first available common date")]
    FirstAvailableCommon,

    [Display(Name="Plan with participants on my first available common date")]
    YourFirstAvailableCommon
}

with following view 以下视图

@model InfoBridgeSmartDatePicker.Models.ViewModels.Appointment.CreateAppointmentSelectOptions
@{
    ViewBag.Title = "Create_WhatIf";
    Layout = "~/Views/Shared/_Layout.cshtml";
} 
@using (Html.BeginForm("Create_WhatIf", "Appointment", FormMethod.Post))
{      
    <div class="col-md-10">
        <div class='well'>
            <div class="form-group">
                <div class="input-group">
                    @Html.EditorFor(m => m.StartQuestion, "RadioButtonQuestion") &nbsp; &nbsp;
                </div>               

            </div>
        </div>
    </div>


    <div class="col-md-10">
        <div class='well'>
            <div class="form-group">         
                <div class="input-group">
                    @Html.EditorFor(m => m.EndQuestion, "RadioButtonQuestion")&nbsp;&nbsp;
                </div>                             

            </div>
        </div>
    </div>

}
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/jqueryval")

the editortemplate works perfectly, it creates radiobuttons for each field of enum, but for following model editortemplate工作得很好,它为枚举的每个字段创建了radiobuttons,但是对于以下模型

public class AttendeeResponse
{
    public IsAttending Isattending { get; set; }      
}

public enum IsAttending
{
    [Display(Name="Attending")]
    Attending,

    [Display(Name="Not attending")]
    NotAttending
}

with following view 以下视图

@model InfoBridgeSmartDatePicker.Models.ViewModels.Response.AttendeeResponse
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}  
@using(Html.BeginForm("Index","Response",FormMethod.Post))
{
    <div class="col-md-10">
        <div class='well'>
            <div class="form-group">
                <div class="input-group">
                    @Html.EditorFor(m => m.Isattending, "RadioButtonQuestion") &nbsp; &nbsp;
                </div>               

            </div>
        </div>
    </div>
}
<div id="provideDateTime">
    @{Html.RenderPartial("~/Views/Response/_AttendeeAvailableDateTime.cshtml");}
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/jqueryval")

i get error on this line of editor template 我在这行编辑器模板上得到错误

     return en.ToString();
Line 21:     };
/*error on this line Line*/ 22:     var listItems = Enum.GetValues(Model.GetType()).OfType<Enum>().Select(e =>
Line 23:     new SelectListItem()
Line 24:     {

any idea why this editor template is not working for second view? 知道为什么这个编辑器模板不能用于第二视图吗?

StackTrace: 堆栈跟踪:

[NullReferenceException: Object reference not set to an instance of an object.]
   ASP._Page_Views_Shared_EditorTemplates_RadioButtonQuestion_cshtml.Execute() in c:\Users\userx\Desktop\Git\DatePickerApp\datepicker\DatePicker\Views\Shared\EditorTemplates\RadioButtonQuestion.cshtml:22
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +271
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +120
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +145
   System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +695
   System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +382
   System.Web.Mvc.Html.TemplateHelpers.ExecuteTemplate(HtmlHelper html, ViewDataDictionary viewData, String templateName, DataBoundControlMode mode, GetViewNamesDelegate getViewNames, GetDefaultActionsDelegate getDefaultActions) +1037
   System.Web.Mvc.Html.TemplateHelpers.TemplateHelper(HtmlHelper html, ModelMetadata metadata, String htmlFieldName, String templateName, DataBoundControlMode mode, Object additionalViewData, ExecuteTemplateDelegate executeTemplate) +1633
   System.Web.Mvc.Html.TemplateHelpers.TemplateHelper(HtmlHelper html, ModelMetadata metadata, String htmlFieldName, String templateName, DataBoundControlMode mode, Object additionalViewData) +94
   System.Web.Mvc.Html.TemplateHelpers.TemplateFor(HtmlHelper`1 html, Expression`1 expression, String templateName, String htmlFieldName, DataBoundControlMode mode, Object additionalViewData, TemplateHelperDelegate templateHelper) +228
   System.Web.Mvc.Html.TemplateHelpers.TemplateFor(HtmlHelper`1 html, Expression`1 expression, String templateName, String htmlFieldName, DataBoundControlMode mode, Object additionalViewData) +140
   System.Web.Mvc.Html.EditorExtensions.EditorFor(HtmlHelper`1 html, Expression`1 expression, String templateName) +94
   ASP._Page_Views_Response_Index_cshtml.Execute() in c:\Users\userx\Desktop\Git\DatePickerApp\datepicker\DatePicker\Views\Response\Index.cshtml:14
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +271
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +120
   System.Web.WebPages.StartPage.RunPage() +63
   System.Web.WebPages.StartPage.ExecutePageHierarchy() +100
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +131
   System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +695
   System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +382
   System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +431
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +39
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +116
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +529
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +106
   System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +321
   System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +185
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +42
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +40
   System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +34
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +44
   System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +39
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +62
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +39
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +39
   System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +39
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +40
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9514812
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

PS: I got this editor template from this github project PS:我从这个github项目得到了这个编辑器模板

Glad to see you already found the solution. 很高兴看到你已经找到了解决方案。

However you could make the editor template more robust, so it doesn't break if a null model is passed to the view and also it is compatible with nullable types like IsAttending? 但是,您可以使编辑器模板更加健壮,因此如果将null模型传递给视图并且它与像IsAttending?这样的可空类型兼容,它不会中断IsAttending?

Just change this code to get the enum values: 只需更改此代码即可获取枚举值:

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

With this code that doesn't depend on the current model instance: 使用此代码不依赖于当前模型实例:

var enumType = this.ViewData.ModelMetadata.ModelType;
if (enumType.IsGenericType && enumType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
    enumType = Nullable.GetUnderlyingType(enumType);
}

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

EDIT - Code used to with the Nullable enum type and null model passed to the view scenarios 编辑 - 用于传递给视图场景的Nullable枚举类型和null模型的代码

Model 模型

public class AttendeeResponse
{
    public IsAttending? Isattending { get; set; }
}

public enum IsAttending
{
    [Display(Name = "Attending")]
    Attending,

    [Display(Name = "Not attending")]
    NotAttending
}

View 视图

@model MvcApplication1.Models.AttendeeResponse

@{
    ViewBag.Title = "Attendee Response";
}

@using(Html.BeginForm("Index","Response",FormMethod.Post))
{
    <div class="col-md-10">
        <div class='well'>
            <div class="form-group">
                <div class="input-group">
                    @Html.EditorFor(m => m.Isattending, "RadioButtonQuestion") &nbsp; &nbsp;
                </div>               

            </div>
        </div>
    </div>
}

Editor Template 编辑模板

@model Enum

@{
    // Looks for a [Display(Name="Some Name")] or a [Display(Name="Some Name", ResourceType=typeof(ResourceFile)] Attribute on your enum
    Func<Enum, string> getDescription = en =>
    {
        Type type = en.GetType();
        System.Reflection.MemberInfo[] memInfo = type.GetMember(en.ToString());

        if (memInfo != null && memInfo.Length > 0)
        {

            object[] attrs = memInfo[0].GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute),
                                                            false);

            if (attrs != null && attrs.Length > 0)
                return ((System.ComponentModel.DataAnnotations.DisplayAttribute)attrs[0]).GetName();
        }

        return en.ToString();
    };

    var enumType = this.ViewData.ModelMetadata.ModelType;
    if (enumType.IsGenericType && enumType.GetGenericTypeDefinition() == typeof(Nullable<>))
    {
        enumType = Nullable.GetUnderlyingType(enumType);
    }

    var listItems = Enum.GetValues(enumType).OfType<Enum>().Select(e =>
    new SelectListItem()
    {
        Text = getDescription(e),
        Value = e.ToString(),
        Selected = e.Equals(Model)
    });
    string prefix = ViewData.TemplateInfo.HtmlFieldPrefix;
    int index = 0;
    ViewData.TemplateInfo.HtmlFieldPrefix = string.Empty;

    foreach (var li in listItems)
    {
        string fieldName = string.Format(System.Globalization.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;
}

Controller 调节器

public ActionResult Attendee()
{
    //You can pass null as the view model and the view/editor template won't break
    return View();
    //This would also work, showing that you can have a nullable with null value
    //return View(new AttendeeResponse());
    //If the nullable enum has a value, it would appear as selected:
    //return View(new AttendeeResponse{Isattending = IsAttending.NotAttending});
}

The problem was I hadn't passed the object(the model that view is using) via the controller's get method. 问题是我没有通过控制器的get方法传递对象(视图正在使用的模型)。 Once i did that it worked. 一旦我做到了,它就有效了。

Previously I had 以前我有过

 public ActionResult Index()
 {

    return View();
 }

once i did 我曾经做过

 public ActionResult Index()
 {
    var attendeeresponse = new AttendeeResponse();
    return View(attendeeresponse);
 }

it worked 有效

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM