MVC 3 CheckboxList and me...Part 3. Try specifying the type arguments explicitly - asp.net-mvc

This IS fun.
Ok, I have the following model(s):
public class My : BusinessCategory
{
[Display(Name = "What types of energy do you use?")]
public List<MyTypes> MyTypeList { get; set; }
public bool? FirstOption { get; set; }
public bool? SecondOption{ get; set; }
public bool? ThirdOption{ get; set; }
public bool? FourthOption { get; set; }
}
Where MyTypes:
public class MyTypes
{
public int MyTypeId { get; set; }
public string MyTypeName { get; set; }
public bool? MyTypeValue { get; set; }
}
My controller is as follows:
public ActionResult My(Guid id)
{
try
{
var model = Model(id);
SetMyTypeList(model.My);
ViewBag.MyTypeMe = new MultiSelectList(model.My.MyTypeList, "MyTypeValue", "MyTypeName");
return View(model.My);
}
catch (Exception ex)
{
ExceptionHelper.WriteLog(ex);
return RedirectToAction("Error");
}
}
private void SetMyTypeList(My model)
{
model.MyTypeList = new List<MyTypes>();
model.MyTypeList.Add(new MyTypes { MyTypeId = 1, MyTypeName = GetName.GetDisplayName(model, m => m.FirstOption), MyTypeValue = model.FirstOption });
model.MyTypeList.Add(new MyTypes { MyTypeId = 2, MyTypeName = GetName.GetDisplayName(model, m => m.SecondOption), MyTypeValue = model.SecondOption});
model.MyTypeList.Add(new MyTypes { MyTypeId = 3, MyTypeName = GetName.GetDisplayName(model, m => m.ThirdOption), MyTypeValue = model.ThirdOption});
model.MyTypeList.Add(new MyTypes { MyTypeId = 4, MyTypeName = GetName.GetDisplayName(model, m => m.FourthOption), MyTypeValue = model.FourthOption });
}
public static string GetDisplayName<TModel, TProperty>(TModel model, Expression<Func<TModel, TProperty>> expression)
{
return ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, new ViewDataDictionary<TModel>(model)).DisplayName;
}
And finally the view is as follows:
#model Valpak.Websites.HealthChecker.Models.My
#{
ViewBag.Title = "My";
}
<h2>
My</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>My Management</legend>
<div style="text-align: left; padding-left: 47%;">
#Html.ListBoxFor(model => model.MyTypeList, ViewBag.MyTypeMe as MultiSelectList)
#Html.CheckBoxListFor(model => model.MyTypeList, ViewBag.EnergyTypeMe as MultiSelectList, Model.ReviewId)
</div>
<p>
<input type="submit" value="Continue" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Cancel and return to categories", "BusinessSummary", new { id = Model.ReviewId })
</div>
CheckboxListFor, if it was working, would use the following extension:
public static class HtmlHelper
{
//Extension
public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty[]>> expression, MultiSelectList multiSelectList, object htmlAttributes = null)
{
//Derive property name for checkbox name
MemberExpression body = expression.Body as MemberExpression;
string propertyName = body.Member.Name;
//Get currently select values from the ViewData model
TProperty[] list = expression.Compile().Invoke(htmlHelper.ViewData.Model);
//Convert selected value list to a List<string> for easy manipulation
List<string> selectedValues = new List<string>();
if (list != null)
{
selectedValues = new List<TProperty>(list).ConvertAll<string>(delegate(TProperty i) { return i.ToString(); });
}
//Create div
TagBuilder divTag = new TagBuilder("div");
divTag.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);
//Add checkboxes
foreach (SelectListItem item in multiSelectList)
{
divTag.InnerHtml += String.Format("<div><input type=\"checkbox\" name=\"{0}\" id=\"{0}_{1}\" " +
"value=\"{1}\" {2} /><label for=\"{0}_{1}\">{3}</label></div>",
propertyName,
item.Value,
selectedValues.Contains(item.Value) ? "checked=\"checked\"" : "",
item.Text);
}
return MvcHtmlString.Create(divTag.ToString());
}
}
Can someone you explain in very simplistic terms (I’m a bit dense) why I can use the ListBoxFor example but this dies and gives me the following error when I use the checkbox?
CS0411: The type arguments for method 'Extensions.HtmlHelper.CheckBoxListFor<TModel,TProperty>(System.Web.Mvc.HtmlHelper<TModel>, System.Linq.Expressions.Expression<System.Func<TModel,TProperty[]>>, System.Web.Mvc.MultiSelectList, System.Guid, object)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Can anyone offer any sort of work around as I’d quite like to use my :’(
As always, apologies for my ignorance.

