Question

How can I provide some form of Attribute against my Business Objects in my ASP.Net MVC application so that I can pick up the help texts in the View and have them appear as pop-up hover help texts like this:

<%= Html.TextBox("PostalCode", Model.PostalCode, new { 
    title = "Please enter your postal code." })%>

I want to be able to get the 'title' value from my ViewModel which will have picked up the text from an attribute on the Business Object.

In summary, how can I apply help text attributes / metadata on my Business Objects?

Was it helpful?

Solution

Here is how I did it:

  1. Created new Attribute as follows:

    public class HelpPromptAttribute : Attribute
    {
      public HelpPromptAttribute(string text)
      {
          myproperty = text; 
      }
      protected string myproperty;
    
      public string HelpTextKey
      {
          get { return myproperty; }
          set { myproperty = value; }
      }
    }
    
  2. Added attribute to entity property as follows:

    [HelpPrompt("ProjectDescription")]
    [Required(ErrorMessageResourceName = "ProjectDescriptionRequired", ErrorMessageResourceType = typeof(EntityValidationMessages))]
    [StringLength(50, ErrorMessageResourceName = "ProjectDescriptionTooLong", ErrorMessageResourceType = typeof(EntityValidationMessages))]
    public string ProjectDescription { get; set; }
    
  3. Added Extension Method to Entities as follows:

    public static class EntityBOExtensions
    {
      public static string PropertyHelp(this object objectBO, string PropertyName)
      {
          Type type = objectBO.GetType();
    
    
          foreach (PropertyInfo pInfo in type.GetProperties())
          {
              if (string.Compare(pInfo.Name, PropertyName) == 0)
              {
                  foreach (Attribute attr in Attribute.GetCustomAttributes(pInfo))
                  {
                      if (attr.GetType() == typeof(HelpPromptAttribute))
                      {
                          string key = ((HelpPromptAttribute)attr).HelpTextKey;
                          if (!string.IsNullOrEmpty(key))
                              return EntityBOHelp.ResourceManager.GetString(key);
                      }
                  }
              }
          }
          return null;
      }
    }
    
  4. Added a HtmlHelper (simple) as follows:

    public static string LocalisedTextBoxWithHelp(this HtmlHelper helper, string name, object value, string helptext)
    {
        string textbox = helper.TextBox(name, value, new { helpprompt = helptext });
        return textbox;
    }
    
  5. And finally used the folowing markup in the View:

     <%= Html.LocalisedTextBoxWithHelp("project.ProjectDescription", Model.ProjectDescription, Model.PropertyHelp("ProjectDescription"))%>
    

This does the job although it needs refinement. ;)

OTHER TIPS

I don't think there is a way to do it straight out of the box.

What you could do though is create a generic "model value" class that encapsulated that information in it thus keeping your strongly typed view. ie:

ModelValue<string> postalCode = new ModelValue<string>("poscode value", "Please enter your postal code.")

You could then build up your model classes to contain properties of type ModelValue.

Your code above would then look something like:

<%= Html.TextBox("PostalCode", Model.PostalCode.Value, new {     watermark = "Postal Code",     title = Model.PostalCode.Title })%>

The down side to doing this is that I don't think mvc is going to do automatic binding for you, so you'll have to do all the mapping in the view yourself like in this example but also on Post you will have to do manual binding if you aren't already. You would probably also instantiate all your ModelValue properties in the constructor of the model and pull in all the title values from whever they are stored then because you wouldn't rebind them on Post (I am thinking about validation errors here causing the form to be redisplayed).

If you were super keen you would put attributes on your model properties and then somehow have them parsed when you render the page, but I have no idea where you would start if you wanted to go this way.

Update:

You could just create a new class property for each of your object named "Title" or "Hint" and add the appropriate string value to them. Then get that property with MyObject.Title


Interesting question. I would like to see an answer to this using attributes, but here are two methods I can think of:

Add an extension method to your objects This would require alot of repetitive code.

public static string GetTitle(this YourObject obj)
{
     return "Title for object";
}

Html Helper extension method

You would store the object titles in this helper method.

public static string GetObjectTitle(this HtmlHelper html, string type)
{
     switch(type)
     {
          case "Object1":
          return "Title for object 1";
          break;

          case "Object2":
          return "Title for object 2";
          break;

          default:
          return "No titled specified for this object type";
          break;
     }
}

To call this method:

<%= Html.GetObjectTitle(Model.GetType()) %>

Or in your example:

<%= Html.TextBox("PostalCode", Model.PostalCode, new { 
watermark = "Postal Code", 
title = Html.GetObjectTitle(Model.GetType()) })%>

I prefer the 2nd method because you have a place to store all the titles and you would need to write less code.

However, I think adding an attribute to the class and creating a means to get that attribute would work a bit better.

I know this is old, but I faced this problem recently with an ASP.NET MVC3 project and implemented a solution.

1. Create a custom attribute to store the help text

public class HelpTextAttribute : DescriptionAttribute
{
    public HelpTextAttribute(string helpText)
        : base(helpText)
    { }
}

2. Create a HtmlHelper extension method to retrieve the attribute value

public static class HtmlHelperExtensions
{
    public static string HelpTextFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        if (memberExpression == null)
            throw new InvalidOperationException("Expression must be a member expression");

        var attributes = memberExpression.Member.GetCustomAttributes(typeof(HelpTextAttribute), true);
        var attribute = attributes.Length > 0 ? attributes[0] as HelpTextAttribute : null;

        return html.Encode(attribute == null ? string.Empty : attribute.Description);
    }
}

3. Annotate the model property with the HelpText attribute

[HelpText("A level from which to start")]
[Required("You must select a level")]
public int Level { get; set; }

4. Simply use the new HtmlHelper extension method with your view

<div class="editor-help">
    <%: Html.HelpTextFor(model => model.Level) %>
</div>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top