How to add default attribute value of "class" in custom htmlhelper? - asp.net-mvc

I try to create a custom textbox in mvc. I want to add default class value "text-uppercase".I also want add new class values in view.
Myhelper class is;
public static MvcHtmlString Custom_TextBox<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
{
ModelMetadata oModelMetadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
if (htmlAttributes == null)
{
htmlAttributes = new Dictionary<string, object>();
}
htmlAttributes.Add("type", "text");
htmlAttributes.Add("name", oModelMetadata.DisplayName);
htmlAttributes.Add("id", oModelMetadata.DisplayName);
htmlAttributes.Add("class", "text-uppercase");
return helper.TextBoxFor(expression, htmlAttributes);
}
And my view code:
#Html.Custom_TextBox(model => model.YazilimAdi,new { #class = "form-control" } )
Actually the error is: An item with the same key has already been added. "class".
How can i add my default class value "text-uppercase"?

if(htmlAttributes.ContainsKey("class"))
{
htmlAttributes["class"] += " text-uppercase";
}
else
{
htmlAttributes.Add("class", "text-uppercase");
}
and put this at the beginning of extension:
if (htmlAttributes == null)
{
htmlAttributes = new Dictionary<string, object>();
}

Related

Custom DropDownListFor Helper loads an empty value for the selected element

