Click here to Skip to main content
15,884,473 members
Articles / .NET

A Strongly-Typed Range Input for .NET and MVC 2

Rate me:
Please Sign up or sign in to vote.
1.00/5 (1 vote)
5 Jan 2012CPOL1 min read 7K   2   1
A strongly-typed range input for .NET and MVC 2

The jquery TOOLS library has a nice API for creating HTML5 range inputs in older browsers. And the MVC 2 framework for ASP.NET offers a nice way of creating interactive web applications for Windows servers. The logical thing to do, then, is combine them somehow.

First, we'll create a custom .NET attribute called StepSize that we can attach to a model property. This attribute will be used to determine the step attribute of our range input. We'll use the existing .NET Range property attribute to determine the min and max values of our range input.

Next, we need some HtmlHelper extension methods to create the range input on our views. We'll create a strongly-typed version of this method and use reflection to traverse the model's property for which we're building the range input. These methods return an MvcHtmlString which means all the contents have been encoded and are ready to be displayed.

Once all that compiles, we can simply write the following line in our view to get a nice range input (which can be styled with CSS.)

HTML
<%= Html.RangeInputFor(model => model.SomeProperty) %>

Of course, you'll need to make sure your jquery TOOLS functions are set up correctly. I'm just using this simple call in my $(document).ready() function:

HTML
$(":range").rangeinput();

As always, check the code before you use it. I don't guarantee that these methods won't allow Satan himself to spawn in your web page and ruin the internet.

Range Input Example

C#
namespace YourNamespace
{
    [AttributeUsage(
        AttributeTargets.Property,
        AllowMultiple = false
    )]
    public class StepSizeAttribute : Attribute
    {
        public object StepSize { get; private set; }
 
        private StepSizeAttribute(object stepSize)
        {
            this.StepSize = stepSize;
        }
 
        public StepSizeAttribute(int stepSize)
            : this((object)stepSize) { }
 
        public StepSizeAttribute(double stepSize)
            : this((object)stepSize) { }
 
        public StepSizeAttribute(float stepSize)
            : this((object)stepSize) { }
 
        public StepSizeAttribute(decimal stepSize)
            : this((object)stepSize) { }
    }
}
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
 
namespace YourNamespace
{
    /// <summary>
    /// HtmlHelper extension methods for working with models in MVC2 views.
    /// </summary>
    public static partial class HtmlHelperExt
    {
        /// <summary>
        /// Creates a range input that will display correctly on browsers that implement HTML5 or
        /// upon inclusion of the jquery TOOLS library.
        /// </summary>
        /// <param name="htmlHelper">
        /// The HtmlHelper object that this method extends.</param>
        /// <param name="name">The name of the range input. 
        /// This will also be used for the id of the input.
        /// </param>
        /// <param name="min">The minimum value of the range input.</param>
        /// <param name="max">The maximum value of the range input.</param>
        /// <param name="value">The default value of the range input.</param>
        /// <param name="step">The step size of the range input.</param>
        /// <param name="htmlAttributes">
        /// Additional attributes that should be included in the range input (i.e. class, etc.).
        /// </param>
        /// <returns>An encoded range input string.</returns>
        public static MvcHtmlString RangeInput(this HtmlHelper htmlHelper, 
        string name, double min, double max, double value, double step, object htmlAttributes)
        {
            var range = new TagBuilder("input");
            range.Attributes.Add("type", "range");
            range.Attributes.Add("min", htmlHelper.Encode(min));
            range.Attributes.Add("max", htmlHelper.Encode(max));
            range.Attributes.Add("value", htmlHelper.Encode(value));
            range.Attributes.Add("step", htmlHelper.Encode(step));
            range.Attributes.Add("id", htmlHelper.Encode(name));
            range.Attributes.Add("name", htmlHelper.Encode(name));
            if (htmlAttributes != null)
                range.MergeAttributes((IDictionary<string, object>)htmlAttributes, true);
            return MvcHtmlString.Create(range.ToString(TagRenderMode.SelfClosing));
        }
 
