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行われます (バインディングのコードは、Player のオブジェクトである CurrentPlayer プロパティの VM で実行されます)。

私が今やりたいことは、検証が失敗したときに保存ボタンを無効にすることです。

まず、最も簡単な解決策がこのスレッドにあるようです:
IDataErrorInfo を使用して検証中に保存ボタンを無効にする

  1. 受け入れられた解決策に従いたい場合は、単純にインデクサーを使用できないため、検証コードを 2 回記述する必要があります。ダブルコードを書くことは私が望んでいることではないので、それは私の問題の解決策ではありません。
  2. そのスレッドの 2 番目の回答は、最初の回答と同様に非常に有望に思えましたが、問題は、検証が必要なフィールドが複数あることです。そうすれば、すべてが最後にチェックされたプロパティに依存します (したがって、そのフィールドが正しく入力されていれば、CanSaveまだ無効な他のフィールドがあっても true になります)。

私が見つけたもう 1 つの解決策は、ErrorCountプロパティを使用することです。しかし、私はプロパティの変更ごとに (そして入力された文字ごとに) 検証しているので、これも不可能ErrorCountです。

この問題を解決する最善の方法は何でしょうか?

ありがとう

4

3 に答える 3

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;
            }
        }
    }
}

(DictionaryAddOrUpdate拡張メソッドを処理する必要があります)。これは、エラー数のアイデアに似ています。

于 2012-11-14T21:04:04.123 に答える
1

上記のコメントに示されているマップ アプローチを実装しました。C# では、これはDictionaryと呼ばれ、匿名メソッドを使用して検証を行っています。

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);
        }
    }
}
于 2012-11-14T21:34:10.617 に答える
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  
}
于 2016-10-20T17:52:02.063 に答える