I am using a custom helper, to create a select element that can take HtmlAttribute. I am using the same code as I found here, in #Alexander Puchkov answer (which I'll post for reference).
It's working fine, apart when the DDL helper is loaded with one of the option as the selected item, then the value of the selected item is null/empty. (i.e. on an edit page the DDL loads up with the option that was set on creation, rather than the '-Please select option-'),
The highlighted attribute in the picture shows the problem, the value should not be empty, but should show 'Medium'...
So the text is display correctly but the element has no value. Any ideas on where this issue comes from?
Here is the full code of the helper:
/// <summary>
/// A selectListItem with an extra property to hold HtmlAttributes
/// <para>Used in conjunction with the sdDDL Helpers</para>
/// </summary>
public class SdSelectListItem : SelectListItem
{
public object HtmlAttributes { get; set; }
}
/// <summary>
/// Generate DropDownLists with the possibility of styling the 'option' tags of the generated 'select' tag
/// </summary>
public static class SdDdlHelper
{
public static MvcHtmlString sdDDLFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, IEnumerable<SdSelectListItem> selectList,
string optionLabel, object htmlAttributes)
{
if (expression == null)
throw new ArgumentNullException("expression");
ModelMetadata metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
return SelectInternal(htmlHelper, metadata, optionLabel, ExpressionHelper.GetExpressionText(expression), selectList,
false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString sdDDLFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, IEnumerable<SdSelectListItem> selectList,
object htmlAttributes)
{
if (expression == null)
throw new ArgumentNullException("expression");
ModelMetadata metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
//-> The below line is my problem (specifically the 'null' param), it set to null if no option label is passed to the method...So if I use this overload, the DDL will load (or re-load) with the default value selected, not the value binded to the property - And if I use the above overload, and set Model.action_priority as the optionLabel, then I get what is shown in the picture...
return SelectInternal(htmlHelper, metadata, null, ExpressionHelper.GetExpressionText(expression), selectList,
false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
#region internal/private methods
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name,
IEnumerable<SdSelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(fullName))
throw new ArgumentException("No name");
if (selectList == null)
throw new ArgumentException("No selectlist");
object defaultValue = (allowMultiple)
? htmlHelper.GetModelStateValue(fullName, typeof(string[]))
: htmlHelper.GetModelStateValue(fullName, typeof(string));
// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (defaultValue == null)
defaultValue = htmlHelper.ViewData.Eval(fullName);
if (defaultValue != null)
{
IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
IEnumerable<string> values = from object value in defaultValues
select Convert.ToString(value, CultureInfo.CurrentCulture);
HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
List<SdSelectListItem> newSelectList = new List<SdSelectListItem>();
foreach (SdSelectListItem item in selectList)
{
item.Selected = (item.Value != null)
? selectedValues.Contains(item.Value)
: selectedValues.Contains(item.Text);
newSelectList.Add(item);
}
selectList = newSelectList;
}
// Convert each ListItem to an <option> tag
StringBuilder listItemBuilder = new StringBuilder();
// Make optionLabel the first item that gets rendered.
if (optionLabel != null)
listItemBuilder.Append(
ListItemToOption(new SdSelectListItem()
{
Text = optionLabel,
Value = String.Empty,
Selected = false
}));
foreach (SdSelectListItem item in selectList)
{
listItemBuilder.Append(ListItemToOption(item));
}
TagBuilder tagBuilder = new TagBuilder("select")
{
InnerHtml = listItemBuilder.ToString()
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */);
tagBuilder.GenerateId(fullName);
if (allowMultiple)
tagBuilder.MergeAttribute("multiple", "multiple");
// If there are any errors for a named field, we add the css attribute.
System.Web.Mvc.ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fullName, metadata));
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
internal static string ListItemToOption(SdSelectListItem item)
{
TagBuilder builder = new TagBuilder("option")
{
InnerHtml = HttpUtility.HtmlEncode(item.Text)
};
if (item.Value != null)
{
builder.Attributes["value"] = item.Value;
}
if (item.Selected)
{
builder.Attributes["selected"] = "selected";
}
builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(item.HtmlAttributes));
return builder.ToString(TagRenderMode.Normal);
}
internal static object GetModelStateValue(this HtmlHelper htmlHelper, string key, Type destinationType)
{
System.Web.Mvc.ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(key, out modelState))
{
if (modelState.Value != null)
{
return modelState.Value.ConvertTo(destinationType, null /* culture */);
}
}
return null;
}
#endregion
}
}
Here is how I call it in the View (using Razor):
#Html.sdDDLFor(x => Model.action_priority, Model.actionPriorityDDL(), Model.action_priority, new
{
#id = "_Action_Priority_DDL",
#class = "form-control"
})
And finally here is the Model.actionPriorityDDL() method:
public List<SdSelectListItem> actionPriorityDDL()
{
action_priority_DDL = new List<SdSelectListItem>();
action_priority_DDL.Add(new SdSelectListItem
{
Value = StringRepository.ActionPriority.high,
Text = StringRepository.ActionPriority.high,
HtmlAttributes = new
{
#class = "lbl-action-priority-high"
}
}
);
action_priority_DDL.Add(new SdSelectListItem
{
Value = StringRepository.ActionPriority.medium,
Text = StringRepository.ActionPriority.medium,
HtmlAttributes = new
{
#class = "lbl-action-priority-medium"
}
}
);
action_priority_DDL.Add(new SdSelectListItem
{
Value = StringRepository.ActionPriority.low,
Text = StringRepository.ActionPriority.low,
HtmlAttributes = new
{
#class = "lbl-action-priority-low"
}
}
);
action_priority_DDL.Add(new SdSelectListItem
{
Value = StringRepository.ActionPriority.psar,
Text = StringRepository.ActionPriority.psar,
HtmlAttributes = new
{
#class = "lbl-action-priority-psar"
}
}
);
return action_priority_DDL;
}
This is caused by a former bug in MVC framework (http://aspnet.codeplex.com/workitem/8311; accessible here: https://web.archive.org/web/20131208041521/http://aspnet.codeplex.com/workitem/8311) that happens when using the DropDownListFor helper within a loop, i.e. the model property has an indexer (like in your example, where the generated select element has an attribute name="actionList[0].action_priority")
I resolved this by copying some code from here https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/Html/SelectExtensions.cs
Specifically, in this method
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name,
IEnumerable<ExtendedSelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
replace this
if (selectList == null)
throw new ArgumentException("No selectlist");
with:
bool usedViewData = false;
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
Now, replace this
if (defaultValue == null)
defaultValue = htmlHelper.ViewData.Eval(fullName);
with:
if (defaultValue == null && !String.IsNullOrEmpty(name))
{
if (!usedViewData)
{
defaultValue = htmlHelper.ViewData.Eval(name);
}
else if (metadata != null)
{
defaultValue = metadata.Model;
}
}
Finally, you also need to add this method:
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
{
object o = null;
if (htmlHelper.ViewData != null)
{
o = htmlHelper.ViewData.Eval(name);
}
if (o == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_MissingSelectData,
name,
"IEnumerable<SelectListItem>"));
}
IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;
if (selectList == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_WrongSelectDataType,
name,
o.GetType().FullName,
"IEnumerable<SelectListItem>"));
}
return selectList;
}
replacing SelectListItem with your custom class (e.g. SdSelectListItem, or ExtendedSelectListItem , or whatever you named it )

