Creating A Custom MVC Web Helper

Creating a custom MVC Web Helper and globalization

In this write-up, I will explain how to create a custom HTML Web Helper and how to use globalization. We will have a combo box binding its options from an Enum, which will have the texts loaded according to the user's language preferences.

Why would I need to create a custom web helper?

HTML helper is a method that returns an HTML string. Then, this string is rendered in the View. MVC provides many built-in HTML helper methods and it also provides the facility to create your own HTML helper methods. Once you create your helper method, you can reuse it many times.

Creating a custom web helper gives you the flexibility to create your own personalized component, which could help regarding code quality, performance, and organization. It is easy to use and for further code maintenance.

Why would I need to use globalization?

Globalization is the process of designing the application in such a way that it can be used by users from across the globe (multiple cultures).

So, you may display texts in different languages, according to the user language preferences or select one to display as default if none of his or her preferences match with the available languages.

Steps to reproduce,
  1. Create a folder for the HTML Helper.
  2. Create a new static class which will contain the HTML helper code.
  1. public static class WebHelperSampleClass  
  2.     {  
  3.         private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = "", Value = "" } };  
  4.   
  5.         /// <summary>  
  6.         /// HTML helper to be used to load the enums with its translations  
  7.         /// </summary>  
  8.         /// <param name="firstLineCombo">the data to appear in the first line of the combo</param>  
  9.         /// <returns>A combo with the translations of the enums</returns>  
  10.         public static MvcHtmlString DisplayEnum<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, string firstLineCombo)  
  11.         {  
  12.             var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);  
  13.             var enumType = GetNonNullableModelType(metadata);  
  14.             IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();  
  15.   
  16.             IEnumerable<SelectListItem> items = new[] { new SelectListItem  
  17.             {  
  18.                 Text = firstLineCombo,  
  19.                 Value = "0",  
  20.                 Selected = true  
  21.             }  
  22.             }.Concat(  
  23.                 from value in values  
  24.                 select new SelectListItem  
  25.                 {  
  26.                     Text = GetEnumDescription(value),  
  27.                     Value = value.ToString(),  
  28.                     Selected = false  
  29.                 });  
  30.   
  31.             return htmlHelper.DropDownListFor(expression, items);  
  32.         }  
  33.   
  34.         /// <summary>  
  35.         /// Gets the type of the enum  
  36.         /// </summary>  
  37.         /// <param name="modelMetadata">the model</param>  
  38.         /// <returns>return the type of enum</returns>  
  39.         private static Type GetNonNullableModelType(ModelMetadata modelMetadata)  
  40.         {  
  41.             var realModelType = modelMetadata.ModelType;  
  42.             var underlyingType = Nullable.GetUnderlyingType(realModelType);  
  43.   
  44.             if (underlyingType != null)  
  45.             {  
  46.                 realModelType = underlyingType;  
  47.             }  
  48.   
  49.             return realModelType;  
  50.         }  
  51.   
  52.         /// <summary>  
  53.         /// Gets the description of each enum  
  54.         /// </summary>  
  55.         /// <returns>the description translated of the respective enum</returns>  
  56.         private static string GetEnumDescription<TEnum>(TEnum value)  
  57.         {  
  58.             var fieldInfo = value.GetType().GetField(value.ToString());  
  59.             var attributes = fieldInfo.GetCustomAttributes(typeof(DisplayAttribute), false);  
  60.             var resourceValueName = ((System.ComponentModel.DataAnnotations.DisplayAttribute)(attributes[0])).Description;  
  61.   
  62.             return Resources.ResourceManager.GetString(resourceValueName);  
  63.   
  64.         }  
  65.     }  

Reference the created class in the View and call the custom web HTML Helper.

  1. @using WebHelpers.WebHelperSampleClass;  
  2. @using Globalization_CustomWebHelper.Resource;  
  3. @using Globalization_CustomWebHelper.Models;  
  4. @model SampleModel  
  5.   
  6. @Resources.ComboSelect  
  7. <br />  
  8. @Html.DisplayEnum(m => m.SampleComboWithEnums, Resources.HeardAboutUsLabel) 

Steps to configure the Globalization files

  1. Create a folder for the resource files.
  2. Create a resource file, which will be the default one and set its access modifier to Public.
  3. Create another one for each additional language in the same folder, always following the same pattern: [Name].[Language Abbreviation].resx. Example: Resources.en.resx (english)
  4. Include the following code in the web.config so it checks the user language and select the value from the correct Resource file:
  1. <globalization enableClientBasedCulture="true" culture="auto" uiCulture="auto" />  

Use the resource's name defined in the resource files.

Enum
  1. public enum WebHelperSampleEnum   
  2. {  
  3.     [Display(ResourceType = typeof(Resources), Description = "ComboAdvert")]  
  4.     Advert = 1, [Display(ResourceType = typeof(Resources), Description = "ComboWordOfMouth")]  
  5.     WordOfMouth = 2, [Display(ResourceType = typeof(Resources), Description = "ComboOther")]  
  6.     Other = 3  
  7. }  

Calling from anywhere is easy [file name].[Setting name]. Example: Resources.Sitename