I want to extend the DropDownList to accept any type of list .
The code that I have is for enums. I want to use it for whatever list not only enums.
public static MvcHtmlString DropDownListFor<TModel, TProperty, TEnum>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
TEnum selectedValue)
{
var values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>();
var items = from value in values
select new SelectListItem()
{
Text = value.ToString(),
Value = value.ToString(),
Selected = value.Equals(selectedValue)
};
return SelectExtensions.DropDownListFor(htmlHelper, expression, items);
}
how to refactor this code so it can be used for all type of lists like ( vehicle type, make models, year ..etc)
I got my code working by using the code below :
public static MvcHtmlString DropDownListFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
List<dynamic> selectedValue)
{
var items = from value in selectedValue
select new SelectListItem()
{
Text = value.ToString(),
Value = value.ToString()
};
return htmlHelper.DropDownListFor(expression, items);
}
Related
I want to create a dropdown list using description of enum instead of its value.
I'd like to know how to get descriptions instead of values in the following code which creates a dropdown list for enum :
public static MvcHtmlString DropDownListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
{
// get expression property description
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
IEnumerable<TEnum> values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>();
IEnumerable<SelectListItem> items =
values.Select(value => new SelectListItem
{
Text = value.ToString(),
Value = value.ToString(),
Selected = value.Equals(metadata.Model)
});
return htmlHelper.DropDownListFor(
expression,
items
);
}
First, make a new method to get the description like shown below:
public static string GetDescription<T>(string value)
{
Type type = typeof(T);
if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = Nullable.GetUnderlyingType(type);
}
T enumerator = (T)Enum.Parse(type, value);
FieldInfo fi = enumerator.GetType().GetField(enumerator.ToString());
DescriptionAttribute[] attributtes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributtes != null && attributtes.Length > 0)
return attributtes[0].Description;
else
return enumerator.ToString();
}
And then use it in your helper:
public static MvcHtmlString DropDownListForEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
{
// get expression property description
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
IEnumerable<TEnum> values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>();
IEnumerable<SelectListItem> items =
values.Select(value => new SelectListItem
{
Text = value.ToString(),
Value = GetDescription<TEnum>(value.ToString()),
Selected = value.Equals(metadata.Model)
});
return htmlHelper.DropDownListFor(
expression,
items
);
}
Use Enum getnames to get the names
http://msdn.microsoft.com/en-us/library/system.enum.getnames(v=vs.110).aspx
I need to get working validation of the custom ASP.NET MVC helper.
Helper
public static class AutocompleteHelper
{
public static MvcHtmlString AutocompleteFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, string actionUrl)
{
return CreateAutocomplete(helper, expression, actionUrl, null, null);
}
public static MvcHtmlString AutocompleteFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, string actionUrl, bool? isRequired, string placeholder)
{
return CreateAutocomplete(helper, expression, actionUrl, placeholder, isRequired);
}
private static MvcHtmlString CreateAutocomplete<TModel, TValue>(HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, string actionUrl, string placeholder, bool? isRequired)
{
var attributes = new Dictionary<string, object>
{
{ "data-autocomplete", true },
{ "data-action", actionUrl }
};
if (!string.IsNullOrWhiteSpace(placeholder))
{
attributes.Add("placeholder", placeholder);
}
if (isRequired.HasValue && isRequired.Value)
{
attributes.Add("required", "required");
}
attributes.Add("class", "form-control formControlAutocomplete");
attributes.Add("maxlength", "45");
Func<TModel, TValue> method = expression.Compile();
var value = method((TModel)helper.ViewData.Model);
var baseProperty = ((MemberExpression)expression.Body).Member.Name;
var hidden = helper.Hidden(baseProperty, value);
attributes.Add("data-value-name", baseProperty);
var automcompleteName = baseProperty + "_autocomplete";
var textBox = helper.TextBox(automcompleteName, null, string.Empty, attributes);
var builder = new StringBuilder();
builder.AppendLine(hidden.ToHtmlString());
builder.AppendLine(textBox.ToHtmlString());
return new MvcHtmlString(builder.ToString());
}
}
HTML
#Html.AutocompleteFor(x => x.ProductUID, Url.Action("AutocompleteProducts", "Requisition"), true, "Start typing Product name...")
#Html.ValidationMessageFor(x => x.ProductUID)
I seems like validating but no message appears.
Any clue?
The name of your text field is ProductUID_autocomplete but your ValidationMessageFor which is supposed to display the error message is bound to ProductUID.
So make sure that you are binding your error message to the same property:
#Html.ValidationMessage("ProductUID_autocomplete")
It appears that whatever custom logic you might have to validate this field is injecting the error under the ProductUID_autocomplete key in the ModelState.
This being said, why not just invoke the ValidationMessage helper inside your custom helper? This way you will have less things to type in your view and the logic with those names being suffixed with _autocomplete will stay inside the helper only.
As far as I know there are no (extension) methods in the HtmlHelper class that can generate a HTML5 input element of type range so I am trying to implement my own by extending the HtmlHelper class:
public static class MvcHtmlHelper
{
public static HtmlString RangeFor<TModel, TProperty>
(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
object htmlAttributes)
{
var name = ExpressionHelper.GetExpressionText(expression);
var metadata = ModelMetadata.FromLambdaExpression(expression,
htmlHelper.ViewData);
//var min = (string)((ViewDataDictionary<TModel>)htmlAttributes)["min"];
//var max = (string)((ViewDataDictionary<TModel>)htmlAttributes)["max"];
//var value = (string)((ViewDataDictionary<TModel>)htmlAttributes)["value"];
return Range(htmlHelper, min, max, value);
}
public static HtmlString Range(this HtmlHelper htmlHelper,
string name, string min, string max, string value = "0")
{
var builder = new TagBuilder("input");
builder.Attributes["type"] = "range";
builder.Attributes["name"] = name;
builder.Attributes["min"] = min;
builder.Attributes["max"] = max;
builder.Attributes["value"] = value;
return new HtmlString(builder.ToString(TagRenderMode.SelfClosing));
}
}
I am trying to emulate the existing TextBoxFor extension method which allows callers to specify extra htmlAttributes via an anonymous object. However I am getting an InvalidCastException on the commented lines above.
Can anyone point me to the correct way of obtaining the values from the anonymous htmlAttributes object (similar to how TextBoxFor handles htmlAttributes?
InputExtensions.TextBoxFor handles attributes the following way:
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
return InputExtensions.TextBoxFor<TModel, TProperty>(htmlHelper, expression, (IDictionary<string, object>) HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
So you have to create attributes dictionary from anomymous object before you use it
I'm using right now this code to implement a RadioButtonList using MVC4.
And as you can see, that function does not have htmlAttributes parameter. So I'd like to add it and here is the problem. Check please that the htmlAttributes for RadioButtonFor() is occupied by the id.
I was trying to add it but throws me errors because the id already exists for the loop.
public static class HtmlExtensions
{
public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> listOfValues)
{
return htmlHelper.RadioButtonForSelectList(expression, listOfValues, null);
}
public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> listOfValues,
object htmlAttributes)
{
return htmlHelper.RadioButtonForSelectList(expression, listOfValues, new RouteValueDictionary(htmlAttributes));
}
public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> listOfValues,
IDictionary<string, object> htmlAttributes)
{
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var sb = new StringBuilder();
if (listOfValues != null)
{
foreach (SelectListItem item in listOfValues)
{
var id = string.Format(
"{0}_{1}",
metaData.PropertyName,
item.Value
);
var radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id }).ToHtmlString();
sb.AppendFormat(
"{0}<label for=\"{1}\">{2}</label>",
radio,
id,
HttpUtility.HtmlEncode(item.Text)
);
}
}
return MvcHtmlString.Create(sb.ToString());
}
}
In the third method, it looks like the html attributes being passed to the radion button being created is new { id = id }. Try to replace that with the parameter from the method.
UPDATED
Include id in the html attributes and assign a new value to id in each loop iteration.
if (listOfValues != null)
{
if (!htmlAttributes.ContainsKey("id"))
{
htmlAttributes.Add("id", null);
}
foreach (SelectListItem item in listOfValues)
{
var id = string.Format(
"{0}_{1}",
metaData.PropertyName,
item.Value
);
htmlAttributes["id"] = id;
var radio = htmlHelper.RadioButtonFor(expression, item.Value, htmlAttributes).ToHtmlString();
sb.AppendFormat(
"{0}<label for=\"{1}\">{2}</label>",
radio,
id,
HttpUtility.HtmlEncode(item.Text)
);
}
}
I want to create a custom method, to be able to call it as
#Html.PaginationFor(o => o.List)
I started looking at reflector, but I don't know exactly what it is doing over there. I tried:
public static MvcHtmlString PaginationFor<TModel, TProperty>(this Html<TModel> html,
Expression<Func<TModel, TProperty>> expression)
{
var propertyValue = ????????
return html.Partial("View", propertyValue);
}
How do I extract the property value from the expression to pass as a model of the partial view?
public static MvcHtmlString PaginationFor<TModel, TProperty>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TProperty>> expression
)
{
TModel model = html.ViewData.Model;
var metaData = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var propertyValue = metaData.Model; // This will be of type TProperty
return html.Partial("View", propertyValue);
}
public static MvcHtmlString PaginationFor<TModel, TProperty>(this HtmlHelper<TModel> html,
Expression<Func<TModel, TProperty>> expression) {
var func = expression.Compile();
var propertyValue = func(html.ViewData.Model);
return html.Partial("View", propertyValue);
}