WPF TextBox验证

Bur*_*urt 12 validation wpf textbox wpf-controls

我已将验证连接到绑定到TextBox容器的模型.当窗口第一次打开时,验证错误出现,因为模型为空,我不希望看到验证错误,直到提交窗口或文本TextBox已更改或丢失焦点.

这是TextBox:

<TextBox Text="{Binding 
                   Path=Firstname, 
                   UpdateSourceTrigger=PropertyChanged, 
                   ValidatesOnDataErrors=True}"
         Width="124"
         Height="24"/>
Run Code Online (Sandbox Code Playgroud)

怎么能实现这一目标?

Joh*_*wen 22

这实际上取决于您对IDataErrorInfo的实现.如果您将其基于错误消息字典,则可以控制何时添加到该列表的验证运行.您通常希望从属性设置器中执行此操作(例如,无论何时调用PropertyChange),此处调用CheckValidationState:

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

    public Dictionary<string, string> Errors { get; private set; }

    protected void SetError(string propertyName, string errorMessage)
    {
        Debug.Assert(!String.IsNullOrEmpty(propertyName), "propertyName is null or empty.");
        if (String.IsNullOrEmpty(propertyName))
            return;

        if (!String.IsNullOrEmpty(errorMessage))
        {
            if (Errors.ContainsKey(propertyName))
                Errors[propertyName] = errorMessage;
            else
                Errors.Add(propertyName, errorMessage);
        }
        else if (Errors.ContainsKey(propertyName))
            Errors.Remove(propertyName);

        NotifyPropertyChanged("Errors");
        NotifyPropertyChanged("Error");
        NotifyPropertyChanged("Item[]");
    }

    protected virtual string ValidateProperty(string propertyName)
    {
        return Errors.ContainsKey(propertyName) ? Errors[propertyName] : null;
    }

    protected virtual bool CheckValidationState<T>(string propertyName, T proposedValue)
    {
        // your validation logic here
    }
Run Code Online (Sandbox Code Playgroud)

然后,您还可以包含一个验证所有属性的方法(例如在保存期间):

    protected bool Validate()
    {
        if (Errors.Count > 0)
            return false;

        bool result = true;
        foreach (PropertyInfo propertyInfo in GetType().GetProperties())
        {
            if (!CheckValidationState(propertyInfo.Name, propertyInfo.GetValue(this, null)))
                result = false;
            NotifyPropertyChanged(propertyInfo.Name);
        }
        return result;
    }
Run Code Online (Sandbox Code Playgroud)

更新:

我建议将上面的代码放入基础ViewModel类中,以便重用它.然后,您可以创建一个这样的派生类:

public class SampleViewModel : ViewModelBase
{
    private string _firstName;

    public SampleViewModel()
    {
        Save = new DelegateCommand<object>(SaveExecuted);
    }

    public DelegateCommand<object> Save { get; private set; }

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName == value)
                return;

            CheckValidationState("FirstName", value);

            _firstName = value;
            NotifyPropertyChanged("FirstName");
        }
    }

    public void SaveExecuted(object obj)
    {
        bool isValid = Validate();
        MessageBox.Show(isValid ? "Saved" : "Validation Error. Save canceled"); // TODO: do something appropriate to your app here
    }

    protected override bool CheckValidationState<T>(string propertyName, T proposedValue)
    {
        // your validation logic here
        if (propertyName == "FirstName")
        {
            if (String.IsNullOrEmpty(proposedValue as String))
            {
                SetError(propertyName, "First Name is required.");
                return false;
            }
            else if (proposedValue.Equals("John"))
            {
                SetError(propertyName, "\"John\" is not an allowed name.");
                return false;
            }
            else
            {
                SetError(propertyName, String.Empty); // clear the error
                return true;
            }
        }
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我使用DelegateCommand来触发保存操作,但它可以是任何使方法调用来执行保存的操作.此设置允许初始空状态在UI中显示为有效,但更改或调用Save会更新验证状态.你也可以在实际进行验证的过程中获得更多通用和更复杂的功能,因此它不会最终出现在一个方法中(这里有一些关于类型的假设)但是这个简化了以便更容易开始.


Bre*_*ent 7

如果您正在实现IDataErrorInfo,我通过在验证逻辑的实现中检查空值来实现这一点.创建新窗口时,检查null将阻止验证逻辑触发.例如:

public partial class Product : IDataErrorInfo
{
    #region IDataErrorInfo Members

    public string Error
    {
        get { return null; }
    }

    public string this[string columnName]
    {
        get
        {
            if (columnName == "ProductName")
            {
                // Only apply validation if there is actually a value
                if (this.ProductName != null)
                {
                    if (this.ProductName.Length <= 0 || this.ProductName.Length > 25)
                        return "Product Name must be between 1 and 25 characters";
                }
            }

            return null;
        }
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

此外,如果要在TextBox.LostFocus上触发验证,请将绑定更改为LostFocus,如下所示:

<TextBox Text="{Binding              
               Path=Firstname,              
               UpdateSourceTrigger=LostFocus,              
               ValidatesOnDataErrors=True}"             
     Width="124"             
     Height="24"/>
Run Code Online (Sandbox Code Playgroud)