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