DropDownListFor for enum with custom values (stored in attributes) doesn't select item

I have enum with assigned string values (in attributes) and would like to have html helper for displaying it. Unfortunately I need to use assigned values as a keys in ddl. Because of that it doesn't select item stored in ViewModel. What to do?
Sample Enum
public enum StatusEnum
{
[Value("AA")]
Active,
[Value("BB")]
Disabled
}
ViewModel
public class UserViewModel
{
public StatusEnum Status { get; set; }
}
View
#Html.DropDownListForEnum(x => x.Status, new {})
DropDownListForEnum helper
public static MvcHtmlString DropDownListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
IEnumerable<SelectListItem> items = Enum.GetNames(typeof(TEnum))
.Select(n => (TEnum)Enum.Parse(typeof(TEnum), n))
.Select(e => new SelectListItem() {
Text = e.ToString(),
Value = EnumExtension.GetValue(e).ToString(),
Selected = EnumExtension.GetValue(e) == EnumExtension.GetValue(metadata.Model)
})
.ToList();
return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
}
Instead of passing IEnumerable<SelectListItem> to the dropdown, create and populate SelectList manually and pass that and it will work:
public static MvcHtmlString DropDownListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var items = Enum.GetNames(typeof(TEnum))
.Select(n => (TEnum)Enum.Parse(typeof(TEnum), n))
.Select(e => new
{
Id = Convert.ToInt32(e),
Text = e.ToString(),
Value = EnumExtension.GetValue(e).ToString(),
Selected = EnumExtension.GetValue(e) == EnumExtension.GetValue(metadata.Model)
})
.ToList();
var selectList = new SelectList(items, "Text", "Value", selectedValue: items.Find(i => i.Selected).Text);
return htmlHelper.DropDownListFor(expression, selectList, htmlAttributes);
}

How to merge htmlAttributes in Custom Helper

