2009-12-16 82 views
9

我有一堆表單,其中輸入貨幣值,我希望它們能夠輸入「$ 1,234.56」。默認情況下,模型聯編程序不會將其解析爲小數。如何使用自定義模型過濾器過濾表單數據

我在做的是創建一個自定義模型綁定器繼承DefaultModelBinder,重寫BindProperty方法,檢查屬性描述符類型是否是十進制的,如果是,只需從值中刪除$和。

這是最好的方法嗎?

代碼:

public class CustomModelBinder : DefaultModelBinder 
{ 
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
{ 
    if(propertyDescriptor.PropertyType == typeof(decimal) || propertyDescriptor.PropertyType == typeof(decimal?)) 
    { 
    var newValue = Regex.Replace(bindingContext.ValueProvider[propertyDescriptor.Name].AttemptedValue, @"[$,]", "", RegexOptions.Compiled); 
    bindingContext.ValueProvider[propertyDescriptor.Name] = new ValueProviderResult(newValue, newValue, bindingContext.ValueProvider[propertyDescriptor.Name].Culture); 
    } 

    base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
} 
} 

更新

這是我落得這樣做:

public class CustomModelBinder : DataAnnotationsModelBinder 
{ 
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
    { 
     if(propertyDescriptor.PropertyType == typeof(decimal) || propertyDescriptor.PropertyType == typeof(decimal?)) 
     { 
      decimal newValue; 
      decimal.TryParse(bindingContext.ValueProvider[propertyDescriptor.Name].AttemptedValue, NumberStyles.Currency, null, out newValue); 
      bindingContext.ValueProvider[propertyDescriptor.Name] = new ValueProviderResult(newValue, newValue.ToString(), bindingContext.ValueProvider[propertyDescriptor.Name].Culture); 
     } 
     base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
    } 
} 
+1

看一看這個職位從Haacked:http://haacked.com/archive/2011/03/ 19/fix-binding-to-decimals.aspx – VinnyG 2011-04-22 15:51:08

回答

5

這是合理的,這樣做的粘合劑。不過,我認爲Decimal.Parse與貨幣格式提供商或數字樣式(see the docs)將比剝離「$」和調用base更可靠。對於初學者來說,它將處理非美元貨幣,這對您來說可能是一個問題。

+1

哇。不知道Decimal.Parse的格式會默認接受。那麼你會認爲模型粘合劑會默認接受這種方法。事實上,在這個例子http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-39-cs.aspx他們甚至有一個十進制類型與正則表達式驗證,說一個$是好的...如果我有一個小數類型,價值$ 1,234或$ 1234來作爲0,並且模型狀態是無效的。 – 2009-12-16 22:41:50

+0

我查看了MVC源代碼和DataAnnotationsModelBinder源代碼,我認爲最好是調用base。有很多事情正在進行,包括處理錯誤和模型狀態。我認爲我在做之前的工作,但使用decimal.Parse函數可以很好地工作。 – 2009-12-17 18:35:30

1

您可以創建自己的ValidationAttribute,它檢查值是否具有正確的格式。然後你可以看看屬性是否用這個屬性裝飾並以適當的方式綁定它。屬性不需要是ValidationAttibute,但它似乎是個好主意。

5

在MVC3中,您可以註冊一個自定義的modelbinder,專門爲小數類型實現IModelBinder接口,然後通過在bindingContext上使用ModelMetaData.DataTypeName屬性來告訴它處理貨幣或小數。

我修改由Phil Haack in his article提供的示例演示如何可以做到:

public class DecimalModelBinder : IModelBinder 
    { 

     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
      var modelState = new ModelState { Value = valueResult }; 

      decimal actualValue = 0; 
      try 
      { 

       if(bindingContext.ModelMetadata.DataTypeName == DataType.Currency.ToString()) 
        decimal.TryParse(valueResult.AttemptedValue, NumberStyles.Currency, null, out actualValue); 
       else 
        actualValue = Convert.ToDecimal(valueResult.AttemptedValue,CultureInfo.CurrentCulture); 


      } 
      catch (FormatException e) 
      { 
       modelState.Errors.Add(e); 
      } 

      bindingContext.ModelState.Add(bindingContext.ModelName, modelState); 
      return actualValue; 
     } 
    }