I have the following classes:
public class Note
{
public string Text { get; set; }
public RowInfo RowInfo { get; set; }
}
public class RowInfo
{
[DisplayName("Created")]
public DateTime Created { get; set; }
[DisplayName("Modified")]
public DateTime Modified { get; set; }
}
In my view I have the following which creates HTML with the correct name and value:
Html.HiddenFor(model => model.Note.Created)
Now what I am trying to do is to create an extension method that will include the above and that I can call in each view. I have tried doing the following. I think I am on the right track but I don't know how to do the equivalent of "model => model.Note.Created" Can someone give me some advice on how I can do this and what I would need to replace the text inside the parenthesis with. I don't have a model but I can do this some other way so the hidden field will go look at my class to get the correct DisplayName just like it does above?
namespace ST.WebUx.Helpers.Html
{
using System.Web.Mvc;
using System.Web.Mvc.Html
using System.Linq;
public static class StatusExtensions
{
public static MvcHtmlString StatusBox(this HtmlHelper helper, RowInfo RowInfo )
{
return new MvcHtmlString(
"Some things here ... " +
System.Web.Mvc.Html.InputExtensions.Hidden( for created field ) +
System.Web.Mvc.Html.InputExtensions.Hidden( for modified field ) );
}
}
You could write a strongly typed helper taking a λ-expression:
public static class StatusExtensions
{
public static IHtmlString StatusBox<TModel, TProperty>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel, TProperty>> ex
)
{
return new HtmlString(
"Some things here ... " +
helper.HiddenFor(ex));
}
}
and then:
#Html.StatusBox(model => model.RowInfo.Created)
UPDATE:
As requested in the comments section here's a revised version of the helper:
public static class StatusExtensions
{
public static IHtmlString StatusBox<TModel>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel, RowInfo>> ex
)
{
var createdEx =
Expression.Lambda<Func<TModel, DateTime>>(
Expression.Property(ex.Body, "Created"),
ex.Parameters
);
var modifiedEx =
Expression.Lambda<Func<TModel, DateTime>>(
Expression.Property(ex.Body, "Modified"),
ex.Parameters
);
return new HtmlString(
"Some things here ..." +
helper.HiddenFor(createdEx) +
helper.HiddenFor(modifiedEx)
);
}
}
and then:
#Html.StatusBox(model => model.RowInfo)
Needless to say that custom HTML helpers should be used to generate small portions of HTML. Complexity could grow quickly and in this case I would recommend you using an editor template for the RowInfo type.
Related
My model is:
public class DynamicEnum
{
public string Name {get; set;}
public int Value {get; set;}
}
public class ViewModel
{
public DynamicEnum DynamicEnum {get; set;}
}
Public ActionResult MyAction
{
var model = new ViewModel();
model.DynamicEnum = new DynamicEnum(){ Name = "System.DayOfWeek", Value = 2};
return View(model);
}
So in the view I need a HtmlHelper to dynamically generate DropDownListFor like:
#Html.EnumDropDownListFor(m => m.DynamicEnum)
I used MVC 5.2.3, Does any one have any idea?
One way to achieve this would be to use reflection:
public static class HtmlExtensions
{
public static IHtmlString EnumDropDownListFor<TModel>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, DynamicEnum>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var dynamicEnum = (DynamicEnum)metadata.Model;
var enumType = Type.GetType(dynamicEnum.Name, true);
if (!enumType.IsEnum)
{
throw new Exception(dynamicEnum.Name + " doesn't represent a valid enum type");
}
// TODO: You definetely want to cache the values here to avoid the expensive
// reflection call: a ConcurrentDictionary<Type, IList<SelectListItem>> could be used
var enumNames = Enum.GetNames(enumType);
var values = enumNames.Select(x => new SelectListItem
{
Text = x,
Value = ((int)Enum.Parse(enumType, x)).ToString(),
}).ToList();
string name = ExpressionHelper.GetExpressionText(expression) + ".Value";
return html.DropDownList(name, values);
}
}
Remark: The HtmlHelper.EnumDropDownListFor extension method already exists in ASP.NET MVC so make sure that you bring the namespace in which you declared your custom extension method into scope to avoid collisions. Or just use a different method name.
I have a simple tag builder that looks like this:
public static MvcHtmlString Tag(this HtmlHelper helper, string tag, string content)
{
var tagBuilder = new TagBuilder(tag){InnerHtml = content};
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.NormalTag));
}
And, I can use it like this:
#Html.Tag("em", Model.Title)
which produces:
<em>The Title</em>
How can this be written to use a Fluent Syntax so it's use would look like this:
#Html.Tag("em").Content(Model.Title)
You have to define a builder interface and implementation. I hope my example can provide some guidance:
public static class MyHtmlExtensions
{
public static IMyTagBuilder Tag(this HtmlHelper helper, string tag)
{
return new MyTagBuilder(tag);
}
}
Then you define your builder interface and implementation:
public interface IMyTagBuilder : IHtmlString
{
IHtmlString Content(string content);
}
public class MyTagBuilder : IMyTagBuilder
{
private readonly TagBuilder _tagBuilder;
public MyTagBuilder(string tag)
{
_tagBuilder = new TagBuilder(tag);
}
public IHtmlString Content(string content)
{
_tagBuilder.InnerHtml = content;
return this;
}
public string ToHtmlString()
{
return _tagBuilder.ToString(TagRenderMode.NormalTag);
}
}
Since IMyTagBuilder implements IHtmlString, it can be used either with or without calling .Content() afterwards.
A great trick to use when implementing fluent interfaces it to use a IFluentInterface to hide object members (ToString, Equals, GetHashCode and GetType) from IntelliSense, it removes some noise.
EDIT: A great resource for building fluent APIs is Daniel Cazzulino's screencast from building Funq here
I have a model say
public class Contact
{
[Display(Name = "Phone Number", Description = "This is Phone number to contact")]
[Visibility(ShowForDisplay=false)]
public string Phone { get; set; }
[Display(Name = "Mail To Support", Description = "This is Mail for support")]
public string Email { get; set; }
}
Now in Mvc html I m doing at several places like
#Html.DisplayTextFor(x=>x.Phone)
Now I want a attribute based something like this which can manage at model level for turning of this display into the view . Like for eg the #html.DisplayTextFor(x=>x.Phone) should be there but when I do [Visibility(ShowForDisplay=false)] then all the visibility for the values or texts should not be rendered on the html .
How can be done through attribute like custom attribute [Visibility(ShowForDisplay=false)] ?
All Html Helper methods working with Model MetaData, and you can't change ModelMetada class so you should make your own Html helper, and ofcourse you need a custom attribute. Check this code:
First create a custom attribute:
public class VisibilityAttribute : ValidationAttribute
{
private bool _isVisible;
public VisibilityAttribute(bool visible = true)
{
_isVisible = visible;
}
public bool ShowForDisplay
{
get
{
return _isVisible;
}
set
{
_isVisible = value;
}
}
}
Then create a Html helper:
public static class MyHtmlExtensions
{
public static MvcHtmlString DisplayTextForCustom<TModel, TResult>(this HtmlHelper<TModel> html,
Expression<Func<TModel, TResult>> expression)
{
ExpressionType type = expression.Body.NodeType;
if (type == ExpressionType.MemberAccess)
{
MemberExpression memberExpression = (MemberExpression) expression.Body;
PropertyInfo pi = memberExpression.Member as PropertyInfo;
var attributes = pi.GetCustomAttributes();
foreach (var attribute in attributes)
{
if (attribute is VisibilityAttribute)
{
VisibilityAttribute vi = attribute as VisibilityAttribute;
if (vi.ShowForDisplay)
{
var metadata = ModelMetadata.FromLambdaExpression<TModel, TResult>(expression, html.ViewData);
return MvcHtmlString.Create(metadata.SimpleDisplayText);
}
}
}
}
return MvcHtmlString.Create("");
}
}
Then call it from your View like this:
#Html.DisplayTextForCustom(x=>x.Phone)
PS: To write this code I looked at Html.DisplayTextFor source code and I try to write a code as simple as possible.
I have in my Model:
[Display(Name = "Check to enter <break> the Quantity of items")]
public bool IsLimitedQuantity { get; set; }
and I am using
#Html.LabelFor(shop => shop.IsLimitedQuantity)
in my view.
Please suggest how I can fix this, because the label is just showing <break> as it is, instead of breaking to a new line.
You could write a custom LabelFor helper which doesn't HTML encode the text as does the standard LabelFor helper:
public static class LabelExtensions
{
public static IHtmlString UnencodedLabelFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
var text = (metadata.DisplayName ?? (metadata.PropertyName ?? htmlFieldName.Split(new char[] { '.' }).Last<string>()));
if (string.IsNullOrEmpty(text))
{
return MvcHtmlString.Empty;
}
var tagBuilder = new TagBuilder("label");
tagBuilder.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
tagBuilder.InnerHtml = text;
return new HtmlString(tagBuilder.ToString(TagRenderMode.Normal));
}
}
and then use this custom helper in the view:
#Html.UnencodedLabelFor(x => x.IsLimitedQuantity)
Now the HTML tags in the display name will be rendered without encoding:
[Display(Name = "Check to enter <br/> the Quantity of items")]
public bool IsLimitedQuantity { get; set; }
Using Html decode may help. MVC for security reasons encode all the values.
How to display HTML stored in a database from an ASP.NET MVC view?
Can somebody please suggest how I could write a Unit Test with Moq for following HtmlHelper method?
public static HtmlTagBase GenerateTag<T>(this HtmlHelper htmlHelper
, object elementData
, object attributes)
where T : HtmlTagBase
{
return (T)Activator.CreateInstance(typeof(T)
, htmlHelper.ViewContext
, elementData
, attributes);
}
which you would use as follows (please note the using statement - this is causing me confusion):
<%--Model is a type of ShareClass--%>
<% using (Html.GenerateTag<DivTag>(Model)) { %>
My Div
<% } %>
using this method, if you specify T as type DivTag, where ShareClass is defined as
public class ShareClass
{
public string Name { get; set; }
public string Type { get; set; }
public IEnumerable<Fund> Funds { get; set; }
public ShareClass(string name, string shareClassType)
{
this.Name = name;
this.Type = shareClassType;
}
}
the following html will be rendered:
<div class="ShareClass" shareclass-type="ShareClass_A" shareclass-name="MyShareClass">My Div</div>
I think this 3 links will help
Prepare Html Helper https://stackoverflow.com/a/747186/980003
Create test method https://stackoverflow.com/a/3424771/980003 (but use HtmlHelper from first link)
Assert result with you expectations as normal.
Here is example of disposable helper. How it looks like inside. https://stackoverflow.com/a/8292524/980003