The following code is generating an error:
public static MvcHtmlString EditControlFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
ModelMetadata metadata = GetModelMetaData(htmlHelper, expression);
Type propertyType = GetPropertyType(metadata);
if (propertyType.IsEnum)
{
return DropDownListForEnum(htmlHelper, expression);
}
else if (propertyType == typeof(bool))
{
return htmlHelper.CheckBoxFor(expression);
}
else
{
return htmlHelper.TextBoxFor(expression);
}
}
The compile time error is below:
Error: cannot convert from
'System.Linq.Expressions.Expression<System.Func<TModel,TProperty>>'
to 'System.Linq.Expressions.Expression<System.Func<TModel,bool>>'.
It looks like CheckBoxFor() is expecting a parameter of type :
Expression<Func<TModel, bool>>
Because of the propertyType == typeof(bool) test, it should already be the right thing - you just need to convince the compiler that your intentions are honest. In this case, via:
var typedExpression = (System.Linq.Expressions.Expression<System.Func<TModel,bool>>)(object)expression;
return htmlHelper.CheckBoxFor(typedExpression);
Related
I am trying to create an extension that I can call like so
#Html.HiddenForSuperComplex(x => x.Abc)
I want it to recursively loop through the properties for that model/expression and create Hiddens for all the fields in that model/expression so that the model will be preserved.
I've hit a roadblock with the following line:
str = htmlHelper.HiddenForComplex<TProperty,mt>(a.Expression);
But I'm starting to think I'm going down the wrong path...
public static MvcHtmlString HiddenForSuperComplex<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
TProperty t = htmlHelper.GetValue(expression);
Type type = typeof(TProperty);
MvcHtmlString retVal = new MvcHtmlString("");
var newExpression = expression.Body as NewExpression;
TModel model = htmlHelper.ViewData.Model;
foreach (MemberExpression a in newExpression.Arguments)
{
Type mt = a.Member.GetType();
MvcHtmlString str;
if (ModelUtil.IsSimpleEnough(mt))
{
string value = Convert.ToString(GetPropertyValue<TModel>(model, a));
str = htmlHelper.Hidden(value);
}
else
{
str = htmlHelper.HiddenForComplex<TProperty,mt>(a.Expression); //DOES NOT COMPILE
}
}
return retVal;
}
private static object GetPropertyValue<T>(T instance, MemberExpression me)
{
object target;
if (me.Expression.NodeType == ExpressionType.Parameter)
{
// If the current MemberExpression is at the root object, set that as the target.
target = instance;
}
else
{
target = GetPropertyValue<T>(instance, me.Expression as MemberExpression);
}
// Return the value from current MemberExpression against the current target
return target.GetType().GetProperty(me.Member.Name).GetValue(target, null);
}
public static MvcHtmlString HiddenForComplex<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
TProperty t = htmlHelper.GetValue(expression);
Type type = typeof(TProperty);
MvcHtmlString retVal = new MvcHtmlString("");
foreach (PropertyInfo info in t.GetType().GetProperties())
{
string value = info.GetValue(t).ToString();
//string value = (string)type.GetProperty(info.ToString()).GetValue(t, null);
MvcHtmlString str = htmlHelper.Hidden(value);
retVal = Concat(retVal, str);
}
return retVal;
}
I'm trying to pass data via a ViewDataDictionary to a template-file in an extension-method for HtmlHelper<TModel>
var vdd = new ViewDataDictionary(helper.ViewData);
vdd["someValue"] = true;
return helper.EditorFor(expression, "_MyTemplate", vdd);
In my _MyTemplate.cshtml I try to access it
#{
var myViewDataValue = ViewData["someValue"];
}
which is always null as my "someValue" is located under ViewData.Values where I cannot access it via it's name.
What am I missing here?
Using it directly in my view
ViewData["someValue"] = true;
#Html.EditorFor(m => m.Start, "_MyTemplate")
is working. The same inside the extension-method fails as of in the _MyTemplate the ViewData has no "someValue" at all.
public static MvcHtmlString MyExtension<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
helper.ViewData["someValue"] = true;
return helper.EditorFor(expression, "_MyTemplate", helper.ViewData);
}
throws an exception:
An item with the same key has already been added.
Changing
public static MvcHtmlString MyExtension<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
helper.ViewData["someValue"] = true;
return helper.EditorFor(expression, "_MyTemplate");
}
to
public static MvcHtmlString MyExtension<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
helper.ViewContext.ViewData["someValue"] = true;
return helper.EditorFor(expression, "_MyTemplate");
}
I thought HtmlHelper.ViewData and HtmlHelper.ViewContext.ViewData are the same if not overridden somehow.
Can anyone explain this behavior?
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);
}
}
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);
}
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));
}