2012-11-14 34 views
3

正如您可能從標題中看到的那樣,我正要問以前曾被問過很多次的問題。但是,在閱讀所有這些其他問題後,我仍然無法找到解決我的問題的體面解決方案。驗證失敗時禁用保存按鈕

我有一個基本的驗證模型類:

partial class Player : IDataErrorInfo 
{ 
    public bool CanSave { get; set; } 

    public string this[string columnName] 
    { 
     get 
     { 
      string result = null; 
      if (columnName == "Firstname") 
      { 
       if (String.IsNullOrWhiteSpace(Firstname)) 
       { 
        result = "Geef een voornaam in"; 
       } 
      } 
      if (columnName == "Lastname") 
      { 
       if (String.IsNullOrWhiteSpace(Lastname)) 
       { 
        result = "Geef een familienaam in"; 
       } 
      } 
      if (columnName == "Email") 
      { 
       try 
       { 
        MailAddress email = new MailAddress(Email); 
       } 
       catch (FormatException) 
       { 
        result = "Geef een geldig e-mailadres in"; 
       } 
      } 
      if (columnName == "Birthdate") 
      { 
       if (Birthdate.Value.Date >= DateTime.Now.Date) 
       { 
        result = "Geef een geldige geboortedatum in"; 
       } 
      } 

      CanSave = true; // this line is wrong 
      return result; 
     } 
    } 

    public string Error { get { throw new NotImplementedException();} } 
} 

此驗證每次完成屬性的變化(所以每次用戶鍵入的文本字符):

<TextBox Text="{Binding CurrentPlayer.Firstname, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="137" IsEnabled="{Binding Editing}" Grid.Row="1"/> 

該作品完善。驗證發生(綁定的PropertyChanged代碼在CurrentPlayer屬性(它是Player的對象)的VM中完成)。

我現在想要做的是在驗證失敗時禁用保存按鈕。

首先,最簡單的解決方案,似乎在這個線程中發現:
Enable Disable save button during Validation using IDataErrorInfo

  1. 如果我想跟進接受的解決方案,我不得不寫我 驗證代碼的兩倍,如我不能簡單地使用索引器。編寫 雙代碼是絕對不是我想要的,所以這不是我的問題 的解決方案。
  2. 該線程的第二個答案聽起來非常有希望作爲首先, 但問題是我有多個字段必須驗證 。這樣,所有東西都依賴於最後選中的屬性 (所以如果該字段填寫正確,CanSave將爲真,即使 儘管還有其他字段仍然無效)。

我發現的另一個解決方案是使用ErrorCount屬性。但是,當我在每次房產更改時(以及每個輸入的字符)驗證時,這是不可能的 - 我怎麼知道何時增加/減少ErrorCount

解決此問題的最佳方法是什麼?

感謝

+0

避免編寫像這樣的所有驗證類,這很受歡迎,因爲它很受歡迎。值得注意的是,如果列的名稱映射到具有錯誤結果的塊,那麼可以使用委託函數的映射;產生所有'如果'是不必要的。但是讓我們假設你要使用它們,你在if(A)中做了一個if(B)',這樣你就可以做一個if(A && B)'。如果只在'if'中執行一條語句,則不必鍵入'{'和'}'字符。通過使用地圖,這個例子可以在~20行而不是~50行;加倍你的發展。 –

回答

1

我實現了在my comment above顯示地圖的方法,在C#這就是所謂的Dictionary中,我使用anonymous methods做驗證:

partial class Player : IDataErrorInfo 
{ 
    private delegate string Validation(string value); 
    private Dictionary<string, Validation> columnValidations; 
    public List<string> Errors; 

    public Player() 
    { 
     columnValidations = new Dictionary<string, Validation>(); 
     columnValidations["Firstname"] = delegate (string value) { 
      return String.IsNullOrWhiteSpace(Firstname) ? "Geef een voornaam in" : null; 
     }; // Add the others... 

     errors = new List<string>(); 
    } 

    public bool CanSave { get { return Errors.Count == 0; } } 

    public string this[string columnName] 
    { 
     get { return this.GetProperty(columnName); } 

     set 
     { 
      var error = columnValidations[columnName](value); 

      if (String.IsNullOrWhiteSpace(error)) 
       errors.Add(error); 
      else 
       this.SetProperty(columnName, value); 
     } 
    } 
} 
+0

謝謝,但我遇到了同樣的問題,如下面的解決方案...構造函數在類的另一部分,這是生成的,所以我不能寫我自己的構造函數... – Bv202

+0

懶加載列驗證在setter中 public string this [string columnName] { get {return this [columnName]; } set { \t if(columnValidations == null) { \t LoadColumnValidations();} \t} \t \t var error = columnValidations [columnName](value); if(String.IsNullOrWhiteSpace(error)) errors.Add(error); else this [columnName] = value; } } – jimbojones