In the signature of the extension method you have the following second argument:
Expression<Func<TModel, TProperty[]>> expression,
This basically means that the expression must return an array of TProperty => TProperty[]
whereas in your view model you have a List<T>:
public List<MyTypes> EnergyTypeList { get; set; }
and inside your view you are using:
model => model.EnergyTypeList
Your code doesn't work because List<EnergyTypeList> is not the same thing as EnergyTypeList[].
So you have different possibilities. Either change the type in your view model to match the one in your helper or use change your helper to use a List or even better an IEnumerable<TProperty>. This way the extension method will work even with arrays.

Related

Custom Validation for nested model in .net core

I am trying to validate a nested model using custom validation. But the problem is AttributeAdapterBase.AddValidation function is never called on nested model. However it works well with simple class property
Custom required validation attribute:
public interface IId
{
long Id { get; set; }
}
public class Select2RequiredAttribute : RequiredAttribute
{
public Select2RequiredAttribute(string errorMessage = "") : base()
{
ErrorMessage = errorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Type t = value.GetType();
if (typeof(IId).IsAssignableFrom(t))
{
if ((value as IId).Id == 0)
{
return new ValidationResult(ErrorMessage);
}
}
else
{
return new ValidationResult(ErrorMessage);
}
return ValidationResult.Success;
}
}
Attribute adapter base:
public class Select2RequiredAttributeAdapter : AttributeAdapterBase<Select2RequiredAttribute>
{
public Select2RequiredAttributeAdapter(Select2RequiredAttribute attribute, IStringLocalizer stringLocalizer) : base(attribute, stringLocalizer)
{
}
public override void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-select2-required", GetErrorMessage(context));
}
public override string GetErrorMessage(ModelValidationContextBase validationContext)
{
return Attribute.ErrorMessage ?? GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName());
}
}
Adapter provider:
public class Select2RequiredAdapterProvider : IValidationAttributeAdapterProvider
{
private readonly IValidationAttributeAdapterProvider _baseProvider = new ValidationAttributeAdapterProvider();
public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
{
if (attribute is Select2RequiredAttribute)
{
return new Select2RequiredAttributeAdapter(attribute as Select2RequiredAttribute, stringLocalizer);
}
else
{
return _baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
}
}
}
Startup.cs:
services.AddSingleton<IValidationAttributeAdapterProvider, Select2RequiredAdapterProvider>();
Model classes:
public interface IBaseBriefViewModel : IId
{
string Name { get; set; }
}
public class BaseBriefViewModel : IBaseBriefViewModel
{
public virtual long Id { get; set; }
public string Name { get; set; }
}
public class UserViewModel
{
public long Id { get; set; }
public string Name { get; set; }
[Select2Required("Branch is required.")]
public BaseBriefViewModel Branch { get; set; }
}
Branch select 2 partial view:
#model DataLibrary.ViewModels.BriefViewModels.BaseBriefViewModel
#{
var elementId = ViewData["ElementId"] != null && !string.IsNullOrEmpty(ViewData["ElementId"].ToString()) ? ViewData["ElementId"].ToString() : "branch-id";
}
<div class="form-group">
<label>Branch: <span class="text-danger"></span></label>
<div class="row">
<div class="#select2Class">
#Html.DropDownListFor(model => model.Id, new List<SelectListItem>() {
new SelectListItem()
{
Value = (Model!=null&&Model.Id>0)?Model.Id.ToString():"",
Text = (Model!=null&&Model.Id>0)?Model.Name:"",
Selected = (Model!=null&&Model.Id>0)?true:false,
}}, new { #id = elementId, #class = "form-control disable-field"})
#Html.ValidationMessageFor(model => model.Id, "", new { #class = "text-danger" })
</div>
</div>
</div>
<script>
$(function () {
var id = "#" + "#elementId";
var url = '/Branch/GetBranchsForSelect2';
var dataArray = function (params) {
params.page = params.page || 1;
return {
prefix: params.term,
pageSize: pageSize,
pageNumber: params.page,
};
};
Select2AutoCompleteAjax(id, url, dataArray, pageSize, "---Branch---");
});
</script>
All this code works well for server side. But for better user experience I want to show error before submitting form. How can I achieve this? I want to use this BaseBriefViewModel for a lot of Select2 in the project. So hard coding a static error message is not a good idea. What I really want to do is pass a error message from parent object. Like Branch is required in this specific case. Maybe in some other class I might pass Product is required
Any direction will be appreciated
At the moment this is not supported - but support is in planned. See dotnet github issue:
https://github.com/dotnet/runtime/issues/36093

How to Use check box list in MVC

I need to display check box list with more than one options. User must select minimum one check box, user can select more than one check boxes.
I want to store values of all check boxes (selected) in one field as a string(Data base) with coma separated. This is not mandatory, mandatory is need to store multiple values of each selected check box. Alternate solutions are welcome.
Model
public class Member
{
public string Member_VehicalType { get; set; }
public IList<SelectListItem> Member_VehicalType_List { get; set; }
Controller
[HttpGet]
public ActionResult Create()
{
Member objMemberModel = new Member();
List<SelectListItem> vehical_Types = new List<SelectListItem>();
vehical_Types.Add(new SelectListItem { Text = "Two Wheeler", Value = "1" });
vehical_Types.Add(new SelectListItem { Text = "Four Wheeler", Value = "2" });
objMemberModel.Member_VehicalType_List = vehical_Types;
return View(objMemberModel);
How do I create view with #Html.CheckBoxFor( or #Html.CheckBox(
I recently had to deal with SelectListItem as a checkbox-list. I came up with the following HtmlExtensions, which might be helpful. These extensions provide the same functionality as the #Html.DropDownListFor(model => ...);
Usage: #Html.CheckBoxListFor(model => model.ModelMemberToPutValueIn, Model.Member_VehicalType_List)
public static class HtmlExtensions
{
public static MvcHtmlString CheckBoxListFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, IEnumerable<SelectListItem> items, object htmlAttributes = null)
{
var listName = ExpressionHelper.GetExpressionText(expression);
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
items = GetCheckboxListWithDefaultValues(metaData.Model, items);
return htmlHelper.CheckBoxList(listName, items, htmlAttributes);
}
public static MvcHtmlString CheckBoxList(this HtmlHelper htmlHelper, string listName, IEnumerable<SelectListItem> items, object htmlAttributes = null)
{
if (items == null) return null;
var container = new TagBuilder("div");
container.AddCssClass("checkbox-list");
container.MergeAttribute("id", HtmlHelper.GenerateIdFromName(listName));
foreach (var item in items)
{
var id = HtmlHelper.GenerateIdFromName(String.Join(".", listName, item.Text));
var div = new TagBuilder("div");
div.AddCssClass("checkbox");
var cb = new TagBuilder("input");
cb.MergeAttribute("type", "checkbox");
cb.MergeAttribute("id", id);
cb.MergeAttribute("name", listName);
cb.MergeAttribute("value", item.Value ?? item.Text);
if (item.Selected) cb.MergeAttribute("checked", "checked");
var label = new TagBuilder("label");
label.MergeAttribute("for", id);
label.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);
label.InnerHtml = item.Text;
div.InnerHtml = cb.ToString(TagRenderMode.SelfClosing) + label;
container.InnerHtml += div;
}
return new MvcHtmlString(container.ToString());
}
private static IEnumerable<SelectListItem> GetCheckboxListWithDefaultValues(object defaultValues, IEnumerable<SelectListItem> selectList)
{
var defaultValuesList = defaultValues as IEnumerable;
if (defaultValuesList == null) return selectList;
var values = from object value in defaultValuesList select Convert.ToString(value, CultureInfo.CurrentCulture);
var selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
var newSelectList = new List<SelectListItem>();
foreach (var item in selectList)
{
item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
newSelectList.Add(item);
}
return newSelectList;
}
}

ASP.NET MVC HtmlHelper extension method to wrap around only if it has content

I am making an configurable form—an admin selects which inputs are displayed. The inputs are wrapped by fieldsets.
I want to make a HtmlHelper to generate fieldset only if it has content—at least one input field to prevent such situations
<fieldset id="Name">
<legend>Name</legend>
<input type="text" placeholer="Forename"></input>
<input type="text" placeholer="Surname"></input>
</fieldset>
<fieldset id="Address">
<legend>Address</legend>
</fieldset>
that we have empty fieldset like that with address.
I've made a FieldSetHelper
public class FieldSetHelper: IDisposable
{
private readonly HtmlHelper _htmlHelper;
private readonly string _fieldSetId;
private readonly string _legendId;
private readonly string _legendText;
public FieldSetHelper(HtmlHelper htmlHelper, string fieldSetId, string legendId, string legendText)
{
_htmlHelper = htmlHelper;
_fieldSetId = fieldSetId;
_legendId = legendId;
_legendText = legendText;
_htmlHelper.ViewContext.Writer = new StringWriter();
OpenFieldSet();
AddLegend();
}
private void OpenFieldSet()
{
string id = string.IsNullOrWhiteSpace(_fieldSetId) ? string.Empty : string.Format(" id=\"{0}\"", _fieldSetId);
_htmlHelper.ViewContext.Writer.WriteLine(string.Format("<fieldset" + id + ">"));
}
private void CloseFieldSet()
{
_htmlHelper.ViewContext.Writer.WriteLine(string.Format("</fieldset>"));
}
private void AddLegend()
{
string id = string.IsNullOrWhiteSpace(_legendId) ? string.Empty : string.Format(" id=\"{0}\"", _legendId);
_htmlHelper.ViewContext.Writer.WriteLine("<legend"+id+">");
_htmlHelper.ViewContext.Writer.WriteLine(_legendText);
_htmlHelper.ViewContext.Writer.WriteLine("</legend>");
}
public void Dispose()
{
CloseFieldSet();
}
But I don't know how to not generate it if its content is empty.
Here is part of the View:
using (Html.BeginFieldSet("Address", null, "Address"))
{
#Html.EditorFor(m => m.Address, new {Model.VisibleInputFields})
}
I recently had a similar situation. I was trying to add some "No Items Here" text if the outer element (the one generated from the using element) did not have any items. This is an untested modified version of what I ended up doing but if you are still looking for an answer it should get you close.
public class FieldSetHelper: IDisposable
{
private readonly HtmlHelper _htmlHelper;
private readonly string _fieldSetId;
private readonly string _legendId;
private readonly string _legendText;
public FieldSetHelper(HtmlHelper htmlHelper, string fieldSetId, string legendId, string legendText)
{
_htmlHelper = htmlHelper;
_fieldSetId = fieldSetId;
_legendId = legendId;
_legendText = legendText;
_htmlHelper.ViewContext.Writer = new StringWriter();
OpenFieldSet();
AddLegend();
}
private string BuildOpenFieldSetString(){
string id = string.IsNullOrWhiteSpace(_fieldSetId) ? string.Empty : string.Format(" id=\"{0}\"", _fieldSetId);
return string.Format("<fieldset" + id + ">");
}
private void OpenFieldSet()
{
_htmlHelper.ViewContext.Writer.WriteLine(BuildOpenFieldSetString());
}
private void CloseFieldSet()
{
_htmlHelper.ViewContext.Writer.WriteLine(string.Format("</fieldset>"));
}
private void AddLegend()
{
string id = string.IsNullOrWhiteSpace(_legendId) ? string.Empty : string.Format(" id=\"{0}\"", _legendId);
_htmlHelper.ViewContext.Writer.WriteLine("<legend"+id+">");
_htmlHelper.ViewContext.Writer.WriteLine(_legendText);
_htmlHelper.ViewContext.Writer.WriteLine("</legend>");
}
public void Dispose()
{
var htmlString = _htmlHelper.ViewContext.Writer.ToString();
var index = htmlString.Trim().LastIndexOf(BuildOpenFieldSetString().Trim());
if(html.IndexOf("<input", index) == -1){
_htmlHelper.ViewContext.Writer = new StringWriter();
_htmlHelper.ViewContext.Writer.WriteLine(html.SubString(0,index));
}else{
CloseFieldSet();
}
}

Generic Checkbox List View model in MVC

I want to create a generic checkbox list view model and so I got this:
public class ChckboxListViewModel<T>
{
public List<CheckboxViewModel<T>> CheckboxList { get; set; }
public IEnumerable<T> SelectedValues
{
get { return CheckboxList.Where(c => c.IsSelected).Select(c => c.Value); }
}
public ChckboxListViewModel()
{
CheckboxList = new List<CheckboxViewModel<T>>();
}
}
public class CheckboxViewModel<T>
{
public string Label { get; set; }
public T Value { get; set; }
public bool IsSelected { get; set; }
public CheckboxViewModel(string i_Label, T i_Value, bool i_IsSelected)
{
Label = i_Label;
Value = i_Value;
IsSelected = i_IsSelected;
}
}
It is used by a different view model to represent filters of different statuses:
public class FaultListFilters
{
public string SearchKeyword { get; set; }
public ChckboxListViewModel<Fault.eFaultStatus> StatusFilter { get; set; }
public FaultListFilters()
{
SearchKeyword = null;
StatusFilter = new ChckboxListViewModel<Fault.eFaultStatus>();
StatusFilter.CheckboxList.Add(new CheckboxViewModel<Fault.eFaultStatus>(FaultManagementStrings.OpenStatus,Fault.eFaultStatus.Open,true));
StatusFilter.CheckboxList.Add(new CheckboxViewModel<Fault.eFaultStatus>(FaultManagementStrings.InProgressStatus, Fault.eFaultStatus.InProgress, true));
StatusFilter.CheckboxList.Add(new CheckboxViewModel<Fault.eFaultStatus>(FaultManagementStrings.ClosedStatus, Fault.eFaultStatus.Close, false));
}
}
Now I can't find the right way to display the editors or to create an editor template for that kind of a view model because it is Generic.
I don't want o create a separate editor template for ChckboxListViewModel<int> and then another for ChckboxListViewModel<Fault.eFaultStatus> and so on..
Is it even a goose idea to use generics in this case?
Is there another way to represent and display a check-box list in MVC?
I have done the following but the modle is not binding for some reason:
#using (Html.BeginForm("FaultManagement", "Faults", FormMethod.Get, null))
{
for (int i=0 ; i<Model.FaultListFilters.StatusFilter.CheckboxList.Count() ; i++)
{
#Html.HiddenFor(m => m.FaultListFilters.StatusFilter.CheckboxList[i].Value)
#Html.CheckBoxFor(m => m.FaultListFilters.StatusFilter.CheckboxList[i].IsSelected)
#Html.LabelFor(m=> m.FaultListFilters.StatusFilter.CheckboxList[i].IsSelected,Model.FaultListFilters.StatusFilter.CheckboxList[i].Label)
}
<input type="submit" />
}
Is it even a goose idea to use generics in this case?
Don't think it is.
Is there another way to represent and display a check-box list in MVC?
I would write a custom HTML helper:
public static class HtmlExtensions
{
public static IHtmlString CheckboxListFor<TModel>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, IEnumerable<string>>> ex,
IEnumerable<string> possibleValues)
{
var metadata = ModelMetadata.FromLambdaExpression(ex, html.ViewData);
var availableValues = (IEnumerable<string>)metadata.Model;
var name = ExpressionHelper.GetExpressionText(ex);
return html.CheckboxList(name, availableValues, possibleValues);
}
private static IHtmlString CheckboxList(this HtmlHelper html, string name, IEnumerable<string> selectedValues, IEnumerable<string> possibleValues)
{
var result = new StringBuilder();
foreach (string current in possibleValues)
{
var label = new TagBuilder("label");
var sb = new StringBuilder();
var checkbox = new TagBuilder("input");
checkbox.Attributes["type"] = "checkbox";
checkbox.Attributes["name"] = name;
checkbox.Attributes["value"] = current;
var isChecked = selectedValues.Contains(current);
if (isChecked)
{
checkbox.Attributes["checked"] = "checked";
}
sb.Append(checkbox.ToString());
sb.Append(current);
label.InnerHtml = sb.ToString();
result.Append(label);
}
return new HtmlString(result.ToString());
}
}
Then you could have a view model:
public class FaultListFiltersViewModel
{
public IEnumerable<string> SelectedStatusFilters { get; set; }
public IEnumerable<string> AvailableStatusFilters
{
get
{
return new[] { "Label 1", "Label 2", "Label 3" }
}
}
}
and inside the view you could use the helper:
#Html.CheckBoxListFor(x => x.SelectedStatusFilters, Model.AvailableStatusFilters)
Here is another implementation that will better support bootstrap button-group labels (as it requires them to be seperated) and enum type selected values.
public static IHtmlString CheckboxListFor<TModel, TKey>(this HtmlHelper<TModel> helper, Expression<Func<TModel, IEnumerable<TKey>>> ex, Dictionary<TKey, string> i_PossibleOptions, object i_LabelHtmlAttributes)
where TKey : struct, IConvertible
{
var metadata = ModelMetadata.FromLambdaExpression(ex, helper.ViewData);
var selectedValues = (IEnumerable<TKey>)metadata.Model;
var name = ExpressionHelper.GetExpressionText(ex);
return helper.CheckboxList(name, selectedValues, i_PossibleOptions, i_LabelHtmlAttributes);
}
private static IHtmlString CheckboxList<TKey>(this HtmlHelper helper, string name, IEnumerable<TKey> i_SelectedValues, Dictionary<TKey, string> i_PossibleOptions, object i_LabelHtmlAttributes)
where TKey : struct, IConvertible
{
if (!typeof(TKey).IsEnum) throw new ArgumentException("T must be an enumerated type");
var result = new StringBuilder();
foreach (var option in i_PossibleOptions)
{
var label = new TagBuilder("label");
label.MergeAttributes(new RouteValueDictionary(i_LabelHtmlAttributes));
label.Attributes["for"] = string.Format("{0}",option.Key.ToString());
label.InnerHtml = option.Value;
var checkbox = new TagBuilder("input");
checkbox.Attributes["type"] = "checkbox";
checkbox.Attributes["name"] = name;
checkbox.Attributes["id"] = string.Format("{0}", option.Key.ToString());
checkbox.Attributes["value"] = option.Key.ToString();
bool isChecked = ((i_SelectedValues != null) && (i_SelectedValues.Contains(option.Key)));
if ( isChecked )
{
checkbox.Attributes["checked"] = "checked";
}
result.Append(checkbox);
result.Append(label);
}
return new HtmlString(result.ToString());
}
And then the View Model looks like that:
public class FaultListFilters
{
[Display(ResourceType = typeof(FaultManagementStrings), Name = "SearchKeyword")]
public string SearchKeyword { get; set; }
public Dictionary<Fault.eFaultStatus, string> PossibleFaultStatuses
{
get
{
var possibleFaultStatuses = new Dictionary<Fault.eFaultStatus, string>();
possibleFaultStatuses.Add(Fault.eFaultStatus.Open, FaultManagementStrings.OpenStatus);
possibleFaultStatuses.Add(Fault.eFaultStatus.InProgress, FaultManagementStrings.InProgressStatus);
possibleFaultStatuses.Add(Fault.eFaultStatus.Close, FaultManagementStrings.ClosedStatus);
return possibleFaultStatuses;
}
}
public IEnumerable<Fault.eFaultStatus> SelectedFaultStatuses { get; set; }
public FaultListFilters()
{
SearchKeyword = null;
SelectedFaultStatuses = new[] { Fault.eFaultStatus.Open, Fault.eFaultStatus.InProgress };
}
}
and the usage remains the same (except i have added the label html attributes)
<div class="btn-group">
#Html.CheckboxListFor(m => m.FaultListFilters.SelectedFaultStatuses, Model.FaultListFilters.PossibleFaultStatuses, new { Class="btn"})
</div>

Radio Button List not being generated from identical code

I am currently pulling my hair out. I have an ASP.NET MVC web site with two forms, both have radio buttons on them. On the first page (that works), the radio list appears just fine. However, on the second page the radio buttons are not even present in the source code. Here is the code chunk from the first page (BestTimeType is an Enum that I made):
//Back End
[DisplayName("Time:")]
public RadioButtonListViewModel<BestTimeType> BestTimeRadioList { get; set; }
public EvalModel()
{
BestTimeRadioList = new RadioButtonListViewModel<BestTimeType>
{
Id = "BestTime",
SelectedValue = BestTimeType.Afternoon,
ListItems = new List<RadioButtonListItem<BestTimeType>>
{
new RadioButtonListItem<BestTimeType>{Text = "Morning", Value = BestTimeType.Morning},
new RadioButtonListItem<BestTimeType>{Text = "Afternoon", Value = BestTimeType.Afternoon},
new RadioButtonListItem<BestTimeType>{Text = "Evening", Value = BestTimeType.Evening}
}
};
}
// Front End
<div class="grid_1 evalLabel reqField" style="padding-top: 5px;">
<%= Html.LabelFor(model => model.BestTimeRadioList)%>
</div>
<div class="grid_4" style="text-align: center; padding: 5px 0px 10px 0px;">
<%= Html.RadioButtonListFor(model => model.BestTimeRadioList) %>
</div>
Here is the code chunk for the second page:
//Back End
[Required(ErrorMessage = "*")]
[DisplayName("HS Diploma:")]
public RadioButtonListViewModel<bool> HsDiplomaRadioList { get; set; }
public EmploymentModel()
{
HsDiplomaRadioList = new RadioButtonListViewModel<bool>
{
Id = "HsDiploma",
SelectedValue = false,
ListItems = new List<RadioButtonListItem<bool>>
{
new RadioButtonListItem<bool> {Text = "Yes", Value = true},
new RadioButtonListItem<bool> {Text = "No", Value = false}
}
};
}
//Front End
<div class="grid_2 employLabel reqField">
<%= Html.LabelFor(model => model.HsDiplomaRadioList) %>
</div>
<div class="grid_3">
<%= Html.RadioButtonListFor(model => model.HsDiplomaRadioList)%>
<%= Html.ValidationMessageFor(model => model.HsDiplomaRadioList)%>
</div>
I also tried making a custom Enum for the HsDiplomaRadioList (Yes/No), but the radio did not show up either.
I must be missing something extremely stupid simple. If any more code is necessary, I will be glad to put them up.
Thanks in advance.
Edit
Here is the code for RadioButtonList:
public static class HtmlHelperExtensions
{
public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression) where TModel : class
{
return htmlHelper.RadioButtonListFor(expression, null);
}
public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression, object htmlAttributes) where TModel : class
{
return htmlHelper.RadioButtonListFor(expression, new RouteValueDictionary(htmlAttributes));
}
public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression, IDictionary<string, object> htmlAttributes) where TModel : class
{
var inputName = GetInputName(expression);
RadioButtonListViewModel<TRadioButtonListValue> radioButtonList = GetValue(htmlHelper, expression);
if (radioButtonList == null)
return String.Empty;
if (radioButtonList.ListItems == null)
return String.Empty;
var divTag = new TagBuilder("div");
divTag.MergeAttribute("id", inputName);
divTag.MergeAttribute("class", "radio");
foreach (var item in radioButtonList.ListItems)
{
var radioButtonTag = RadioButton(htmlHelper, inputName, new SelectListItem { Text = item.Text, Selected = item.Selected, Value = item.Value.ToString() }, htmlAttributes);
divTag.InnerHtml += radioButtonTag;
}
return string.Concat(divTag, htmlHelper.ValidationMessage(inputName, "*"));
}
public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
if (expression.Body.NodeType == ExpressionType.Call)
{
var methodCallExpression = (MethodCallExpression)expression.Body;
string name = GetInputName(methodCallExpression);
return name.Substring(expression.Parameters[0].Name.Length + 1);
}
return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
}
private static string GetInputName(MethodCallExpression expression)
{
// p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
var methodCallExpression = expression.Object as MethodCallExpression;
if (methodCallExpression != null)
{
return GetInputName(methodCallExpression);
}
return expression.Object.ToString();
}
public static string RadioButton(this HtmlHelper htmlHelper, string name, SelectListItem listItem,
IDictionary<string, object> htmlAttributes)
{
var inputIdSb = new StringBuilder();
inputIdSb.Append(name)
.Append("_")
.Append(listItem.Value);
var sb = new StringBuilder();
var builder = new TagBuilder("input");
if (listItem.Selected) builder.MergeAttribute("checked", "checked");
builder.MergeAttribute("type", "radio");
builder.MergeAttribute("value", listItem.Value);
builder.MergeAttribute("id", inputIdSb.ToString());
builder.MergeAttribute("name", name + ".SelectedValue");
builder.MergeAttributes(htmlAttributes);
sb.Append(builder.ToString(TagRenderMode.SelfClosing));
sb.Append(RadioButtonLabel(inputIdSb.ToString(), listItem.Text, htmlAttributes));
//sb.Append("<br>");
return sb.ToString();
}
public static string RadioButtonLabel(string inputId, string displayText,
IDictionary<string, object> htmlAttributes)
{
var labelBuilder = new TagBuilder("label");
labelBuilder.MergeAttribute("for", inputId);
labelBuilder.MergeAttributes(htmlAttributes);
labelBuilder.InnerHtml = displayText;
return labelBuilder.ToString(TagRenderMode.Normal);
}
public static TProperty GetValue<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class
{
TModel model = htmlHelper.ViewData.Model;
if (model == null)
{
return default(TProperty);
}
Func<TModel, TProperty> func = expression.Compile();
return func(model);
}
}
Ok finally got around to stack tracing (I should do this first, but I was in a rush last night) and found that for some reason model.HsDiplomaRadio is null. I will need to track down the cause of this.

Resources