【WPF】數據驗證


引言

     數據驗證在任何用戶界面程序中都是不可缺少的一部分.在WPF中,數據驗證更是和綁定緊緊聯系在一起,下面簡單介紹MVVM模式下常用的幾種驗證方式.

錯誤信息顯示

     在介紹數據驗證之前,有必要介紹一下如何顯示錯誤信息.方式很簡單,定義一個樣式觸發器,將錯誤信息和 ToolTip綁定,如下:

           <Style TargetType="TextBox">
                <Style.Triggers>
                    <Trigger Property="Validation.HasError" Value="true">
                        <Setter Property="ToolTip"
                            Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
                    </Trigger>
                </Style.Triggers>
            </Style>

 

 ValidatesOnExceptions驗證規則

      ValidatesOnExceptions是WPF預定義的驗證規則,它會捕捉任何位置上的異常,包括類型轉換異常,屬性設置器異常,值轉換器異常等.捕捉到異常的時候,輸入的邊框會變成紅色,當然也可以自定義錯誤的模板(Validation.ErrorTemplate).想要ValidatesOnExceptions生效,將綁定屬性中的ValidatesOnExceptions設置為true即可.

PS:無論設置為true或false,類型轉換異常總會發生的,也就是總會有紅色框. 

數據對象中驗證

    直接在數據對象中編寫驗證規則是最簡單粗暴的方式,如下

        public int Price
        {

            get { return _price; }
            set
            {
                if (Equals(value, _price)) return;

                if (value < 0)
                {

                    throw new ArgumentException("數值不能小於0");

                }
                else
                {
                    _price = value;
                    RaisePropertyChanged(() => Price);
                }
            }
        }

 

如果小於0,程序不會拋錯,文字提示也會顯示在ToolTip上,前提是ValidatesOnExceptions=true.

PS:這種方式能如期實現,是因為WPF的Binding 捕捉屬性設置中的所有異常.但是,如果是代碼設置負數的話,程序直接掛掉.

自定義驗證規則

       除了WPF預定義的驗證規則外,我們還可以自定義驗證規則,要繼承ValidationRule,編寫驗證不能大於99的數值,代碼如下:

    public class NumberRule : ValidationRule
    {
        public override System.Windows.Controls.ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            int i;
            //  int.TryParse(value.ToString(), out i);
            if (!int.TryParse(value.ToString(), out i))
            {
                return new System.Windows.Controls.ValidationResult(false,
                       "字符串格式不對!");
            }
            if (i > 99)
            {
                return new System.Windows.Controls.ValidationResult(false,
                        "數值不能大於99!");
            }
            else
            {
                return new System.Windows.Controls.ValidationResult(true, null);
            }

        }
    }
       <TextBox  Height="25" Width="100" Margin="208,142,0,0"  VerticalAlignment="Top" HorizontalAlignment="Left" >
            <TextBox.Text>
                <Binding Path="Price" Mode="TwoWay" ValidatesOnDataErrors="True">  
                    <Binding.ValidationRules>
                        <ExceptionValidationRule></ExceptionValidationRule>
                        <local:NumberRule></local:NumberRule>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>           
        </TextBox>

 

PS:自定義驗證規則總是在ExceptionValidationRule之前進行,所以要在NumberRule加上轉換類型的異常處理,不然會有拋錯的可能性,當然上面的代碼中如果有類型錯誤變量i總會返回0.

PS:驗證的執行順序:自定義驗證規則->值轉換器->ExceptionValidationRule->數據對象驗證.

 ValidatesOnDataErrors驗證規則

     正常情況下,上面的幾種方式都可以工作得很好,但是屬性多達幾十個的時候,寫起來就不是那么的舒服了.這個時候我們可以通過繼承接口IDataErrorInfo來將我們的驗證規則統一起來,代碼如下:

        public string Error
        { get { return ""; } } public string this[string propertyname] { get { string result = null; if (propertyname == "Price") { if (Price >99) { result = "數值不能大於99!!"; } } return result; } }
Error在WPF中沒作用,返回任意都可以.
PS:記得把ValidatesOnDataErrors=true
PS:驗證的執行順序:自定義驗證規則->值轉換器->ExceptionValidationRule->數據對象驗證->ValidatesOnDataErrors.

進階 ValidatesOnDataErrors驗證規則
ValidatesOnDataErrors雖然能統一起來到一個地方,但是還免不了每一個屬性單獨寫一個規則.所以我們需要一個更簡便的方式,那就是DataAnnotations+IDataErrorInfo的方式,代碼如下:
        private int _price;
        [Range(0, 99, ErrorMessage = "數值要在0到99之間")]
        public int Price
        {

            get { return _price; }
            set
            {
                if (Equals(value, _price)) return;

                if (value < 0)
                {

                    throw new ArgumentException("數值不能小於0");

                }
                else
                {
                    _price = value;
                    RaisePropertyChanged(() => Price);
                }
            }
        }

        public string this[string propertyname]
        {
            get
            {
                var vc = new ValidationContext(this, null, null);
                vc.MemberName = propertyname;
                var res = new List<System.ComponentModel.DataAnnotations.ValidationResult>();

                var result = Validator.TryValidateProperty(this.GetType().GetProperty(propertyname).GetValue(this, null), vc, res);
                if (res.Count > 0)
                {
                    return string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
                }
                return string.Empty;
            }
        }

 

 
        

 采用這種方式,開發的時候只需要簡單的設置一下特性,就能如期望的顯示我們的驗證提示了,關於這種方式的詳細用法,網上有一篇更好的文章:傳送門.

 注意事項

       到這里,如果沒有什么意外,相信大家都會采用DataAnnotations+IDataErrorInfo的方式,這種方式實現最簡單,而且發生在viewmodel上,我們很容易地在保存環節得到所有異常信息,從而阻止保存數據的進行.但是WPF的數據驗證中都有個通病,就是發生數據異常的時候,屬性實際的值還是上次合法的值,和界面上顯示的值有所不同.這個時候如果用戶強行保存,我們就發現DataAnnotations+IDataErrorInfo的驗證方式竟然通過了!這不符合我們的期望.這種情況我沒發現有什么優雅的解決方案,暫時想到的只有在按鈕的點擊事件中遍歷LogicalTreeHelper的輸入控件,檢查Validation.HasError屬性,組合異常信息傳給viewmodel,讓viewmodel作出處理.其實最為徹底的方式是,封裝數字輸入控件等各類特定的控件,提高用戶體驗的同時,也讓異常處理更簡單.

小結

    本文簡單介紹了WPF數據驗證的各種方式,而我們基本上都會采用 DataAnnotations+IDataErrorInfo的方式,如果您有更好的方式,請不吝指教,感激不盡!

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM