验证规则无法正确更新2验证规则

问题描述:

我查看了一些有关验证规则的帖子,但没有遇到我遇到的问题。验证规则无法正确更新2验证规则

我对WPF窗口中的文本框使用验证规则。我有两个检查,一个用于空文本框,另一个用于使用RegEx匹配的无效字符。

我的问题是这样的:

在我的文本框:

  1. A型 - 作品,显示空话
  2. 命中退格为空字符串 - 作品,显示验证错误消息“请输入一个值在所有领域“
  3. 类型! - 不起作用 - 应显示“找到无效字符”,但仍显示“请在所有字段中输入值”。
  4. 退格为空字符串 - 技术上可行,因为它仍然显示第一个错误“请在所有字段中输入值。”
  5. Type A - Works,没有错误信息
  6. Type! - 精细,显示消息 “被发现无效字符

同样的情况倒过来

打开窗口

  1. 类型 - !好吧,显示” 无效字符被发现。 。
  2. Backspace键空字符串 - 仍显示 “无效字符被发现”,而不是“请在所有字段中输入一个值

我的代码如下:

ProviderNew.xaml:

<Label Name="lblProviderName" Content="Provider Name: " 
        Grid.Row="1" Grid.Column="0"/> 

<TextBox Name="txtBoxProviderName" Margin="2" 
    MaxLength="20" CharacterCasing="Upper" 
    Grid.Row="1" Grid.Column="1" LostFocus="TxtBoxProviderNameLostFocus" TextChanged="TxtBoxProviderNameTextChanged"> 
       <TextBox.Text> 
        <Binding ElementName="This" Path="ProviderName" UpdateSourceTrigger="PropertyChanged"> 
         <Binding.ValidationRules> 
          <Domain:ProviderValidation /> 
         </Binding.ValidationRules> 
        </Binding> 
       </TextBox.Text> 
      </TextBox> 

      <TextBlock x:Name="tbNameValidation" Foreground="Red" FontWeight="Bold" Margin="10,0,0,0" 
         Text="{Binding ElementName=txtBoxProviderName, Path=(Validation.Errors), Converter={StaticResource eToMConverter}}" 
         Grid.Row="1" Grid.Column="2" /> 

Privder后面的代码 - ProviderNew.xaml.cs

public static readonly DependencyProperty NewProviderNameProperty = DependencyProperty.Register("ProviderName", 
     typeof (string), typeof(ProviderNew), new UIPropertyMetadata("")); 


    public string ProviderName 
    { 
     get { return (string) GetValue(NewProviderNameProperty); } 
     set { SetValue(NewProviderNameProperty, value); } 
    } 

值转换器类

public class ErrorsToMessageConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     var sb = new StringBuilder(); 
     var errors = value as ReadOnlyCollection<ValidationError>; 

     if (errors != null && errors.Count > 0) 
     { 

      foreach (var e in errors.Where(e => e.ErrorContent != null)) 
      { 
       sb.AppendLine(e.ErrorContent.ToString()); 
      } 
     } 
     return sb.ToString(); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Provider类

private string _providerName; 
    public string ProviderName 
    { 
     get { return _providerName; } 
     set 
     { 
      _providerName = value; 
      RaisePropertyChanged("ProviderName"); 
     } 
    } 

private void RaisePropertyChanged(string propertyName) 
    { 
     PropertyChangedEventHandler handler = this.PropertyChanged; 

     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

验证规则类

public class ProviderValidation : ValidationRule 
{ 
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) 
    { 
     var str = value as string; 

     if (str != null && !Regex.IsMatch(str, @"^[a-zA-Z0-9]*$")) 
     { 
      return new ValidationResult(false, "Invalid characters were found."); 
     } 


     if (String.IsNullOrEmpty(str)) 
     { 
      return new ValidationResult(false, "Please enter a value in all fields."); 
     } 

     return new ValidationResult(true, null); 
    } 
} 

我已经试过被同时设置引发LostFocus和框TextChanged事件使用强制更新:

var expression = txtBoxProviderName.GetBindingExpression(TextBox.TextProperty); 
if (expression != null) 
    expression.UpdateSource(); 

在Validate方法上设置断点表明完成了正确的匹配并返回了正确的ValidationResult,但它不能正确更新文本。

