【问题标题】:WPF Validation for the whole form整个表单的 WPF 验证
【发布时间】:2008-09-19 18:54:18
【问题描述】:

我对 WPF 验证系统感到非常失望。反正!如何通过单击“按钮”来验证完整的表格?

由于某种原因,WPF 中的一切都太复杂了!我可以在 ASP.NET 中的 1 行代码中进行验证,这需要 WPF 中的 10-20 行代码!!

我可以使用我自己的 ValidationEngine 框架来做到这一点:

Customer customer = new Customer();
customer.FirstName = "John";
customer.LastName = String.Empty;

ValidationEngine.Validate(customer);

if (customer.BrokenRules.Count > 0)
{
   // do something display the broken rules! 
}

【问题讨论】:

    标签: wpf validation


    【解决方案1】:

    如果输入的数据无效,WPF 应用程序应禁用提交表单的按钮。您可以通过在业务对象上实现IDataErrorInfo 接口来实现此目的,使用带有ValidatesOnDataErrors=true 的绑定。要在出现错误时自定义各个控件的外观,请设置 Validation.ErrorTemplate

    XAML:

    <Window x:Class="Example.CustomerWindow" ...>
        <Window.CommandBindings>
            <CommandBinding Command="ApplicationCommands.Save"
                            CanExecute="SaveCanExecute"
                            Executed="SaveExecuted" />
        </Window.CommandBindings>
        <StackPanel>
            <TextBox Text="{Binding FirstName, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
            <TextBox Text="{Binding LastName, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
            <Button Command="ApplicationCommands.Save" IsDefault="True">Save</Button>
            <TextBlock Text="{Binding Error}"/>
        </StackPanel>
    </Window>
    

    这将创建一个带有两个 TextBoxes 的 Window,您可以在其中编辑客户的名字和姓氏。只有在没有发生验证错误时才启用“保存”按钮。按钮下方的TextBlock 显示当前错误,因此用户知道发生了什么。

    默认ErrorTemplate 是错误控件周围的细红色边框。如果这不符合您的视觉概念,请查看 CodeProject 上的 Validation in Windows Presentation Foundation 文章,深入了解可以做些什么。

    要让窗口真正工作,窗口和客户中必须有一些基础设施。

    代码隐藏

    // The CustomerWindow class receives the Customer to display
    // and manages the Save command
    public class CustomerWindow : Window
    {
        private Customer CurrentCustomer;
        public CustomerWindow(Customer c) 
        {
            // store the customer for the bindings
            DataContext = CurrentCustomer = c;
            InitializeComponent();
        }
    
        private void SaveCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = ValidationEngine.Validate(CurrentCustomer);
        }
    
        private void SaveExecuted(object sender, ExecutedRoutedEventArgs e) 
        {
            CurrentCustomer.Save();
        }
    }
    
    public class Customer : IDataErrorInfo, INotifyPropertyChanged
    {
        // holds the actual value of FirstName
        private string FirstNameBackingStore;
        // the accessor for FirstName. Only accepts valid values.
        public string FirstName {
            get { return FirstNameBackingStore; }
            set {
                FirstNameBackingStore = value;
                ValidationEngine.Validate(this);
                OnPropertyChanged("FirstName");
            }
        }
        // similar for LastName        
    
        string IDataErrorInfo.Error {
            get { return String.Join("\n", BrokenRules.Values); }
        }
    
        string IDataErrorInfo.this[string columnName]
        {
            get { return BrokenRules[columnName]; }
        }
    }
    

    一个明显的改进是将IDataErrorInfo 实现向上移动到类层次结构中,因为它只依赖于ValidationEngine,而不依赖于业务对象。

    虽然这确实比您提供的简单示例代码更多,但它的功能也比仅检查有效性要多得多。这为您提供了细粒度的、自动更新的关于验证问题的指示,并在用户尝试输入无效数据时自动禁用“保存”按钮。

    【讨论】:

    • 当我只需要确保所有字段都填满时,这似乎太多了。
    • 如果您只需要确保所有字段都已填写,您只需将验证码放入提交按钮的 Click 处理程序中即可。但是你的用户会问“为什么我不能提交表单?”你仍然需要实现所有的通知和东西。
    • 如果所有这些都是 TextBox 或输入控件的一部分,那将是合乎逻辑的。喜欢
    • 验证规则是数据模型的一部分,而不是 GUI。必须能够在没有 GUI 的情况下检查对象的有效性。例如,考虑面向批处理的数据处理器。
    • 这就是我的观点! WPF 可以通过使用绑定和设置 ValidatesOnDataErrors=true 和/或 ValidatesOnException=true 将验证委托给域级别。我的回答显示了一种方法。
    【解决方案2】:

    我建议查看您的业务对象上的 IDataErrorInfo 接口。也可以看看这篇文章:Self Validating Text Box

    【讨论】:

      【解决方案3】:

      您可能对 WPF Application Framework (WAF)BookLibrary 示例应用程序感兴趣。它展示了如何在 WPF 中使用验证,以及在存在验证错误时如何控制“保存”按钮。

      【讨论】:

        【解决方案4】:

        ValidatesOnDataError 用于根据您的视图模型验证业务规则,并且只有在绑定成功时才会验证。

        ValidatesOnExceptions 需要与 ValidatesOnDataError 一起应用以处理由于数据类型不匹配而导致 wpf 无法执行绑定的情况,假设您要将 TextBox 绑定到视图模型中的 Age(整数)属性

        <TextBox Text="{Binding Age, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
        

        如果用户通过键入字母而不是数字作为年龄输入无效条目,例如 xyz,wpf 数据绑定将默默地忽略该值,因为它无法将 xyz 绑定到 Age,并且绑定错误将丢失,除非绑定用ValidatesOnExceptions

        <TextBox Text="{Binding Age, ValidatesOnDataErrors=true, ValidatesOnExceptions="True", UpdateSourceTrigger=PropertyChanged}" />
        

        ValidatesOnException 使用 ExceptionValidationRule 对绑定错误使用默认的异常处理,上面的语法是下面的简写形式

        <TextBox>
            <TextBox.Text>
                <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
                    <Binding.ValidationRules>
                          <ExceptionValidationRule />
                     </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        

        您可以定义自己的规则来验证用户输入,方法是从 ValidationRule 派生并实现 Validate 方法,在以下示例中为 NumericRule

        <TextBox.Text>
         <Binding Path="Age" ValidatesOnDataErrors="True">
           <Binding.ValidationRules>
                <rules:NumericRule />
              </Binding.ValidationRules>
            </Binding>
          </TextBox.Text>
        

        验证规则应该是通用的,不依赖于业务,因为后者是通过 IDataErrorInfo 和 ValidatesOnDataError 完成的。

        上面的语法与我们的单行绑定语法相比相当混乱,通过将 ValidationRule 实现为附加属性,可以改进语法,你可以看看here

        【讨论】:

          【解决方案5】:

          您的问题的描述对我来说有点模糊。我的意思是,我不确定你的困难是什么。 假设 DataContext 是某种具有代表客户实例的属性的演示者或控制器,而 ValidateCommand 是 ICommand 类型的属性:

            <StackPanel>  
              <TextBox Text="{Binding CurrentCustomer.FirstName}" />
              <TextBox Text="{Binding CurrentCustomer.LastName}" />
              <Button Content="Validate" 
                      Command="{Binding ValidateCommand}"
                      CommandParameter="{Binding CurrentCustomer}" />
              <ItemsControl ItemsSource="{Binding CurrentCustomer.BrokenRules}" />
            </StackPanel>
          

          当然,这个 XAML 确实被简化了,还有其他方法可以做到这一点。 作为一名现在与 WPF 密切相关的 Web 开发人员,我发现在 WPF 中,大多数此类任务都变得容易得多。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-12-21
            • 1970-01-01
            • 2022-01-01
            • 2016-02-18
            相关资源
            最近更新 更多