4

我有一个名为“CustomerDataContract”的类,它具有三个属性:MobilePhone、OfficePhone 和 PrivatePhone。为了与客户取得联系,必须设置至少一个属性。

我创建了一个 Validator-Class 并添加了一个名为“CheckForAnyPhoneNumber”的自定义规则来检查是否至少提供了一个电话号码。这几乎解决了我的问题。我唯一的问题是,如果我清除例如 MobilePhone-Attribute 它会被评估并处于错误状态。如果我现在输入 OfficePhone 号码,MobilePhone-Attribute 不会被重新评估并保持错误模式。

知道如何正确解决这个问题吗?我也尝试创建一个 RuleSet 但没有成功。

这是(部分)CustomerDataContract:

public partial class CustomerDataContract
{

    public CustomerDataContract Clone()
    {
        return (CustomerDataContract) MemberwiseClone();
    }

    public override ValidationResult SelfValidate()
    {
        return ValidationHelper.Validate<CustomerDataContractValidator, CustomerDataContract>(this);
    }

}

所有 DataContract 的基类:

[Serializable]
public abstract class BDataModel : IDataErrorInfo, INotifyPropertyChanged
{

    [field: NonSerialized]
    private ValidationResult _currentState = new ValidationResult();

    public ValidationResult CurrentValidationState
    {
        get { return _currentState; }
        set 
        { 
            _currentState = value;
            OnPropertyChanged("CurrentValidationState");
            OnPropertyChanged("IsValid");
            OnPropertyChanged("Error");
        }
    }

    #region INotifyPropertyChanged

    [field: NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(params string[] propertyName)
    {
        propertyName.ToList().ForEach(x =>
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(x));
        });
    }

    #endregion

    public virtual ValidationResult SelfValidate()
    {
        return new ValidationResult();
    }

    public bool IsValid
    {
        get
        {
            if (CurrentValidationState == null)
                CurrentValidationState = SelfValidate();
            return CurrentValidationState.IsValid;
        }
    }

    #region IDataErrorInfo

    public string Error
    {
        get { return ValidationHelper.GetError(CurrentValidationState); }
    }

    public string this[string columnName]
    {
        get
        {
            CurrentValidationState = SelfValidate();
            if (_currentState == null) return string.Empty;
            var columnResults = _currentState.Errors.FirstOrDefault(x => string.Compare(x.PropertyName, columnName, true) == 0);
            return columnResults != null ? columnResults.ErrorMessage : string.Empty;
        }
    }

    #endregion

}

这是验证器类:

public class CustomerDataContractValidator : AbstractValidator<CustomerDataContract>
{
    private readonly ILanguageManager _languageManager = LanguageManager.Instance;

    public CustomerDataContractValidator()
    {
        RuleFor(val => val.Lastname).NotEmpty().WithMessage(_languageManager.LanguageData["Global.Validation.ProvideLastname"]);
        RuleFor(val => val.Firstname).NotEmpty().WithMessage(_languageManager.LanguageData["Global.Validation.ProvideFirstname"]);
        RuleFor(val => val.Active).NotEmpty().WithMessage(_languageManager.LanguageData["Global.Validation.ProvideActive"]);
        RuleFor(val => val.Gender).NotEmpty().WithMessage(_languageManager.LanguageData["Global.Validation.ProvideGender"]);
        RuleFor(val => val.Mail).EmailAddress().When(val => !String.IsNullOrEmpty(val.Mail)).WithMessage(_languageManager.LanguageData["Global.Validation.ProvideValidEmail"]);

        // At least one phone number has to be filled in order to contact the customer
        RuleFor(val => val.MobilePhone).Must(CheckForAnyPhoneNumber).WithMessage(_languageManager.LanguageData["Global.Validation.ProvideAnyPhone"]);
        RuleFor(val => val.OfficePhone).Must(CheckForAnyPhoneNumber).WithMessage(_languageManager.LanguageData["Global.Validation.ProvideAnyPhone"]);
        RuleFor(val => val.PrivatePhone).Must(CheckForAnyPhoneNumber).WithMessage(_languageManager.LanguageData["Global.Validation.ProvideAnyPhone"]);

    }

    /// <summary>
    /// Checks wheter or not any phone number has been provided
    /// </summary>
    /// <param name="customer">The customer</param>
    /// <param name="phoneNumber">The phone number</param>
    /// <returns></returns>
    private static bool CheckForAnyPhoneNumber(CustomerDataContract customer, string phoneNumber)
    {
        return (!String.IsNullOrEmpty(customer.MobilePhone) || !String.IsNullOrEmpty(customer.OfficePhone) || !String.IsNullOrEmpty(customer.PrivatePhone));
    }
}

和验证助手:

public class ValidationHelper
{
    public static ValidationResult Validate<T, K>(K entity)
        where T : IValidator<K>, new()
        where K : class
    {
        IValidator<K> validator = new T();
        return validator.Validate(entity);
    }

    public static string GetError(ValidationResult result)
    {
        var errorMessage = "";
        if (result != null)
        {
            var failure = result.Errors.FirstOrDefault();
            if(failure != null)
            {
                errorMessage = failure.ErrorMessage;
            }
        }
        return errorMessage;
    }
}

视图模型:

public class CustomerViewModel : BMovingSelectionViewModel, IEventListener, ICustomerViewModel
{

    private CustomerDataContract _customer;

    public CustomerDataContract Customer
    {
        get
        {
            return _customer;
        }

        set
        {
            if (_customer != value)
            {
                _customer = value;
                OnPropertyChanged(() => Customer);
            }
        }
    }
}

和观点:

<TextBlock Text="{MarkupExtensions:LanguageManager Global.Person.PhoneOffice}" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" />
<TextBox Grid.Row="3" Grid.Column="1" Text="{Validation:ValidationBinding Path=Customer.OfficePhone}" HorizontalContentAlignment="Stretch"  />

<TextBlock Text="{MarkupExtensions:LanguageManager Global.Person.PhonePrivate}" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" />
<TextBox Grid.Row="4" Grid.Column="1" Text="{Validation:ValidationBinding Path=Customer.PrivatePhone}" />

<TextBlock Text="{MarkupExtensions:LanguageManager Global.Person.PhoneMobile}" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" />
<TextBox Grid.Row="5" Grid.Column="1" Text="{Validation:ValidationBinding Path=Customer.MobilePhone}" />
4

1 回答 1

0

要使 WPF 更新错误信息,需要触发它来执行此操作。该触发器是PropertyChanged绑定属性上的事件。


最简单的解决方案是让您的视图模型参与进来。如果你能做到这一点,你只需要在PropertyChanged依赖属性组中每个属性的设置器中为所有依赖属性引发事件。
这意味着设置PhoneOffice会引发PropertyChangedfor"PhoneOffice""PhonePrivate"for "PhoneMobile"


另一种解决方案是使用规则集并将相关代码放入基类中。要使其工作,需要执行以下步骤:

  1. 让基类监听自己的PropertyChanged事件
  2. 在事件处理程序中,获取此属性的验证规则
  3. 获取同一规则集的所有其他验证规则
  4. 提高PropertyChanged验证规则的属性
于 2013-04-23T12:18:52.747 回答