我在做一件令人难以置信的傻事吗?

任何建议,将不胜感激。

编辑1.

是啊,我有工作,使用MultiDataTrigger并绑定到文本框。

什么是不工作是当我第一次显示窗口,按钮是启用,我不希望它是因为这可以允许用户单击保存与空文本框。

窗口打开时,验证规则不会从一开始就直接工作。

我将焦点放在文本框上,如果输入焦点或输入的数据不正确,那么验证规则会启动并禁用该按钮。

将按钮设置为默认禁用,使其在打开时禁用,但在没有验证错误时不启用。

我可以把它通过强制验证规则的检查上说Load事件工作,可使用

var expression = txtBoxProviderName.GetBindingExpression(TextBox.TextProperty); 
if (expression != null) 
    expression.UpdateSource(); 

但是当窗口第一次打开时,它会立即显示验证错误消息“请输入所有领域的价值“,我不太喜欢它的外观。

任何方式,或我可以不是两全其美。

这里是按钮的代码

<Button x:Name="btnSave" Height="25" Width="100" Content="Save" HorizontalAlignment="Right" Margin="0,0,120,0" 
      Grid.Row="2" Click="BtnSaveClick" > 
     <Button.Style> 
      <Style TargetType="{x:Type Button}"> 
       <Setter Property="IsEnabled" Value="False" /> 
       <Style.Triggers> 
        <MultiDataTrigger> 
         <MultiDataTrigger.Conditions> 
          <Condition Binding="{Binding ElementName=txtBoxProviderName, Path=(Validation.HasError)}" Value="False" /> 
          <Condition Binding="{Binding ElementName=txtBoxHelpDesk, Path=(Validation.HasError)}" Value="False" /> 
          <Condition Binding="{Binding ElementName=txtBoxRechargeInstructions, Path=(Validation.HasError)}" Value="False" /> 
         </MultiDataTrigger.Conditions> 
         <Setter Property="IsEnabled" Value="True" /> 
        </MultiDataTrigger> 
       </Style.Triggers> 
      </Style> 
     </Button.Style>  
    </Button> 

感谢,

奥尼尔

编辑2

一个简单的问题。我找不到RelayCommand命名空间。搜索其他代码我从Microsoft发现了一个实现RelayCommand:ICommand类的MVVM示例。

这是正确的吗?

的代码是:

public class RelayCommand : ICommand 
{ 
    #region Fields 

    readonly Action<object> _execute; 
    readonly Predicate<object> _canExecute; 

    #endregion // Fields 

    #region Constructors 

    /// <summary> 
    /// Creates a new command that can always execute. 
    /// </summary> 
    /// <param name="execute">The execution logic.</param> 
    public RelayCommand(Action<object> execute) 
     : this(execute, null) 
    { 
    } 

    /// <summary> 
    /// Creates a new command. 
    /// </summary> 
    /// <param name="execute">The execution logic.</param> 
    /// <param name="canExecute">The execution status logic.</param> 
    public RelayCommand(Action<object> execute, Predicate<object> canExecute) 
    { 
     if (execute == null) 
      throw new ArgumentNullException("execute"); 

     _execute = execute; 
     _canExecute = canExecute; 
    } 

    #endregion // Constructors 

    #region ICommand Members 

    [DebuggerStepThrough] 
    public bool CanExecute(object parameter) 
    { 
     return _canExecute == null ? true : _canExecute(parameter); 
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    public void Execute(object parameter) 
    { 
     _execute(parameter); 
    } 

    #endregion // ICommand Members 
} 

我实现我的ProviderNew.xaml.cs如下:

private ICommand saveCommand; 
    public ICommand SaveCommand 
    { 
     get 
     { 
      if (saveCommand == null) 
      { 
       saveCommand = new RelayCommand(p => DoSave(), p => CanSave()); 
      } 
      return saveCommand; 
     } 
    } 

