WPF -如何在单击按钮后验证表单?



我找到了这个简单的示例来验证WPF中的用户输入。它工作得很好,但是验证发生在每次输入更改之后。我希望验证只发生当按钮被单击(提交表单后)。换句话说,我想要与具有required属性的HTTP5输入相同的行为。

我认为,问题是在UpdateSourceTrigger="PropertyChanged",但我不知道,如何将其绑定在按钮上。

MainWindow.xaml

<Window x:Class="B_Validation_ByDataErrorInfo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:B_Validation_ByDataErrorInfo"
mc:Ignorable="d"
Title="Validation By IDataErrorInfo" Height="450" Width="800">
<Window.Resources>
<local:Person x:Key="data"/>
<!--The tool tip for the TextBox to display the validation error message.-->
<Style x:Key="textBoxInError" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Margin="20">
<TextBlock>Enter your age:</TextBlock>
<TextBox Style="{StaticResource textBoxInError}">
<TextBox.Text>
<Binding Path="Age" Source="{StaticResource data}"
ValidatesOnExceptions="True"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<DataErrorValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Button>Submit</Button>
</StackPanel>

Person.cs

public class Person : IDataErrorInfo
{
private int age;
public int Age
{
get { return age; }
set { age = value; }
}
public string Error => null;
public string this[string name]
{
get
{
string result = null;
if (name == "Age")
{
if (this.age < 0 || this.age > 150)
{
result = "Age must not be less than 0 or greater than 150.";
}
}
return result;
}
}
}

DataErrorInfo为旧接口。自。net 4.5以来,我们应该始终实现现代INotifyDataErrorInfo接口(如何添加验证以查看模型属性或如何实现INotifyDataErrorInfo)。

你可以在上面的链接中找到如何实现这个接口的详细信息。它还允许您通过将实际的属性规则(值约束)移动到扩展ValidationRule的专用类来清理代码。

下面的例子展示了如何在属性setter之外控制属性验证。

在实现上面的链接之后,我们将有一个IsPropertyValid方法,我们通常会从属性设置器中调用它:
// Example property, which validates its value before applying it
private string userInput;
public string UserInput
{ 
get => this.userInput; 
set 
{ 
// Validate the value
bool isValueValid = IsPropertyValid(value);
// Optionally reject value if validation has failed
if (isValueValid)
{
this.userInput = value; 
OnPropertyChanged();
}
}
}

上述属性在每次set()调用时验证。要在单击Button时验证整个类型,可以从命令处理程序调用IsPropertyValid方法。

因此,您应该在Person类中引入IComannd(command Overview)。您可以将此命令绑定到Button.Command属性。

下面的示例将SaveCommand添加到Person类。ExecuteSaveCommand是命令处理程序,它将对完整的Person执行属性验证。
为了避免显式地引用每个属性,我们可以使用反射来获得我们需要验证的属性的所有public属性名。

您可以通过访问Microsoft Docs: relay command Logic找到一个可重用命令实现(RelayCommand)的示例。

下面的示例扩展了如何向视图模型属性添加验证或如何实现INotifyDataErrorInfo的示例。它基本上添加了成员TryValidateAllProperties,该成员调用(上述链接的)上述示例的现有IsPropertyValid方法。
下面的示例还显示了如何将您的属性规则提取到自定义的ValidationRule中,您可以从链接中使用该示例(IsPropertyValid方法当前依赖于已定义的ValidationRule实例的Dictionary<string, IList<ValidationRule>>(属性名称映射到验证规则的集合)。

AgeValidationRule.cs

public class AgeValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
=> value is int age && age > 0 && age <= 150 
? ValidationResult.ValidResult 
: new ValidationResult(false, "Age must not be less than 0 or greater than 150.");
}

Person.cs

public class Person : INotifyDataErrorInfo, INotifyPropertyChanged
{
// Maps a property name to a list of ValidationRules that belong to this property
private Dictionary<string, IList<ValidationRule>> ValidationRules { get; }
public ICommand SavePersonCommand => new RelayCommand(ExecuteSavePersonCommand);
private int age;
public int Age
{
get => this.age; 
set => this.age = value; // TODO::Raise INotifyPropertyChangedPropertyChanged event
}       
public Person()
{
this.ValidationRules = new Dictionary<string, IList<ValidationRule>>
{
{ nameof(this.Age), new List<ValidationRule>() { new AgeValidationRule() } }
};
}
private void ExecuteSavePersonCommand(object obj)
{
if (TryValidateAllProperties())
{
// TODO::Save this Person
}
}
private bool TryValidateAllProperties()
{
PropertyInfo[] publicPropertyInfos = GetType().GetProperties(BindingFlags.Public);
foreach (PropertyInfo propertyInfo in publicPropertyInfos)
{
string propertyName = propertyInfo.Name;
object propertyValue = propertyInfo.GetValue(this);
_ = IsPropertyValid(propertyValue, propertyName);
}
return this.HasErrors;
}
}

最新更新