How can I change my extension method in C# - c#

I started to use this extension that I found on the web:
public static class NewLabelExtensions
{
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
return LabelFor(html, expression, new RouteValueDictionary(htmlAttributes));
}
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
var labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(labelText))
{
return MvcHtmlString.Empty;
}
var tag = new TagBuilder("label");
tag.MergeAttributes(htmlAttributes);
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
tag.SetInnerText(labelText);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
}
I use it like this:
#Html.LabelFor(m => m.Login.RememberMe, new { #class = "adm" })
The result is like this:
<label class="adm" for="Login_RememberMe">Remember me?</label>
However I would like to style this label. I don't really understand the code that I am using. Can anyone suggest a change to the code above that would make the LabelFor method generate?
<label class="adm" id="Login_RememberMe" for="Login_RememberMe">Remember me?</label>
Thanks

you just need to add the below line :
tag.Attributes.Add("id", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
code:
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
var labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(labelText))
{
return MvcHtmlString.Empty;
}
var tag = new TagBuilder("label");
tag.MergeAttributes(htmlAttributes);
tag.Attributes.Add("id", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
tag.SetInnerText(labelText);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}

Related

C# Pass Class Property Name as parameter

In my razor view I am using an HTML Helper Extension method in order to use the Display Name attribute of my Model Property to display a placeholder value in a textbox. See example below.
My Helper Extension Method:
public static MvcHtmlString TextBoxPlaceHolderFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (!String.IsNullOrEmpty(labelText))
{
if (htmlAttributes == null)
{
htmlAttributes = new Dictionary<string, object>();
}
htmlAttributes.Add("placeholder", labelText);
}
return html.TextBoxFor(expression, htmlAttributes);
}
My Model Property:
[Display(Name = "First Name", Description = "Enter Display Name")]
public string FirstName { get; set; }
Current Outcome:
Display Name of property displays inside the field as a placeholder.
However, sometimes I might want to use another property on the model for the placeholder text (like the Description property in the model above).
How can I make it so a Model data annotation property can be passed in the razor view and the the placeholder uses the value of that property instead?
Example code of what I want is below.
The razor view with meta-data property passed:
#Html.TextBoxPlaceHolderFor(model => model.FirstName, new { #class = "form-control" }, MODELMETADATAPROPERTY HERE)
The HTML Helper Extension (what I imagine the code would do, probably need to use reflection or something?)
public static MvcHtmlString TextBoxPlaceHolderFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes, MODELMETADATAPROPERTY HERE)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string labelText = metadata.MODELMETADATAPROPERTY HERE;
if (!String.IsNullOrEmpty(labelText))
{
if (htmlAttributes == null)
{
htmlAttributes = new Dictionary<string, object>();
}
htmlAttributes.Add("placeholder", labelText);
}
return html.TextBoxFor(expression, htmlAttributes);
}
you can use the following
public static MvcHtmlString TextBoxPlaceHolderFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string labelText = metadata.DisplayName ?? Proper(metadata.PropertyName?? htmlFieldName.Split('.').Last());
if (!String.IsNullOrEmpty(labelText))
{
if (htmlAttributes == null)
{
htmlAttributes = new Dictionary<string, object>();
}
htmlAttributes.Add("placeholder", modeldata.Description??Proper(metadata.PropertyName?? htmlFieldName.Split('.').Last())); // to get the description value
}
return html.TextBoxFor(expression, labelText);
}
// this function will convert FullName to Full Name with space
private static string Proper(string value)
{
var newValue = Regex.Replace(value, "([a-z])([A-Z])", "$1 $2");
return newValue;
}

Extend Kendo HtmlHelpers for TextBoxFor

