Проверка запуска слишком рано - PullRequest
1 голос
/ 01 ноября 2011

Я построил базовый класс для моделей моего вида. Вот некоторые кода:

public class BaseViewModel<TModel> : DependencyObject, INotifyPropertyChanged, IDisposable, IBaseViewModel<TModel>, IDataErrorInfo
{
        public TModel Model { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (this._disposed)
            {
                return;
            }

            if (disposing)
            {
                this.Model = default(TModel);
            }

            this._disposed = true;
        }
}

Хорошо, я подумал, давайте добавим некоторую проверку в базовый класс, что привело меня к следующей статье: Проверка Prism IDataErrorInfo с DataAnnotation в объектах ViewModel . Поэтому я добавил следующие методы / свойства (IDataErrorInfo) в свой базовый класс:

string IDataErrorInfo.Error
{
    get { return null; }
}

string IDataErrorInfo.this[string columnName]
{
    get { return ValidateProperty(columnName); }
}

protected virtual string ValidateProperty(string columnName)
{
    // get cached property accessors
    var propertyGetters = GetPropertyGetterLookups(GetType());

    if (propertyGetters.ContainsKey(columnName))
    {
        // read value of given property
        var value = propertyGetters[columnName](this);

        // run validation
        var results = new List<ValidationResult>();
        var vc = new ValidationContext(this, null, null) { MemberName = columnName };
        Validator.TryValidateProperty(value, vc, results);

        // transpose results
        var errors = Array.ConvertAll(results.ToArray(), o => o.ErrorMessage);
        return string.Join(Environment.NewLine, errors);
    }
    return string.Empty;
}

private static Dictionary<string, Func<object, object>> GetPropertyGetterLookups(Type objType)
{
    var key = objType.FullName ?? "";
    if (!PropertyLookupCache.ContainsKey(key))
    {
        var o = objType.GetProperties()
        .Where(p => GetValidations(p).Length != 0)
        .ToDictionary(p => p.Name, CreatePropertyGetter);

        PropertyLookupCache[key] = o;
        return o;
    }
    return (Dictionary<string, Func<object, object>>)PropertyLookupCache[key];
}

private static Func<object, object> CreatePropertyGetter(PropertyInfo propertyInfo)
{
    var instanceParameter = System.Linq.Expressions.Expression.Parameter(typeof(object), "instance");

    var expression = System.Linq.Expressions.Expression.Lambda<Func<object, object>>(
        System.Linq.Expressions.Expression.ConvertChecked(
            System.Linq.Expressions.Expression.MakeMemberAccess(
                System.Linq.Expressions.Expression.ConvertChecked(instanceParameter, propertyInfo.DeclaringType),
                propertyInfo),
            typeof(object)),
        instanceParameter);

    var compiledExpression = expression.Compile();

    return compiledExpression;
}

private static ValidationAttribute[] GetValidations(PropertyInfo property)
{
    return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
}

Хорошо, это подводит меня к вопросу. Дело в том, что проверка работает отлично, но, допустим, у меня есть свойство (в моей модели представления с именем: Person) с атрибутом StringLength. Атрибут StringLength срабатывает, как только приложение открывается. У пользователя даже не было возможности что-либо сделать. Проверка запускается, как только приложение запускается.

public class PersonViewModel : BaseViewModel<BaseProxyWrapper<PosServiceClient>>
{
    private string _password = string.Empty;
    [StringLength(10, MinimumLength = 3, ErrorMessage = "Password must be between 3 and 10 characters long")]
    public string Password
    {
        get { return this._password; }
        set
        {
            if (this._password != value)
            {
                this._password = value;
                this.OnPropertyChanged("Password");
            }
        }
    }
}

Я заметил, что это вызвано свойством IDataErrorInfo.this[string columnName] и, в свою очередь, вызывает метод ValidateProperty. Но я понятия не имею, как это исправить?

Ответы [ 2 ]

2 голосов
/ 01 ноября 2011

Может быть две проблемы ...

Заполняете ли вы экземпляр yopur Person с помощью открытых свойств?

например.

  new Person { Password = null }

Это вызовет уведомление об изменении свойства для пароля и подтвердит его.

Некоторые разработчики также устанавливают свойства в конструкторах ...

public class Person {
   public Person() {
      this.Password = null;
   }
} 

Рекомендуется использовать закрытые поля ...

public class Person {
   public Person() {
      _password = null;
   }

   public Person(string pwd) {
      _password = pwd;
   }
} 

OR

Вы можете создать флаг в нашей базе моделей вида, скажем IsLoaded. Убедитесь, что вы установили значение true только после загрузки пользовательского интерфейса (возможно, в событии UI.Loaded) В вашем IDataErrorInfo.this[string columnName] проверьте, является ли это свойство истинным, и только затем проверяйте значения. В противном случае вернуть ноль.

[EDIT]

Следующее изменение сделало работу:

public class PersonViewModel : BaseViewModel<BaseProxyWrapper<PosServiceClient>>
{
    private string _password;
    [StringLength(10, MinimumLength = 3, ErrorMessage = "Password must be between 3 and 10 characters long")]
    public string Password
    {
        get { return this._password; }
        set
        {
            if (this._password != value)
            {
                this._password = value;
                this.OnPropertyChanged("Password");
            }
        }
    }

    public PersonViewModel(BaseProxyWrapper<PosServiceClient> model)
        : base(model)
    {
        this._username = null;
    }
}
0 голосов
/ 02 ноября 2011

Что-то, что я делал в прошлом, изменил триггер источника обновлений на явный, создаю поведение, которое будет обновлять источник, когда TextBox теряет фокус, а затем присоединяет это поведение к TextBox.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...