Orchard Hungary is an archive

This site is now an archive. We'll keep it so the content is accessible, but we won't update it anymore. However, we frequently publish Orchard-related news on our other site Orchard Dojo, so be sure to check that out instead. And if you're interested in news about our Orchard development company, Lombiq, check out our website.

Adding a custom model binder to Orchard

Tags: Orchard, model binder

An undocumented extensiblity point of Orchard is that you can add custom MVC model binders in an Orchard-y way, with providers. We needed this for adding the ability of parsing floats with a decimal dot and comma equally well, and it works as following.

public class FloatModelBinderProvider : Orchard.Mvc.ModelBinders.IModelBinderProvider
{
    public IEnumerable<Orchard.Mvc.ModelBinders.ModelBinderDescriptor> GetModelBinders()
    {
        return new[]
        {
            new Orchard.Mvc.ModelBinders.ModelBinderDescriptor
            {
                Type = typeof(float),
                ModelBinder = new DecimalModelBinder()
            }
        };
    }


    // Mainly taken from http://stackoverflow.com/a/5117441/220230
    private class DecimalModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            object result = null;

            var modelName = bindingContext.ModelName;
            var attemptedValue = bindingContext.ValueProvider.GetValue(modelName).AttemptedValue;

            // Depending on cultureinfo the NumberDecimalSeparator can be "," or "."
            // Both "." and "," should be accepted, but aren't.
            var wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
            var alternateSeperator = (wantedSeperator == "," ? "." : ",");

            if (attemptedValue.IndexOf(wantedSeperator) == -1
                && attemptedValue.IndexOf(alternateSeperator) != -1)
            {
                attemptedValue = attemptedValue.Replace(alternateSeperator, wantedSeperator);
            }

            try
            {
                if (bindingContext.ModelMetadata.IsNullableValueType && string.IsNullOrWhiteSpace(attemptedValue))
                    return null;

                result = float.Parse(attemptedValue, NumberStyles.Any);
            }
            catch (FormatException e)
            {
                bindingContext.ModelState.AddModelError(modelName, e);
            }

            return result;
        }
    }
}

As you can see you only have to implement the pretty simple IModelBinderProvider interface and return instances of your custom model binders. Note that IModelBinderProvider comes from Orchard, not from under the System namespace (there is one there too).

Happy model binding!

2 Comments

  • Oliver said Reply

    Funny, just the other day I exchanged our home-grown ModelBinder hookup code with this useful Orchard extension point :-)

    Btw, the single & inside the try block should be &&, I guess.

  • Piedone said Reply

    Thanks for the observation, you're right, fixed.