    private bool CanSave() 
    { 
     if (!string.IsNullOrEmpty(ProviderName)) 
     { 
      ??? What goes here? 
     } 


     return true; 
    } 
private bool DoSave() 
    { 
     // Save the information to DB 
    } 

说实话我不确定什么应该在块的编码'if(!string.IsNullOrEmpty(ProviderName))'

另外你说要在DataContext中添加ICommand代码,所以不知道它是否在正确的位置,因为当我打开窗口时,Save是enab le并点击它什么也不做,即使所有的字段都有正确的数据。

下面是按钮

<Button x:Name="btnSave" Height="25" Width="100" Content="Save" HorizontalAlignment="Right" Margin="0,0,120,0" 
      Grid.Row="2" Command="{Binding SaveCommand}" > 
     <Button.Style> 
      <Style TargetType="{x:Type Button}"> 
       <Setter Property="IsEnabled" Value="False" /> 
       <Style.Triggers> 
        <MultiDataTrigger> 
         <MultiDataTrigger.Conditions> 
          <Condition Binding="{Binding ElementName=txtBoxHelpDesk, Path=(Validation.HasError)}" Value="False" /> 
          <Condition Binding="{Binding ElementName=txtBoxProviderName, Path=(Validation.HasError)}" Value="False" /> 
          <Condition Binding="{Binding ElementName=txtBoxRechargeInstructions, Path=(Validation.HasError)}" Value="False" /> 
         </MultiDataTrigger.Conditions> 
         <Setter Property="IsEnabled" Value="True" /> 
        </MultiDataTrigger> 
       </Style.Triggers> 
      </Style> 
     </Button.Style>  
    </Button> 

你的帮助是非常赞赏的ProviderNew XAML代码。

问候,

奥尼尔

+0

存在同样的问题,如果任何人有这个问题 – 2016-06-03 11:02:57

好,我设法重现此问题,这是窃听我,这是行不通的。但我知道了。
的问题是,当误差的变化,因为实际的财产(的ProviderName)不因有效性规则改变转换器没有被解雇。如果您在Converter,ValidationRule和Property-setter中放置断点,则可以看到此行为。

因此而不是使用IValueConverter,你应该改变tbNameValidation结合以下几点:

<TextBlock x:Name="tbNameValidation" 
      Text="{Binding ElementName=txtBoxProviderName, 
        Path=(Validation.Errors).CurrentItem.ErrorContent}"> 

Path=(Validation.Errors).CurrentItem.ErrorContent"的重要组成部分。 这将确保所显示的当前的错误信息。

另外,如果你想的“在各个领域请输入值”的消息从一开始就表明,你应该添加Mode=TwoWaytxtBoxProviderName绑定:

<Binding ElementName="This" 
      Path="ProviderName" 
      UpdateSourceTrigger="PropertyChanged" 
      Mode="TwoWay"> 

编辑第2部分

要修复该按钮的启用属性,可以使用与您的问题中相同的代码,但不使用Click事件来运行保存代码,可以使用Command属性来代替:

在你的DataContext

,创建类型ICommand的属性和按钮绑定到这一点:

<Button Command="{Binding SaveCommand}" .... /> 

private ICommand saveCommand; 
public ICommand SaveCommand { 
    get { 
     if (saveCommand == null) { 
      saveCommand = new RelayCommand(p => DoSave(), p=> CanSave()); 
     } 
     return saveCommand; 
     } 
} 

和CanSave实现可以检查是否所有期望得到满足:

private bool CanSave(){ 
    if (!string.IsNullOrEmpty(ProviderName)){ 
     //.... etc 
    } 
} 

CanSave()的决定你按钮的状态,这是因为会的CommandBinding自动采取根据界命令的CanExecute方法使按钮的照顾。尽管你的验证器的逻辑将会回到这里,所以找到一种重用代码的方法可能是一个好主意。与IDataErrorInfo的验证,而不是有效性规则

更多有关Relaying Commands

+0

太谢谢你了。它现在很好用。你已经回答了第二个问题。从一开始就使规则验证工作,因为我有一个保存按钮,如果规则验证失败,则不启用。 Mode =“TwoWay”然而并不这样做。无论如何不是双向的默认模式? – Neill 2013-02-21 10:16:42

+0

你可以使用'Validation.HasError'属性来控制Save(保存)按钮的状态,无论是通过'Binding'或通过'Trigger' – RoelF 2013-02-21 10:53:19

+0

请参阅编辑1上后,过短,这里的所有信息。谢谢 – Neill 2013-02-21 12:20:08