我查看了一些有关验证规则的帖子,但是我没遇到过我遇到的问题。
我在WPF窗口中使用文本框的验证规则。我有两个检查,一个用于空文本框,另一个用于使用RegEx匹配的无效字符。
我的问题是:
在我的文本框中:
反过来也是如此。
打开窗口
我的代码如下:
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>
非常感谢您的帮助。
此致
尼尔
答案 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而不是:
创建一个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的逻辑会回到这里,所以找到重用该代码的方法可能是个好主意。