I am generating HTML textbox through the html helper and TagBuilder.
we have the method TagBuilder.Attributes.Add("key","value")
but for HTML5 required attribute does not need value to be passed, so if i pass empty string then the output with value of required = ""
So how do i add required attribute without passing the value?
public static IHtmlString AppTextBox(this HtmlHelper helper, string model)
{
var input = new TagBuilder("input");
input.Attributes.Add("class", "form-control");
input.Attributes.Add("ng-model", model);
input.Attributes.Add("required","");
return new MvcHtmlString(input.ToString(TagRenderMode.Normal));
}
It's also valid to pass the name of the attribute as the value:
input.Attributes.Add("required", "required");
I've tested on MVC 5, not sure about older versions but the following does what you want.
tagBuilder.MergeAttribute("required", string.Empty);
Not sure if you still need an answer to this, but in the end I ended up writing a new class that derives from the base MVC tag builder. It's nothing too complex and I suspect there may be a few edge cases that this doesn't handle too well, but from the unit tests I have so far, it's pretty solid.
internal class ValuelessAttributeTagBuilder : System.Web.Mvc.TagBuilder
{
public List<string> ValuelessAttributes { get; private set; }
public ValuelessAttributeTagBuilder(string tagName) : base(tagName)
{
ValuelessAttributes = new List<string>();
}
public void AddValuelessAttribute(string value)
{
if(ValuelessAttributes.Contains(value))
ValuelessAttributes.Add(value);
}
/// <summary>
/// Renders the HTML tag by using the specified render mode.
/// </summary>
///
/// <returns>
/// The rendered HTML tag.
/// </returns>
/// <param name="renderMode">The render mode.</param>
public string ToString(TagRenderMode renderMode)
{
var sb = new StringBuilder();
switch (renderMode)
{
case TagRenderMode.StartTag:
sb.Append('<').Append(this.TagName);
AppendAttributes(sb);
AppendValuelessAttributes(sb);
sb.Append('>');
break;
case TagRenderMode.EndTag:
sb.Append("</").Append(this.TagName).Append('>');
break;
case TagRenderMode.SelfClosing:
sb.Append('<').Append(this.TagName);
AppendAttributes(sb);
AppendValuelessAttributes(sb);
sb.Append(" />");
break;
default:
sb.Append('<').Append(this.TagName);
AppendAttributes(sb);
AppendValuelessAttributes(sb);
sb.Append('>').Append(this.InnerHtml).Append("</").Append(this.TagName).Append('>');
break;
}
return sb.ToString();
}
private void AppendAttributes(StringBuilder sb)
{
foreach (KeyValuePair<string, string> keyValuePair in (IEnumerable<KeyValuePair<string, string>>)this.Attributes)
{
string key = keyValuePair.Key;
if (!string.Equals(key, "id", StringComparison.Ordinal) || !string.IsNullOrEmpty(keyValuePair.Value))
{
string str = HttpUtility.HtmlAttributeEncode(keyValuePair.Value);
sb.Append(' ').Append(key).Append("=\"").Append(str).Append('"');
}
}
}
private void AppendValuelessAttributes(StringBuilder sb)
{
foreach (var v in ValuelessAttributes)
{
var str = HttpUtility.HtmlAttributeEncode(v);
sb.Append(' ').Append(str);
}
}
}
Related
I'm trying to write a helper for my ASP.NET MVC3 website which will be able to return a new SelectList containing all the Description attribute tag of an Enum
For example, with the following enum :
public enum Test
{
[Display(Name = "Membre 1")]
Member1,
[Display(Name = "Membre 2")]
Member2
}
I would like to be able to fill a DropDownListFor with something like :
#Html.DropDownListFor(m => m.MyTest, MyHelper(Test))
(with MyTest is a Test variable).
and I expect my DropDownList contains :
Membre 1
Membre 2
I used to use this working helper :
public static string GetEnumDescription(this Enum value)
{
Type enumType = value.GetType();
var enumValue = Enum.GetName(enumType, value);
MemberInfo member = enumType.GetMember(enumValue)[0];
var attrs = member.GetCustomAttributes(typeof(DisplayAttribute), false);
var outString = ((DisplayAttribute)attrs[0]).Name;
if (((DisplayAttribute)attrs[0]).ResourceType != null)
{
outString = ((DisplayAttribute)attrs[0]).GetName();
}
return outString;
}
... but I can't get it work in a SelectList
How can I modify this to directly "incorporate" it directly in my #Html.DropDownListFor helper ?
I have seen some helper over the Internet, especially here or here, but no one works for me. Does anyone is able to share a short and elegant helper which returns all the Display attributes of the members of an Enum in order to put them in a DropDownListFor ?
The following is what I use. It's a slightly modified version of something I found online at one point. I'd give credit where credit is due, but I don't remember where I found it originally at this point:
public static SelectList ToSelectList(this Enum enumeration)
{
var list = (from Enum d in Enum.GetValues(enumeration.GetType())
select new { Value = Enum.GetName(enumeration.GetType(), d), Text = d.GetDescription() }).ToList();
var selectedValue = (int)Enum.Parse(enumeration.GetType(), Enum.GetName(enumeration.GetType(), enumeration));
return new SelectList(list, "Value", "Text");
}
public static string GetDescription(this Enum 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();
}
In your view, you'd use it:
#Html.DropDownListFor(m => m.MyEnumProperty, Model.MyEnumProperty.ToSelectList())
For implementing Enum type data, I think the easiest way is to use custom Enum helper and Templates. Below is how I implement them in my project.
1) Create Enum Helper
public static class EnumHelper
{
public static IEnumerable<SelectListItem> GetItems(this Type enumType, int? selectedValue)
{
if (!typeof (Enum).IsAssignableFrom(enumType))
{
throw new ArgumentException("Type must be an enum");
}
string[] names = Enum.GetNames(enumType);
IEnumerable<int> values = Enum.GetValues(enumType).Cast<int>();
IEnumerable<SelectListItem> items = names.Zip(values, (name, value) =>
new SelectListItem
{
Text = GetName(enumType, name),
Value = value.ToString(),
Selected = value == selectedValue
}
);
return items;
}
// Get Display Name
private static string GetName(Type enumType, string name)
{
string result = name;
DisplayAttribute attribute = enumType.GetField(name)
.GetCustomAttributes(false)
.OfType<DisplayAttribute>()
.FirstOrDefault();
if (attribute != null)
{
result = attribute.GetName();
}
return result;
}
public static string GetItemName(this Type enumType, int selectedValue)
{
if (!typeof (Enum).IsAssignableFrom(enumType))
{
throw new ArgumentException("Type must be an enum");
}
var itemName = GetName(enumType, Enum.GetNames(enumType)[selectedValue]);
return itemName;
}
}
2) Create folder call "DisplayTemplates" in Shared folder.
3) Create View inside "DisplayTemmplates". The view will look like below:
#using Demo.Web.Helper
#{
var itemName = typeof(Test).GetItemName((int)Model);
}
4) Create floder call "EditorTemplates" in Shared folder.
5) Create View inside "EditorTemplates". The view will look like below:
#using Demo.Web.Helper
#{
var items = typeof (Test).GetItems((int?)Model);
}
#Html.DropDownList("",items)
Here you have finished all of helper and templates, ready for use. When you want to implement Enum Type data, just use it like below:
Model
public class MyModel
{
public int Id { get; set; }
//
public Test Test { get; set; }
}
View
#Html.DisplayFor(m => m.Test)
or
#Html.EditorFor(m => m.Test)
Hope it helps.
I've written an If-IsRequired custom attribute to validate that a property contains a value depending on the values of some other properties in the model. Since I want to make this attribute apply to as many situations as possible, I want to allow the option for the developer leveraging the attribute to supply an infinite number of matched parameters. And lastly, I want to be able to enforce that all the parameters are matched correctly.
This is what I've written thus far. While I'm currently using arrays of strings, I'd be perfectly happy to use some sort of collection, which been unable to work. In addition, I now have a need to support the current attribute definition and create a new overload that includes the comparison operator. This will allow me to make less than, greater than, and not equal comparisons in addition to the original definition which just assumes all comparisons are done with equals.
/// <summary>
/// A custom attribute that checks the value of other properties passed to it in order to
/// determine if the property this attribute is bound to should be required.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class IsPropertyRequiredAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "{0} is required.";
public string[] _selectionContextNames { get; private set; }
public string[] _expectedValues { get; private set; }
/// <summary>
/// Creates a new instance of the IsPropertyRequriedAttribute.
/// </summary>
/// <param name="SelectionContextNames">The name of the other property in the view model to check the value of.</param>
/// <param name="ExpectedValues">The expected value of the other property in the view model in order to determine if the current property the attribute is bound to should be required.</param>
public IsPropertyRequiredAttribute(string[] SelectionContextNames, string ExpectedValues)
: base(DefaultErrorMessage)
{
_selectionContextNames = SelectionContextNames;
_expectedValues = ExpectedValues;
}
public override bool IsValid(object value)
{
if (_selectionContextNames == null || _expectedValues == null)
{
if (_selectionContextNames != null || _expectedValues != null)
{
string paramName;
if (_selectionContextNames == null)
{
paramName = "ExpectedValues";
}
else
{
paramName = "SelectionContextNames";
}
throw new ArgumentException("Key/Value pairs need to match for IsPropertyRequired.", paramName);
}
}
else if (_selectionContextNames.Length != _expectedValues.Length)
{
string paramName;
if (_selectionContextNames.Length < _expectedValues.Length)
{
paramName = "ExpectedValues";
}
else
{
paramName = "SelectionContextNames";
}
throw new ArgumentException("Parameter element counts need to match for IsPropertyRequired.", paramName);
}
bool paramsValid = true;
if (_selectionContextName!= null)
{
for (int i = 0; i < _selectionContextName.Length; i++)
{
string paramValue = HttpContext.Current.Request[_selectionContextName[i]];
if (_expectedValue[i] != paramValue)
{
paramsValid = false;
}
}
if (paramsValid == true)
{
return (value != null);
}
else
{
return true;
}
}
else
{
return true;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(DefaultErrorMessage, name);
}
}
While using the attribute to decorate the property will depend on how the attribute is defined, this is what I have currently implemented (which could also probably be improved):
[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4"}, new string[] {"1", "2", "3", "4"})]
public string SomeText { get; set; }
Also, I want to prevent, as much as I can, the following decoration from happening:
[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4", "prop5withoutvalue"}, new string[] {"1", "2", "3", "4"})]
public string SomeOtherText { get; set; }
And with the new overload including comparison operators as a parameter, we could now have:
[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4"}, new string[] {"==", ">", "!=", "<="}, new string[] {"1", "2", "3", "4"})]
public string SomeComparisonText { get; set; }
Attributes in .NET are very limited in the allowed types you can specify, as mentioned on MSDN. If you want more complex data to be specified, I would recommend writing the attribute to specify an alternate location for the richer data structure.
For example, imagine an attribute with this syntax:
[ValidationRules(typeof(MyValidationRuleInfo, "MyRuleSet")]
public int SomeProperty { get; set; }
...
public static class MyValidationRuleInfo {
public static Dictionary<string, ValidationRule> MyRuleSet {
get {
return new { ... rules go here ... }
}
}
And the attribute would look up the property on the target class and get all the rules there. It's still up to you to implement all the logic of all the rules, but you get to avoid attribute soup, and you also avoid unwieldy data structures.
In fact, the xUnit.NET unit testing library does something similar with its Theory and PropertyData attributes, as shown here.
Im using RadioButtonList to generate my radio buttons, in a MVC Razor project.
Here's an example of my code:
#Html.RadioButtonList(n => n.HouseType)
For some reason my radio button lists gets a preselected value. The first checkbox is always checked, which makes my UI kinda confusing.
How do I disable this in a good way?
One way is to loop through the whole page with Jquery and unselect each box. But thats not a pretty work around imho.
EDIT:
Here's more info about HouseType, which is a custom enum.
public enum HouseType
{
House,
Apartment,
Garage
};
and its called upon by using this line
public HouseType HouseType { get; set; }
You could make the HouseType property a nullable type on your view model. For example if it is an enum type:
public HouseTypes? HouseType { get; set; }
or if it is an integer:
public int? HouseType { get; set; }
UPDATE:
It seems that you are using the following helper. This helper doesn't support nullable enum values. So adapt it:
public static class RaidioButtonListHelper
{
/// <summary>
/// Create a radiobutton list from viewmodel.
/// </summary>
public static MvcHtmlString RadioButtonList<TModel, TResult>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TResult>> expression, IEnumerable<SelectListItem> listOfValues = null)
{
var typeOfProperty = expression.ReturnType;
// Added by Darin Dimitrov to support nullable enums
var underlyingType = Nullable.GetUnderlyingType(typeOfProperty);
if (underlyingType != null)
{
typeOfProperty = underlyingType;
}
// End of addition
if (listOfValues == null && typeOfProperty.IsEnum)
{
listOfValues = new SelectList(Enum.GetValues(typeOfProperty));
}
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
// Ctreat table
TagBuilder tableTag = new TagBuilder("table");
tableTag.AddCssClass("radio-main");
// Create tr:s
var trTagLable = new TagBuilder("tr id=\"" + metaData.PropertyName + "Lables\"");
var trTagRadio = new TagBuilder("tr id=\"" + metaData.PropertyName + "Radios\"");
foreach (SelectListItem item in listOfValues)
{
var text = item.Text;
var value = item.Value ?? text;
// Generate an id to be given to the radio button field
var id = string.Format("{0}_{1}", metaData.PropertyName, value);
// Create the radiobuttons
var radioTag = htmlHelper.RadioButtonFor(expression, value, new { id = id }).ToHtmlString();
// Create the label for the radiobuttons.
var labelTag = htmlHelper.Label(id, HttpUtility.HtmlEncode(text));
// Add the lables and reaiobuttons to td:s
var tdTagLable = new TagBuilder("td style=\"padding-left: 10px; text-align: center\"");
var tdTagRadio = new TagBuilder("td style=\"padding-left: 10px; text-align: center\"");
tdTagLable.InnerHtml = labelTag.ToString();
tdTagRadio.InnerHtml = radioTag.ToString();
// Add tds: to tr:s
trTagLable.InnerHtml += tdTagLable.ToString();
trTagRadio.InnerHtml += tdTagRadio.ToString();
}
// Add tr:s to table
tableTag.InnerHtml = trTagLable.ToString() + trTagRadio.ToString();
//Return the table tag
return new MvcHtmlString(tableTag.ToString());
}
}
Now it's gonna work with a nullable enum and it won't select any radio button if the value of the corresponding property is null.
Let's say that I got the following class and enum:
public class MyModel
{
[DisplayName("Min egenskap")]
public MyEnum TheProperty {get;set;}
}
public enum MyEnum
{
[DisplayName("Inga från Sverige")]
OneValue,
[DisplayName("Ett annat värde")]
AnotherValue
}
The above code doesn't work since DisplayNameAttribute cannot be used on enums. Are there another attribute that can be used?
What I want to do is to generate a nice html select tag using something like Html.SelectListFor(m => m.TheProperty). The list would use the DisplayNameAttribute or similar attribute during generation.
Wanted result:
<select name="TheProperty">
<option value="OneValue">Inga från Sverige</option>
<option value="AnotherValue" selected="selected">Ett annat värde</option>
</select>
An example of how to do this is to use the [Description] attribute on your enum:
public enum DaysOfWeek
{
[Description("Monday")]
Monday = 1,
[Description("Tuesday")]
Tuesday = 2
}
Then create this EnumerationHelper class that will allow you to get the Description attribute of your enum:
public static class EnumerationHelper
{
//Transforms an enumeration description into a string
public static string Description<TEnum>(this TEnum enumObject)
{
Type type = enumObject.GetType();
MemberInfo[] memInfo = type.GetMember(enumObject.ToString());
if(memInfo != null && memInfo.Length > 0)
{
DescriptionAttribute[] attributes = (DescriptionAttribute[])memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
}
return enumObject.ToString();
}
}
Then you can query your enum class to get the value and description to then build a SelectList. You must reference the EnumerationHelper in this class:
var listOfDaysOfWeek = (from DaysOfWeek d in Enum.GetValues(typeof(DaysOfWeek))
select new { ID = d, Description = d.Description() });
viewModel.selectListDaysOfWeek = new SelectList(listOfDaysOfWeek, "ID", "Description");
And then finally in your view:
<%: Html.DropDownListFor(m => m.DayOfWeek, Model.DaysOfWeek) %>
I hope this helps.
I wanted to display the Enum in the view so I made a similar Html helper:
/// <summary>
/// Returns the [Description] value of a Enum member.
/// </summary>
/// <typeparam name="TModel"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="helper"></param>
/// <param name="expression"></param>
/// <returns></returns>
public static MvcHtmlString DisplayEnumFor<TModel, TResult>(this HtmlHelper<TModel> helper,
Expression<Func<TModel, TResult>> expression) where TResult : struct {
TResult value = expression.Compile().Invoke(helper.ViewData.Model);
string propName = ExpressionHelper.GetExpressionText(expression);
var description = typeof(TResult).GetMember(value.ToString())[0]
.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
if (description != null) {
return MvcHtmlString.Create((description as DescriptionAttribute).Description);
}
return MvcHtmlString.Create(value.ToString());
}
Usage:
#Html.DisplayEnumFor(m => m.SomeEnumProperty)
Anyone understand why the following doesn't work?
What I want to do is copy current route data plus whatever I add via an anonymous object into new routedata when forming new links on the view.
For example if I have the parameter "page" as a non route path (i.e. so it overflows the route path and its injected into the method parameter if a querystring is present) e.g.
public ActionResult ChangePage(int? page) { }
and I want the View to know the updated page when building links using helpers. I thought the best way to do this is with the following:
public ActionResult ChangePage(int? page)
{
if(page.HasValue)
RouteData.Values.Add("Page", page);
ViewData.Model = GetData(page.HasValue ? page.Value : 1);
}
Then in the view markup I can render my next, preview, sort, showmore (any links relevant) with this overload:
public static class Helpers
{
public static string ActionLinkFromRouteData(this HtmlHelper helper, string linkText, string actionName, object values)
{
RouteValueDictionary routeValueDictionary = new RouteValueDictionary();
foreach(var routeValue in helper.ViewContext.RouteData.Values)
{
if(routeValue.Key != "controller" && routeValue.Key != "action")
{
routeValueDictionary[routeValue.Key] = routeValue;
}
}
foreach(var prop in GetProperties(values))
{
routeValueDictionary[prop.Name] = prop.Value;
}
return helper.ActionLink(linkText, actionName, routeValueDictionary;
}
private static IEnumerable<PropertyValue> GetProperties(object o)
{
if (o != null) {
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(o);
foreach (PropertyDescriptor prop in props) {
object val = prop.GetValue(o);
if (val != null) {
yield return new PropertyValue { Name = prop.Name, Value = val };
}
}
}
}
private sealed class PropertyValue
{
public string Name { get; set; }
public object Value { get; set; }
}
}
I have posted the code only to illustrate the point. This doesn't work and doesn't feel right... Pointers?
Pass the page info into ViewData?
PagedResultsInfo (or something) sounds like a class you could write too... we do.