Я хотел бы создать модель обязательная функциональность, таким образом, пользователь может войти'','.' и т.д. для значений валюты, которые связывают с двойным значением моего ViewModel.
Я смог сделать это в MVC 1.0 путем создания пользовательского образцового редактора связей, однако начиная с обновления до MVC 2.0, эта функциональность больше не работает.
У кого-либо есть какие-либо идеи или лучшие решения для выполнения этой функциональности? Лучшее решение состояло бы в том, чтобы использовать некоторое аннотирование данных или пользовательский атрибут.
public class MyViewModel
{
public double MyCurrencyValue { get; set; }
}
Предпочтительное решение было бы чем-то вроде этого...
public class MyViewModel
{
[CurrencyAttribute]
public double MyCurrencyValue { get; set; }
}
Ниже мое решение для привязки модели в MVC 1.0.
public class MyCustomModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object result = null;
ValueProviderResult valueResult;
bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName, out valueResult);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueResult);
if (bindingContext.ModelType == typeof(double))
{
string modelName = bindingContext.ModelName;
string attemptedValue = bindingContext.ValueProvider[modelName].AttemptedValue;
string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
string alternateSeperator = (wantedSeperator == "," ? "." : ",");
try
{
result = double.Parse(attemptedValue, NumberStyles.Any);
}
catch (FormatException e)
{
bindingContext.ModelState.AddModelError(modelName, e);
}
}
else
{
result = base.BindModel(controllerContext, bindingContext);
}
return result;
}
}
Вы можете попробовать что-нибудь из следующих строк:
// Just a marker attribute
public class CurrencyAttribute : Attribute
{
}
public class MyViewModel
{
[Currency]
public double MyCurrencyValue { get; set; }
}
public class CurrencyBinder : DefaultModelBinder
{
protected override object GetPropertyValue(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
var currencyAttribute = propertyDescriptor.Attributes[typeof(CurrencyAttribute)];
// Check if the property has the marker attribute
if (currencyAttribute != null)
{
// TODO: improve this to handle prefixes:
var attemptedValue = bindingContext.ValueProvider
.GetValue(propertyDescriptor.Name).AttemptedValue;
return SomeMagicMethodThatParsesTheAttemptedValue(attemtedValue);
}
return base.GetPropertyValue(
controllerContext,
bindingContext, propertyDescriptor,
propertyBinder
);
}
}
public class HomeController: Controller
{
[HttpPost]
public ActionResult Index([ModelBinder(typeof(CurrencyBinder))] MyViewModel model)
{
return View();
}
}
ОБНОВЛЕНИЕ:
Вот усовершенствование связующего ( см. раздел TODO
в предыдущем коде):
if (!string.IsNullOrEmpty(bindingContext.ModelName))
{
var attemptedValue = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName).AttemptedValue;
return SomeMagicMethodThatParsesTheAttemptedValue(attemtedValue);
}
Для обработки коллекций вам необходимо зарегистрировать связыватель в Application_Start
, поскольку вы больше не сможете украсить список ModelBinderAttribute
:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(MyViewModel), new CurrencyBinder());
}
И тогда ваше действие может выглядеть следующим образом:
[HttpPost]
public ActionResult Index(IList<MyViewModel> model)
{
return View();
}
Резюмируя важную часть:
bindingContext.ValueProvider.GetValue(bindingContext.ModelName)
Дальнейшим шагом улучшения этого связывателя будет обработка проверки (AddModelError / SetModelValue)