diff --git a/samples/MvcSample.Web/HomeController.cs b/samples/MvcSample.Web/HomeController.cs index ba95f76de5..b6cc868f8c 100644 --- a/samples/MvcSample.Web/HomeController.cs +++ b/samples/MvcSample.Web/HomeController.cs @@ -96,7 +96,8 @@ namespace MvcSample.Web Address = "Dependents address", Alive = false, }, - About = "I am a Software Engineer" + Profession = "Software Engineer", + About = "I like playing Football" }; return user; diff --git a/samples/MvcSample.Web/Models/User.cs b/samples/MvcSample.Web/Models/User.cs index 97189a7af1..1cc9b93066 100644 --- a/samples/MvcSample.Web/Models/User.cs +++ b/samples/MvcSample.Web/Models/User.cs @@ -13,6 +13,8 @@ namespace MvcSample.Web.Models public User Dependent { get; set; } public bool Alive { get; set; } public string Password { get; set; } + [DisplayFormat(ConvertEmptyStringToNull = true, NullDisplayText = "You can explain about your profession")] + public string Profession { get; set; } public string About { get; set; } public string Log { get; set; } } diff --git a/samples/MvcSample.Web/Views/Home/Create.cshtml b/samples/MvcSample.Web/Views/Home/Create.cshtml index f7fdcc49e2..f07cba929a 100644 --- a/samples/MvcSample.Web/Views/Home/Create.cshtml +++ b/samples/MvcSample.Web/Views/Home/Create.cshtml @@ -18,7 +18,7 @@ } else { -

Hello @Model.Name! Happy @(Model.Age)th birthday.

+

Hello @Html.DisplayTextFor(model => model.Name)! Happy @(Model.Age)th birthday.

} @{ @@ -69,10 +69,18 @@ - + - @Html.TextArea("About", "You can explain about your profession, hobbies etc.", 5, 40, htmlAttributes: new { style = "font-weight:bold" }) + + + + + + + + + @Html.TextArea("About", "You can explain about your hobbies, work etc.", 5, 40, htmlAttributes: new { style = "font-weight:bold" }) diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs index e39a0dfc28..13ae3bf9ba 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs @@ -258,6 +258,13 @@ namespace Microsoft.AspNet.Mvc.Rendering return GenerateDisplayName(metadata, expression); } + /// + public HtmlString DisplayText(string name) + { + var metadata = ExpressionMetadataProvider.FromStringExpression(name, ViewData, MetadataProvider); + + return GenerateDisplayText(metadata); + } /// public HtmlString DropDownList(string name, IEnumerable selectList, string optionLabel, @@ -291,6 +298,12 @@ namespace Microsoft.AspNet.Mvc.Rendering htmlAttributes: htmlAttributes); } + /// + public HtmlString Id(string name) + { + return GenerateId(name); + } + /// public HtmlString Label(string expression, string labelText, object htmlAttributes) { @@ -652,6 +665,11 @@ namespace Microsoft.AspNet.Mvc.Rendering return new HtmlString(Encode(resolvedDisplayName)); } + + protected virtual HtmlString GenerateDisplayText(ModelMetadata metadata) + { + return new HtmlString(metadata.SimpleDisplayText); + } protected HtmlString GenerateDropDown(ModelMetadata metadata, string expression, IEnumerable selectList, string optionLabel, object htmlAttributes) @@ -760,6 +778,11 @@ namespace Microsoft.AspNet.Mvc.Rendering htmlAttributes: htmlAttributeDictionary); } + protected virtual HtmlString GenerateId(string expression) + { + return new HtmlString(Encode(ViewData.TemplateInfo.GetFullHtmlFieldName(expression))); + } + protected virtual HtmlString GenerateLabel([NotNull] ModelMetadata metadata, string htmlFieldName, string labelText, diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs index ad32c4e014..8141b33a2e 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs @@ -108,6 +108,12 @@ namespace Microsoft.AspNet.Mvc.Rendering return GenerateDisplayName(metadata, expressionText); } + + /// + public HtmlString DisplayTextFor([NotNull] Expression> expression) + { + return GenerateDisplayText(GetModelMetadata(expression)); + } /// public HtmlString EditorFor( @@ -134,6 +140,12 @@ namespace Microsoft.AspNet.Mvc.Rendering htmlAttributes: htmlAttributes); } + /// + public HtmlString IdFor([NotNull] Expression> expression) + { + return GenerateId(GetExpressionName(expression)); + } + /// public HtmlString LabelFor([NotNull] Expression> expression, string labelText, object htmlAttributes) { diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperNameExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperNameExtensions.cs index 1c58924329..91e30c9f72 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperNameExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperNameExtensions.cs @@ -15,5 +15,15 @@ namespace Microsoft.AspNet.Mvc.Rendering { return htmlHelper.Name(string.Empty); } + + /// + /// Gets the full HTML field id for the current model. + /// + /// The instance that this method extends. + /// An that represents HTML markup. + public static HtmlString IdForModel([NotNull] this IHtmlHelper htmlHelper) + { + return htmlHelper.Id(string.Empty); + } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs index fe18fff9bc..80c37299ff 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs @@ -161,6 +161,16 @@ namespace Microsoft.AspNet.Mvc.Rendering /// HtmlString DisplayName(string expression); + /// Returns the HtmlString corresponding to the property in the model specified by the name. + /// + /// + /// The string which identifies the object for which the HtmlString should be returned. + /// + /// New containing the display text. If the value is null, + /// then it returns the ModelMetadata.NullDisplayText. + /// + HtmlString DisplayText(string name); + /// /// Returns a single-selection HTML {select} element using the specified name of the form field, /// list items, option label, and HTML attributes. @@ -248,6 +258,13 @@ namespace Microsoft.AspNet.Mvc.Rendering /// New containing the rendered HTML. HtmlString Hidden(string name, object value, object htmlAttributes); + /// + /// Gets the Id of the given string. + /// + /// The string which identifies the object for which the Id should be returned. + /// New containing the Id. + HtmlString Id(string name); + /// /// Returns an HTML label element and the property name of the property that is represented by the specified /// expression. diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs index 1aea63244d..5eb8d91fe0 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs @@ -70,6 +70,18 @@ namespace Microsoft.AspNet.Mvc.Rendering HtmlString DisplayNameForInnerType( [NotNull] Expression> expression); + /// + /// Returns the HtmlString corresponding to the expression specified. + /// + /// + /// The expression identifies the object for which the HtmlString should be returned. + /// + /// + /// New containing the display text. If the value is null, + /// then it returns the ModelMetadata.NullDisplayText. + /// + HtmlString DisplayTextFor([NotNull] Expression> expression); + /// /// Returns a single-selection HTML {select} element for the object that is represented /// by the specified expression using the specified list items, option label, and HTML attributes. @@ -125,6 +137,13 @@ namespace Microsoft.AspNet.Mvc.Rendering HtmlString HiddenFor([NotNull] Expression> expression, object htmlAttributes); + /// + /// Gets the Id of the given expression. + /// + /// The expression identifies the object for which the Id should be returned. + /// New containing the Id. + HtmlString IdFor([NotNull] Expression> expression); + /// /// Returns an HTML label element and the property name of the property that is represented by the specified /// expression. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedDataAnnotationsModelMetadata.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedDataAnnotationsModelMetadata.cs index 5c650e4d87..72c3455de2 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedDataAnnotationsModelMetadata.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedDataAnnotationsModelMetadata.cs @@ -31,6 +31,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding : base.ComputeConvertEmptyStringToNull(); } + protected override string ComputeNullDisplayText() + { + return PrototypeCache.DisplayFormat != null + ? PrototypeCache.DisplayFormat.NullDisplayText + : base.ComputeNullDisplayText(); + } + protected override string ComputeDescription() { return PrototypeCache.Display != null diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedModelMetadata.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedModelMetadata.cs index 58ae174f7c..f612caee5b 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedModelMetadata.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/CachedModelMetadata.cs @@ -17,11 +17,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public abstract class CachedModelMetadata : ModelMetadata { private bool _convertEmptyStringToNull; + private string _nullDisplayText; private string _description; private bool _isReadOnly; private bool _isComplexType; private bool _convertEmptyStringToNullComputed; + private bool _nullDisplayTextComputed; private bool _descriptionComputed; private bool _isReadOnlyComputed; private bool _isComplexTypeComputed; @@ -62,6 +64,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding } } + public sealed override string NullDisplayText + { + get + { + if (!_nullDisplayTextComputed) + { + _nullDisplayText = ComputeNullDisplayText(); + _nullDisplayTextComputed = true; + } + return _nullDisplayText; + } + set + { + _nullDisplayText = value; + _nullDisplayTextComputed = true; + } + } + public sealed override string Description { get @@ -118,6 +138,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding return base.ConvertEmptyStringToNull; } + protected virtual string ComputeNullDisplayText() + { + return base.NullDisplayText; + } + protected virtual string ComputeDescription() { return base.Description; diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/ModelMetadata.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/ModelMetadata.cs index 87cad826d5..3a74a7aece 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/ModelMetadata.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/ModelMetadata.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; using Microsoft.AspNet.Mvc.ModelBinding.Internal; namespace Microsoft.AspNet.Mvc.ModelBinding @@ -20,6 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding private int _order = DefaultOrder; private IEnumerable _properties; private Type _realModelType; + private string _simpleDisplayText; public ModelMetadata([NotNull] IModelMetadataProvider provider, Type containerType, @@ -141,6 +144,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding } } + public virtual string SimpleDisplayText + { + get + { + if (_simpleDisplayText == null) + { + _simpleDisplayText = ComputeSimpleDisplayText(); + } + + return _simpleDisplayText; + } + + set { _simpleDisplayText = value; } + } + public virtual string TemplateHint { get; set; } internal EfficientTypePropertyKey CacheKey @@ -177,6 +195,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // return validatorProviders.SelectMany(provider => provider.GetValidators(this, validatorProviders)); //} + protected virtual string ComputeSimpleDisplayText() + { + if (Model == null) + { + return NullDisplayText; + } + + var stringResult = Convert.ToString(Model, CultureInfo.CurrentCulture); + if (stringResult == null) + { + return string.Empty; + } + + if (!stringResult.Equals(Model.GetType().FullName, StringComparison.Ordinal)) + { + return stringResult; + } + + var firstProperty = Properties.FirstOrDefault(); + if (firstProperty == null) + { + return string.Empty; + } + + if (firstProperty.Model == null) + { + return firstProperty.NullDisplayText; + } + + return Convert.ToString(firstProperty.Model, CultureInfo.CurrentCulture); + } + private static EfficientTypePropertyKey CreateCacheKey(Type containerType, Type modelType, string propertyName) { // If metadata is for a property then containerType != null && propertyName != null diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs index b67e8f8da9..2fe92cd57e 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/ModelMetadataTest.cs @@ -25,6 +25,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test // Assert Assert.Equal(typeof(Exception), metadata.ContainerType); Assert.True(metadata.ConvertEmptyStringToNull); + Assert.Null(metadata.NullDisplayText); Assert.Null(metadata.Description); Assert.Equal("model", metadata.Model); Assert.Equal(typeof(string), metadata.ModelType);