        /// <summary>
        /// Creates a range input that will display correctly on browsers that implement HTML5 or
        /// upon inclusion of the jquery TOOLS library.
        /// </summary>
        /// <typeparam name="TModel">
        /// The model type for which to create the range input.</typeparam>
        /// <typeparam name="TProperty">
        /// The property type for which to create the range input.</typeparam>
        /// <param name="htmlHelper">
        /// The HtmlHelper object that this method extends.</param>
        /// <param name="expression">
        /// The LINQ expression that identifies the model and property 
        /// for which to create this range input.</param>
        /// <returns>An encoded range input string.</returns>
        public static MvcHtmlString RangeInputFor<TModel, 
        TProperty>(this HtmlHelper<TModel> htmlHelper, 
        Expression<Func<TModel, TProperty>> expression)
            where TModel : class
        {
            return RangeInputFor(htmlHelper, expression, null);
        }
 
        /// <summary>
        /// Creates a range input that will display correctly on browsers that implement HTML5 or
        /// upon inclusion of the jquery TOOLS library.
        /// </summary>
        /// <typeparam name="TModel">
        /// The model type for which to create the range input.</typeparam>
        /// <typeparam name="TProperty">
        /// The property type for which to create the range input.</typeparam>
        /// <param name="htmlHelper">
        /// The HtmlHelper object that this method extends.</param>
        /// <param name="expression">
        /// The LINQ expression that identifies the model and property 
        /// for which to create this range input.</param>
        /// <param name="htmlAttributes">
        /// Additional attributes that should be included in the range input (i.e. class, etc.).
        /// </param>
        /// <returns>An encoded range input string.</returns>
        public static MvcHtmlString RangeInputFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, 
        Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
            where TModel : class
        {
            // use the expression to get a Range attribute from the property for which
            // we're creating this range input.
            var mexp = expression.Body as MemberExpression;
            var range = mexp.Member.GetCustomAttributes(typeof(RangeAttribute), false)
                .OfType<RangeAttribute>().FirstOrDefault();
 
            // if no range was found, then we're not creating this input; i.e. you need
            // a range attribute for this method.
            if (range == null)
                throw new ArgumentException("The property " + 
                mexp.Member.Name + " does not have a range attribute.");
 
            // we need numeric values for the min and max values. Try to get them here.
            var min = 0m;
            var max = 0m;
            if (!Decimal.TryParse(range.Minimum.ToString(), out min))
                throw new InvalidCastException("The minimum value of the 
                range attribute (" + range.Minimum.ToString() + ") for " + 
                mexp.Member.Name + " cannot be converted to numeric.");
            if (!Decimal.TryParse(range.Maximum.ToString(), out max))
                throw new InvalidCastException("The maximum value 
                of the range attribute (" + range.Maximum.ToString() + ") 
                for " + mexp.Member.Name + " cannot be converted to numeric.");
 
            // we need the value of the property for this model so we can default the
            // range input to this position. This value needs to be numeric, too.
            var value = 0m;
            var strValue = typeof(TModel).GetProperty(mexp.Member.Name).GetValue
            (htmlHelper.ViewData.Model, null).ToString();
            if (!Decimal.TryParse(strValue, out value))
                throw new InvalidCastException("The value of " + 
                mexp.Member.Name + " cannot be converted to numeric.");
 
            // now, get the step size attribute. This is a custom attribute defined in this
            // namespace, and the value will default to 1 if the attribute isn't present.
            var step = 1m;
            var stepSize = mexp.Member.GetCustomAttributes(typeof(StepSizeAttribute), false)
                .OfType<StepSizeAttribute>().FirstOrDefault();
            if (stepSize != null)
                if (!Decimal.TryParse(stepSize.StepSize.ToString(), out step))
                    throw new InvalidCastException("The value of the step size 
                    (" + stepSize.StepSize.ToString() + ") 
                    cannot be converted to numeric.");
 
            // we should have everything we need, so create the HTML range input and return.
            var rangeInput = htmlHelper.RangeInput(
               mexp.Member.Name,
                (double)min,
                (double)max,
                (double)value,
                (double)step,
                htmlAttributes                
            );
            return rangeInput;
        }
    }
}

This article was originally posted at http://kendoll.net/strongly_typed_range_input_for_mvc_2

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 1 Pin
Selvin5-Jan-12 13:04
Selvin5-Jan-12 13:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.