Reputation: 8674
I have this editor template with name 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")
</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")
</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
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")
</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
Upvotes: 3
Views: 1213
Reputation: 34992
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?
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
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")
</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});
}
Upvotes: 3
Reputation: 8674
The problem was I hadn't passed the object(the model that view is using) via the controller's get method. 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
Upvotes: 1