验证规则未使用2个验证规则正确更新

时间:2013-02-20 15:56:33

标签: c# wpf validation multidatatrigger

我查看了一些有关验证规则的帖子,但是我没遇到过我遇到的问题。

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

我的问题是:

在我的文本框中:

  1. A型 - 工程,显示无所事事
  2. 为空字符串点击Backspace - Works,显示验证错误消息"请在所有字段中输入值"
  3. 类型! - 不起作用 - 它应显示"找到无效字符",但仍显示"请在所有字段中输入值。"
  4. Backspace to empty string-技术上有效,因为它仍然显示第一个错误"请在所有字段中输入值。"
  5. 类型A - 工作,没有错误消息
  6. 类型! - 很好,显示消息"找到无效字符。
  7. 反过来也是如此。

    打开窗口

    1. 类型! - 精细,显示"找到无效字符。
    2. 退格到空字符串 - 仍显示"找到无效字符"而不是"请在所有字段中输入值。
    3. 我的代码如下:

      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" />
      

      私有代码 - 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();
          }
      }
      

      提供者类

      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和Binding到文本框。

      当我第一次显示Window时,按钮是Enabled,我不想要它,因为这可以让用户点击带有空文本框的保存。

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

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

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

      我可以通过使用

      强制检查验证规则来说明Load事件
      var expression = txtBoxProviderName.GetBindingExpression(TextBox.TextProperty);
      if (expression != null)
          expression.UpdateSource();
      

      但是当Window首次打开时,它会立即显示验证错误消息&#34;请在所有字段中输入一个值&#34;,我不太喜欢它的外观。

      无论如何,或者我都不能充分利用这两个世界。

      这是按钮代码

      <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的MVVM示例,它实现了一个RelayCommand:ICommand类。

      这是对的吗?

      代码是:

      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
          }
      

      说实话,我不确定在&#39; if(!string.IsNullOrEmpty(ProviderName))&#39;

      中的块中应该编码什么

      另外你说要在DataContext中添加ICommand代码,所以不确定它是否在正确的位置因为当我打开窗口时,启用了Save并且单击它什么都不做,即使所有字段都有正确的数据它

      这是按钮

      的ProviderNew xaml代码
      <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>
      

      非常感谢您的帮助。

      此致

      尼尔

1 个答案:

答案 0 :(得分:2)

好的,我设法重现了这个问题,并且它让我觉得它无法正常工作。但我想通了。
问题是当错误发生变化时,转换器不会被触发,因为实际属性(ProviderName)不会因ValidationRule而改变。如果在转换器,ValidationRule和Property-setter中放置断点,则可以看到此行为。

因此,您应该将IValueConverter的绑定更改为以下内容,而不是使用tbNameValidation

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

重要部分是 Path=(Validation.Errors).CurrentItem.ErrorContent" 。 这将确保显示当前错误消息。

此外,如果您希望从头开始显示“请输入所有字段中的值。”消息,则应将Mode=TwoWay添加到{{1}的绑定中}:

txtBoxProviderName

编辑第2部分

要修复按钮的启用属性,您可以使用与问题中相同的代码,但不是使用 <Binding ElementName="This" Path="ProviderName" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay"> 事件来运行保存代码,而是可以使用{{1而不是:

在DataContext中

创建一个Click类型的属性,并将按钮绑定到:

Command

并且CanSave实施可以检查是否满足所有期望:

ICommand

<Button Command="{Binding SaveCommand}" .... /> private ICommand saveCommand; public ICommand SaveCommand { get { if (saveCommand == null) { saveCommand = new RelayCommand(p => DoSave(), p=> CanSave() ); } return saveCommand; } } 将确定按钮的状态,因为CommandBinding将根据有界命令的private bool CanSave(){ if (!string.IsNullOrEmpty(ProviderName)){ //.... etc } } 方法自动启用按钮。你的Validator的逻辑会回到这里,所以找到重用该代码的方法可能是个好主意。

有关Relaying Commands

的更多信息
相关问题