+0

嗯,謝謝,這將是一個選項。但說實話,我不太瞭解這個例子。索引器的get部分到底返回什麼?那麼什麼會返回這[columnName];實際返回? – Bv202

3

本文http://www.asp.net/mvc/tutorials/older-versions/models-%28data%29/validating-with-the-idataerrorinfo-interface-cs移動個人驗證到的屬性:

public partial class Player : IDataErrorInfo 
{ 
    Dictionary<string, string> _errorInfo; 

    public Player() 
    { 
     _errorInfo = new Dictionary<string, string>(); 
    } 

    public bool CanSave { get { return _errorInfo.Count == 0; } 

    public string this[string columnName] 
    { 
     get 
     { 
      return _errorInfo.ContainsKey(columnName) ? _errorInfo[columnName] : null; 
     } 
    } 

    public string FirstName 
    { 
     get { return _firstName;} 
     set 
     { 
      if (String.IsNullOrWhiteSpace(value)) 
       _errorInfo.AddOrUpdate("FirstName", "Geef een voornaam in"); 
      else 
      { 
       _errorInfo.Remove("FirstName"); 
       _firstName = value; 
      } 
     } 
    } 
} 

(你將不得不處理的字典AddOrUpdate擴展方法)。這與您的錯誤計數想法類似。

+0

編輯:'_firstName'應該設置在else子句中,否則可以將對象設置爲無效狀態。這也消除了'FirstName'的出現,由於需要替換代碼的更少部分,所以更容易創建額外的屬性。 –

+0

謝謝,這似乎是一個不錯的解決方案。問題在於我的屬性是在另一個分類(生成的代碼)中定義的,而這些是自動屬性。該解決方案可以以某種方式應用嗎? – Bv202

0

這種方法適用於數據註釋。您還可以將「IsValid」屬性綁定到「保存」按鈕以啓用/禁用。

public abstract class ObservableBase : INotifyPropertyChanged, IDataErrorInfo 
{ 
    #region Members 
    private readonly Dictionary<string, string> errors = new Dictionary<string, string>(); 
    #endregion 

    #region Events 

    /// <summary> 
    /// Property Changed Event 
    /// </summary> 
    public event PropertyChangedEventHandler PropertyChanged; 

    #endregion 

    #region Protected Methods 

    /// <summary> 
    /// Get the string name for the property 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="expression"></param> 
    /// <returns></returns> 
    protected string GetPropertyName<T>(Expression<Func<T>> expression) 
    { 
     var memberExpression = (MemberExpression) expression.Body; 
     return memberExpression.Member.Name; 
    } 

    /// <summary> 
    /// Notify Property Changed (Shorted method name) 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="expression"></param> 
    protected virtual void Notify<T>(Expression<Func<T>> expression) 
    { 
     string propertyName = this.GetPropertyName(expression); 
     PropertyChangedEventHandler handler = this.PropertyChanged; 
     handler?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    /// <summary> 
    /// Called when [property changed]. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="expression">The expression.</param> 
    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> expression) 
    { 
     string propertyName = this.GetPropertyName(expression); 
     PropertyChangedEventHandler handler = this.PropertyChanged; 

     handler?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    #endregion 

    #region Properties 

    /// <summary> 
    /// Gets an error message indicating what is wrong with this object. 
    /// </summary> 
    public string Error => null; 

    /// <summary> 
    /// Returns true if ... is valid. 
    /// </summary> 
    /// <value> 
    /// <c>true</c> if this instance is valid; otherwise, <c>false</c>. 
    /// </value> 
    public bool IsValid => this.errors.Count == 0; 

    #endregion 

    #region Indexer 

    /// <summary> 
    /// Gets the <see cref="System.String"/> with the specified column name. 
    /// </summary> 
    /// <value> 
    /// The <see cref="System.String"/>. 
    /// </value> 
    /// <param name="columnName">Name of the column.</param> 
    /// <returns></returns> 
    public string this[string columnName] 
    { 
     get 
     { 
      var validationResults = new List<ValidationResult>(); 
      string error = null; 

      if (Validator.TryValidateProperty(GetType().GetProperty(columnName).GetValue(this), new ValidationContext(this) { MemberName = columnName }, validationResults)) 
      { 
       this.errors.Remove(columnName); 
      } 
      else 
      { 
       error = validationResults.First().ErrorMessage; 

       if (this.errors.ContainsKey(columnName)) 
       { 
        this.errors[columnName] = error; 
       } 
       else 
       { 
        this.errors.Add(columnName, error); 
       } 
      } 

      this.OnPropertyChanged(() => this.IsValid); 
      return error; 
     } 
    } 

    #endregion 
}