I have a Custom Helper where I receive a htmlAttributes as parameter:
public static MvcHtmlString Campo<TModel, TValue>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel, TValue>> expression,
dynamic htmlAttributes = null)
{
var attr = MergeAnonymous(new { #class = "form-control"}, htmlAttributes);
var editor = helper.EditorFor(expression, new { htmlAttributes = attr });
...
}
The MergeAnonymous method must return the merged htmlAttributes received in parameter with "new { #class = "form-control"}":
static dynamic MergeAnonymous(dynamic obj1, dynamic obj2)
{
var dict1 = new RouteValueDictionary(obj1);
if (obj2 != null)
{
var dict2 = new RouteValueDictionary(obj2);
foreach (var pair in dict2)
{
dict1[pair.Key] = pair.Value;
}
}
return dict1;
}
And in the Editor Template for an example field I need to add some more attributes:
#model decimal?
#{
var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(ViewData["htmlAttributes"]);
htmlAttributes["class"] += " inputmask-decimal";
}
#Html.TextBox("", string.Format("{0:c}", Model.ToString()), htmlAttributes)
What I have in htmlAttributes at last line in Editor Template is:
Click here to see the image
Note that the "class" is appearing correctly, but the others attributes from the Extension Helper are in a Dictionary, what am I doing wrong?
If possible, I want to change only Extension Helper and not Editor Template, so I think the RouteValueDictionary passed to EditorFor need to cast to a anonymous object...
I solved for now changing all my Editor Templates the line:
var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(ViewData["htmlAttributes"]);
for this:
var htmlAttributes = ViewData["htmlAttributes"] as IDictionary<string, object> ?? HtmlHelper.AnonymousObjectToHtmlAttributes(ViewData["htmlAttributes"]);
and the MergeAnonymous method to this:
static IDictionary<string,object> MergeAnonymous(object obj1, object obj2)
{
var dict1 = new RouteValueDictionary(obj1);
var dict2 = new RouteValueDictionary(obj2);
IDictionary<string, object> result = new Dictionary<string, object>();
foreach (var pair in dict1.Concat(dict2))
{
result.Add(pair);
}
return result;
}
public static class CustomHelper
{
public static MvcHtmlString Custom(this HtmlHelper helper, string tagBuilder, object htmlAttributes)
{
var builder = new TagBuilder(tagBuilder);
RouteValueDictionary customAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
foreach (KeyValuePair<string, object> customAttribute in customAttributes)
{
builder.MergeAttribute(customAttribute.Key.ToString(), customAttribute.Value.ToString());
}
return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing));
}
}

Stronglytyped html helper with different model for get and post

If a Get Action returns a View with a "Car" model. The view displays info from the object and takes input to post within a form to another action that takes an object of type "Payment"
The Model on the view is of type Car and gives me stronglytyped html support and some other features like displaytext. But for posting I there is no Htmlhelper support like TextBox(x => x.amount I need to make it like #Html.TextBox("Amount"...
Its possible, but is this the only option?
You can do this:
#{
var paymentHtml = Html.HtmlHelperFor<Payment>();
}
#paymentHtml.EditorFor(p => p.Amount)
with this extension method:
public static class HtmlHelperFactoryExtensions {
public static HtmlHelper<TModel> HtmlHelperFor<TModel>(this HtmlHelper htmlHelper) {
return HtmlHelperFor(htmlHelper, default(TModel));
}
public static HtmlHelper<TModel> HtmlHelperFor<TModel>(this HtmlHelper htmlHelper, TModel model) {
return HtmlHelperFor(htmlHelper, model, null);
}
public static HtmlHelper<TModel> HtmlHelperFor<TModel>(this HtmlHelper htmlHelper, TModel model, string htmlFieldPrefix) {
var viewDataContainer = CreateViewDataContainer(htmlHelper.ViewData, model);
TemplateInfo templateInfo = viewDataContainer.ViewData.TemplateInfo;
if (!String.IsNullOrEmpty(htmlFieldPrefix))
templateInfo.HtmlFieldPrefix = templateInfo.GetFullHtmlFieldName(htmlFieldPrefix);
ViewContext viewContext = htmlHelper.ViewContext;
ViewContext newViewContext = new ViewContext(viewContext.Controller.ControllerContext, viewContext.View, viewDataContainer.ViewData, viewContext.TempData, viewContext.Writer);
return new HtmlHelper<TModel>(newViewContext, viewDataContainer, htmlHelper.RouteCollection);
}
static IViewDataContainer CreateViewDataContainer(ViewDataDictionary viewData, object model) {
var newViewData = new ViewDataDictionary(viewData) {
Model = model
};
newViewData.TemplateInfo = new TemplateInfo {
HtmlFieldPrefix = newViewData.TemplateInfo.HtmlFieldPrefix
};
return new ViewDataContainer {
ViewData = newViewData
};
}
class ViewDataContainer : IViewDataContainer {
public ViewDataDictionary ViewData { get; set; }
}
}
If I understand your question correctly, here's some code I just wrote for one of my projects to do something similar. It doesn't require anything special like what was suggested by Max Toro.
#{
var teamHelper = new HtmlHelper<Team>(ViewContext, this);
}
#using (teamHelper.BeginForm())
{
#teamHelper.LabelFor(p => p.Name)
#teamHelper.EditorFor(p => p.Name)
}
Adding to the implementation by Max Toro, here are a couple more for when you have a non-null model but don't have static type information (these two methods need to be embedded into the implementation Max provides).
These methods work well when you have dynamically retrieved property names for a model and need to call the non-generic HtmlHelper methods that take a name instead of an expression:
#Html.TextBox(propertyName)
for example.
public static HtmlHelper HtmlHelperFor( this HtmlHelper htmlHelper, object model )
{
return HtmlHelperFor( htmlHelper, model, null );
}
public static HtmlHelper HtmlHelperFor( this HtmlHelper htmlHelper, object model, string htmlFieldPrefix )
{
var t = model.GetType();
var viewDataContainer = CreateViewDataContainer( htmlHelper.ViewData, model );
TemplateInfo templateInfo = viewDataContainer.ViewData.TemplateInfo;
if( !String.IsNullOrEmpty( htmlFieldPrefix ) )
templateInfo.HtmlFieldPrefix = templateInfo.GetFullHtmlFieldName( htmlFieldPrefix );
ViewContext viewContext = htmlHelper.ViewContext;
ViewContext newViewContext = new ViewContext( viewContext.Controller.ControllerContext, viewContext.View, viewDataContainer.ViewData, viewContext.TempData, viewContext.Writer );
var gt = typeof( HtmlHelper<> ).MakeGenericType( t );
return Activator.CreateInstance( gt, newViewContext, viewDataContainer, htmlHelper.RouteCollection ) as HtmlHelper;
}
For ASP.NET Core 2
public static class HtmlHelperFactoryExtensions
{
public static IHtmlHelper<TModel> HtmlHelperFor<TModel>(this IHtmlHelper htmlHelper)
{
return HtmlHelperFor(htmlHelper, default(TModel));
}
public static IHtmlHelper<TModel> HtmlHelperFor<TModel>(this IHtmlHelper htmlHelper, TModel model)
{
return HtmlHelperFor(htmlHelper, model, null);
}
public static IHtmlHelper<TModel> HtmlHelperFor<TModel>(this IHtmlHelper htmlHelper, TModel model, string htmlFieldPrefix)
{
ViewDataDictionary<TModel> newViewData;
var runtimeType = htmlHelper.ViewData.ModelMetadata.ModelType;
if (runtimeType != null && typeof(TModel) != runtimeType && typeof(TModel).IsAssignableFrom(runtimeType))
{
newViewData = new ViewDataDictionary<TModel>(htmlHelper.ViewData, model);
}
else
{
newViewData = new ViewDataDictionary<TModel>(htmlHelper.MetadataProvider, new ModelStateDictionary())
{
Model = model
};
}
if (!String.IsNullOrEmpty(htmlFieldPrefix))
newViewData.TemplateInfo.HtmlFieldPrefix = newViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldPrefix);
ViewContext newViewContext = new ViewContext(htmlHelper.ViewContext, htmlHelper.ViewContext.View, newViewData, htmlHelper.ViewContext.Writer);
var newHtmlHelper = htmlHelper.ViewContext.HttpContext.RequestServices.GetRequiredService<IHtmlHelper<TModel>>();
((HtmlHelper<TModel>)newHtmlHelper).Contextualize(newViewContext);
return newHtmlHelper;
}
}
If I understand your problem correctly, try:
#Html.EditorFor(x => x.Amount)
You could also create an editor template for Payment. See this page for details on doing this.
If I'm misunderstanding, some sample code might help.

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