【问题标题】:WPF TextBox Binding Validation Rules not Firing on LostFocus When TextBox is Empty当文本框为空时,WPF 文本框绑定验证规则不会在 LostFocus 上触发
【发布时间】:2009-06-25 18:03:08
【问题描述】:

我们都知道开箱即用的 WPF 验证有多糟糕。我正在尝试一件非常简单的事情,但由于某种原因它总是失败。我有一个 TextBox,我唯一的要求是验证用户在 TextBox 中输入的内容。 TextBox 绑定到具有 FirstName 和 LastName 属性的 Customer 对象。

这是 XAML 代码:

            <TextBox Style="{StaticResource TextBoxStyle}" Grid.Column="1"  Grid.Row="0" Height="20" Width="100" Margin="10">
                <TextBox.Text>
                    <Binding Path="FirstName" >
                        <Binding.ValidationRules>
                            <ExceptionValidationRule />
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>

            </TextBox>

这是 Customer 类的 FirstName 属性:

public string FirstName
        {
            get { return _firstName;}
            set
            {
                if(String.IsNullOrEmpty(value))
                    throw new ApplicationException("FirstName cannot be null or empty!");
                _firstName = value; 

                OnPropertyChanged("FirstName");
            }
        }

即使我在 FirstName(值)为 null 或为空时引发异常,但只有在我在 TextBox 中键入内容然后删除它然后关闭选项卡时才会处理它。原因是它依赖于属性更改事件。但即使我将 TextBox 绑定放在 Focus 上,它也不会触发验证。

更新:

处理此问题的最丑陋的方法之一是将 String.Empty 分配给 Window.Loaded 事件上的 TextBoxes:

 void AddCustomerWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // get all the textboxes and set the property to empty strings! 

            txtFirstName.Text = String.Empty;
            txtLastName.Text = String.Empty; 
        }

下面是绑定代码:

 public AddCustomerWindow()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(AddCustomerWindow_Loaded);

            gvAddCustomer.DataContext = new Customer();  
        }

【问题讨论】:

    标签: wpf validation textbox


    【解决方案1】:

    我使用的解决方案是连接控件以在它们失去焦点时更新它们自己的绑定源。这使得控件在用户通过选项卡或单击一次后生效。

    public AddCustomerWindow()
    {
        InitializeComponent();
        this.Loaded += AddCustomerWindow_Loaded;
        gvAddCustomer.DataContext = new Customer();  
    }
    
    void AddCustomerWindow_Loaded(object sender, RoutedEventArgs e)
    {
        // Manually wire up textboxes of specific interest.
        UpdateSourceOnLostFocus(new TextBox[] {txtFirstName, txtLastName},
                                TextBox.TextProperty);
    
        // Or, wire up ALL controls of a given type with the help of Telerik's
        // ChildrenOfType extension, using Linq to filter the list where appropriate.
        UpdateSourceOnLostFocus(this.ChildrenOfType<RadDatePicker>()
                                    .Where(picker => !picker.IsReadOnly), 
                                RadDatePicker.SelectedValueProperty);
    }
    
    void UpdateSourceOnLostFocus<T>(IEnumerable<T> controls, DependencyProperty prop) where T : Control
    {
        controls.ToList()
            .ForEach(ctrl =>
            {
                var binding = BindingOperations.GetBindingExpression(ctrl, prop);
                if (binding != null)
                    ctrl.LostFocus += (sender, args) => binding.UpdateSource();
            });
    }
    

    如果您还希望控件在加载时验证 - 即,用户选项卡/单击它们之前 - 那么您可以编写类似于上述方法的 UpdateSourceNow 方法,并从 AddCustomerWindow_Loaded 调用它反而。

    但是,我更喜欢等到每个控件都被访问后,因为如果用户在想要输入新的 Model 对象实例时立即看到一个充满红色框的屏幕,这可能会让他们有些担心。

    【讨论】:

      【解决方案2】:

      在您的示例 XAML 代码中,我没有看到任何绑定。我假设 TextBox 的 Text 属性绑定到 FirstName?

      其次:FirstName是先初始化的,还是TextBox检索时返回null?绑定到 null 值总是会产生奇怪的行为...

      尝试在属性中这样说:

         public string FirstName
          {
              get
              {
                  if (_firstName == null)
                  {
                      _firstName = String.Empty;
                  }
                  return _firstName;
              }
              set
              {
                  if (String.IsNullOrEmpty(value))
                      throw new ApplicationException("FirstName cannot be null or empty!");
                  _firstName = value;
      
                  OnPropertyChanged("FirstName");
              }
          }
      

      【讨论】:

      • 如,您可以看到 TextBox 绑定到新的 Customer 对象的更新代码。当我在 TextBox 和 Tab 中没有输入任何内容时,没有任何反应。它应该触发验证,但它不会!触发验证的唯一方法是,如果我在 Window.Loaded 事件中手动分配 TextBox 的 .Text 属性。
      • 是的,我明白了。而当 FirstName 被调用时,它是返回 String 还是 null 值?
      【解决方案3】:

      我认为您只需要指定 UpdateSourceTrigger 就可以了(注意:我通常还添加 ValidatesOnDataErrors=true):

      【讨论】:

        【解决方案4】:

        不必在开始时设置所有值,或者使用 if 语句,您可以最初设置一个私有变量:

        private string _FirstName = String.Empty;
        public string FirstName
        {
            get { return _FirstName; }
            set
            {
                // Probably check for null and handle it here first
                _FirstName = value;
                OnPropertyChanged("FirstName");
            }
         }
        

        【讨论】:

          【解决方案5】:
          猜你喜欢
          • 2018-03-18
          • 1970-01-01
          • 1970-01-01
          • 2019-08-29
          • 2010-12-02
          • 2014-02-04
          • 1970-01-01
          • 1970-01-01
          • 2011-12-15
          相关资源
          最近更新 更多