WPF自验证控制

本文关键字:控制 验证 WPF | 更新日期: 2023-09-27 17:57:59

我必须构建一个自定义控件。带有内置验证的简单文本框,我可以在应用程序的不同部分使用。我是这样做的:

  1. 我创建了直接从TextBox派生的新自定义控件(称之为ValidTextBox)
  2. 它有一个带有简单验证逻辑的视图模型(ValidTextBoxVM)
  3. 控件的Text属性绑定到视图模型的Number属性

ValidTextBoxVM代码:

public class ValidTextBoxVM : INotifyPropertyChanged
    {
    #region INotifyPropertyChange implementation
    private String _number;
    public String Number 
    {
      get { return _number; }
      set 
      {
        if (_number != value)
        {
          Validate(value);
          _number = value;
          RaisePropertyChanged("Number");
        }
      }
    }
    private void Validate(string number)
    {
      if (!string.IsNullOrEmpty(number) && number.Length > 10)
      {
        throw new ArgumentOutOfRangeException("Number too long.");
      }
    }
  }

ValidTextBox.xaml代码:

<TextBox x:Class="WpfApplication1.ValidTextBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:vm="clr-namespace:WpfApplication1"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             Text="{Binding Number, ValidatesOnExceptions=True, UpdateSourceTrigger=LostFocus}">
    <TextBox.DataContext>
        <vm:ValidTextBoxVM/>
    </TextBox.DataContext>
</TextBox>

我把控制权放在MainWindow上,它运行得很好。在失去焦点时,如果验证过程没有通过,ViewModel会引发异常——这没关系(下面的代码)。

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ctrl="clr-namespace:WpfApplication1"
        xmlns:vm="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <ctrl:ValidTextBox Margin="5" Width="200" HorizontalAlignment="Left"/>
    </StackPanel>
</Window>

当我为MainWindow(MainWindowVM)使用单独的视图模型并将控件的Text属性与MainWindowVM中的字段(MainNumber)绑定时,情况发生了变化。它隐藏了我以前的绑定,验证已经停止工作(下面的代码)。

<StackPanel>
    <ctrl:ValidTextBox Text="{Binding MainNumber, ValidatesOnExceptions=True, UpdateSourceTrigger=LostFocus}" Margin="5" Width="200" HorizontalAlignment="Left"/>
</StackPanel>

是否有任何模式可以使创建自验证控件成为可能。我发现了许多解决方案,但验证过程超出了控制范围。

WPF自验证控制

问题是在TextBox上设置DataContext。这意味着当你写:

<ctrl:ValidTextBox Text="{Binding MainNumber

框架将尝试解析ctrl:ValidTextBox对象上的"MainNumber",即对象的DataContext。(这是违反直觉的,但它就是这样工作的——如果您检查Visual Studio"Output"窗口,您应该能够看到对象"ValidTextBox"上"找不到属性"MainNumber"的绑定错误。)


我发现,使用特定于控件的视图模型通常很棘手,并且会导致复杂性。我建议尽可能避免这种做法。在这种情况下,为什么不扩展TextBox并为LostFocus事件添加一个验证处理程序呢?

public class ValidTextBox : TextBox
{
    public ValidTextBox()
    {
        LostFocus += ValidTextBox_LostFocus;
    }
    void ValidTextBox_LostFocus(object sender, System.Windows.RoutedEventArgs e)
    {
        // TODO validate
    }
}