【问题标题】:WPF TextBox ValidationWPF 文本框验证
【发布时间】:2010-01-17 00:49:51
【问题描述】:

我已将验证连接到绑定到 TextBox 容器的模型。首次打开窗口时出现验证错误,因为模型为空,在提交窗口或TextBox 中的文本已更改或失去焦点之前,我不想看到验证错误。

这里是TextBox

<TextBox Text="{Binding 
                   Path=Firstname, 
                   UpdateSourceTrigger=PropertyChanged, 
                   ValidatesOnDataErrors=True}"
         Width="124"
         Height="24"/>

如何做到这一点?

【问题讨论】:

    标签: wpf validation textbox wpf-controls


    【解决方案1】:

    这实际上取决于您对 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
        }
    

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

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

    更新:

    我建议将上述代码放入基础 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;
        }
    }
    

    在这种情况下,我使用 DelegateCommand 来触发保存操作,但它可以是任何调用方法来进行保存的东西。此设置允许初始空状态在 UI 中显示为有效,但更改或调用 Save 会更新验证状态。您还可以在实际进行验证的方式上获得更通用和更复杂的方法,因此它不会全部以一种方法结束(这里有一些关于类型的假设),但这被简化以使其更容易开始.

    【讨论】:

    • 感谢约翰的回复,请您扩展一下您的答案。我试图实施你所讨论的内容,但有点迷失了。谢谢...
    • Burt - 我更新以添加更多代码来展示一个类如何实现这一点,我希望为您填补一些漏洞。这对你有帮助吗?
    • 谢谢约翰真的很感激,去投票给你,但不知何故设法删除了它没有让我再次添加它的投票。
    • @JohnBowen 为什么 NotifyPropertyChanged("Errors"); NotifyPropertyChanged("错误"); NotifyPropertyChanged("Item[]");在 SetError 中触发?
    【解决方案2】:

    如果您正在实现 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
    }
    

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

    <TextBox Text="{Binding              
                   Path=Firstname,              
                   UpdateSourceTrigger=LostFocus,              
                   ValidatesOnDataErrors=True}"             
         Width="124"             
         Height="24"/>
    

    【讨论】:

    • 虽然在这种情况下会发生什么,但某些字段为 null 可能是有效的?
    • 如果 null 是一个有效的条目,那么如果您告诉它不这样做,验证将不会返回错误。这完全取决于您如何实现业务逻辑。例如,如果该字段可以为 null 或为空,则只允许在您的逻辑中进行这些异常。你能举个例子说明你想在特定领域检查什么吗?
    【解决方案3】:

    在您的 app.xaml 文件中,您需要使用自定义文本框样式进行文本框验证,无需任何第三方组件。

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <Grid Name="test">
                    <Border Background="{StaticResource TextBackColor}"
                            BorderBrush="#FF888888"
                            x:Name="Bd"
                            CornerRadius="1"
                            BorderThickness="1">
                        <ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
                    </Border>
                    <Image  Name="ErrorImage"
                            Width="15"
                            Height="15"
                            Margin="0,0,4,0"
                            Source="Images/validate.png"
                            HorizontalAlignment="Right">
                    </Image>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    

    【讨论】:

      【解决方案4】:

      我做什么,我不知道这是否是正确的方法(我很高兴学习,现在是一个机会),但在实体或模型的初始化程序中,我运行所有验证器。

      【讨论】:

        猜你喜欢
        • 2015-04-21
        • 2014-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多