I'm looking how to extend Kendo HtmlHelpers to do things like
#Html.Kendo().TextBoxFor(model => model.field)
This is my suggestion
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using Kendo.Mvc.UI.Fluent;
namespace Kendo.Mvc.UI
{
public static class KendoExtensions
{
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this WidgetFactory<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return htmlHelper.TextBoxFor(expression, format: null);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this WidgetFactory<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string format)
{
return htmlHelper.TextBoxFor(expression, format, null);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this WidgetFactory<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
return htmlHelper.TextBoxFor(expression, null, htmlAttributes);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this WidgetFactory<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string format, object htmlAttributes)
{
return htmlHelper.TextBoxFor(expression, format, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this WidgetFactory<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
{
return htmlHelper.TextBoxFor(expression, null, htmlAttributes);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this WidgetFactory<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string format, IDictionary<string, object> htmlAttributes)
{
var lKWidget = new TagBuilder("span");
lKWidget.AddCssClass("k-widget k-numerictextbox");
var lKExpanding = new TagBuilder("span");
lKExpanding.AddCssClass("k-numeric-wrap k-expand-padding k-state-disabled");
if (htmlAttributes == null) htmlAttributes = new Dictionary<string, object>();
if (htmlAttributes.ContainsKey("class"))
{
htmlAttributes["class"] += "k-formatted-value k-input";
} else
{
htmlAttributes.Add("class", "k-formatted-value k-input");
}
var lTextBoxFor = htmlHelper.HtmlHelper.TextBoxFor(expression, format, htmlAttributes).ToHtmlString();
lKExpanding.InnerHtml += lTextBoxFor;
lKWidget.InnerHtml += lKExpanding;
lKWidget.InnerHtml += htmlHelper.HtmlHelper.ValidationMessageFor(expression);
return MvcHtmlString.Create(lKWidget.ToString(TagRenderMode.Normal));
}
}
}
New version of Kendo UI (2014.2.716) has this extension, and much more...
I use this with lable
#Html.EnhancedEditorField(model => model.Title)
in class InputExtensions
public static class InputExtensions
{
public const string noteTemplate = #"<span class=""inputNote"">{0}</span>";
public const string fieldClosing = #"</tr>";
public const string fieldOpenning = #"<tr>";
public const string fieldContent =
#"<td class=""labelWrp""> {0} </td> <td class=""inputWrp"" colspan=""{4}""> {1} <br /> {2} {3} </td>";
public static MvcHtmlString EnhancedEditorField<TModel, TValue>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TValue>> expression,
object htmlAttributes = null, string note = null)
{
return EnhancedField(htmlHelper, expression, htmlHelper.EditorFor(expression, htmlAttributes), note);
}
private static MvcHtmlString EnhancedField<TModel, TValue>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TValue>> expression, MvcHtmlString inputString,
string note = null, FieldGroupingSetting fieldGroupingSetting = FieldGroupingSetting.CloseBoth, bool checkIsRequired = true)
{
MvcHtmlString labelString = htmlHelper.EnhancedLabelFor(expression, checkIsRequired: checkIsRequired);
MvcHtmlString validationMessageString = htmlHelper.ValidationMessageFor(expression);
return CreateField(inputString.ToString(), note, labelString.ToString(), validationMessageString.ToString(),
fieldGroupingSetting);
}
private static MvcHtmlString CreateField(string inputString, string note, string labelString,
string validationMessageString,
FieldGroupingSetting fieldGroupingSetting = FieldGroupingSetting.CloseBoth, int valueCellCount = 1)
{
string noteString = string.Empty;
if (!string.IsNullOrWhiteSpace(note))
noteString = string.Format(noteTemplate, note);
string fieldTemplate =
(fieldGroupingSetting.HasFlag(FieldGroupingSetting.CloseStart) ? fieldOpenning : string.Empty) +
fieldContent +
(fieldGroupingSetting.HasFlag(FieldGroupingSetting.CloseEnd) ? fieldClosing : string.Empty);
string htmlString = string.Format(fieldTemplate, labelString, inputString, validationMessageString,
noteString, valueCellCount);
return MvcHtmlString.Create(htmlString);
}
}

Custom HtmlHelper method that generates an <input type="range"> HtmlString

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

How to add a htmlAttributes in this HtmlHelper?

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)
);
}
}

How to write a custom #Html.ControlFor(o => o.Property)?

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);
}

Categories

Resources