我有这个非常简单的课程,我们称之为客户。 它看起来像这样:
namespace TestValidation
{
class Customer
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
if (String.IsNullOrEmpty(value))
{
throw new Exception("Customer name is mandatory.");
}
}
}
}
}
现在,我已经创建了一个基本表单,用户可以在其中将客户添加到数据库中。该表单包含简单的TextBox,绑定到Customer的Name属性,以及一个“Add”按钮。
XAML代码是:
<Window x:Class="TestValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestValidation"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Margin="119,86,107,194" Name="CustomerName"
Text="{Binding Path=Customer.Name,
ValidatesOnExceptions=True,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged,
NotifyOnValidationError=True}"
/>
<Button Content="Add" HorizontalAlignment="Left" Margin="204,176,0,0" VerticalAlignment="Top" Width="74"/>
</Grid>
</Window>
从Name属性的setter中,您可以理解该名称对我来说是必需的,因此如果Name TextBox留空,我希望验证事件上升。通过WPF的验证规则 - 一旦用户将焦点移出文本框,并且那里没有值 - 它应该将边框颜色更改为红色。出于某种原因 - 这没有发生,我也不知道为什么。我的过程有什么问题?
现在,我已经阅读了很多关于WPF验证的好文章(如Enforcing Complex Business Data Rules with WPF,Data validation in WPF和Windows Presentation Foundation中的验证),但它们都没有帮助我解决我的问题。
最终,我希望表单看起来像Brian Noyes中第一个链接上的优秀文章(没有10个学分,所以我无法附上照片......对不起)。
如果有人能向我解释它是如何运作的,我将不胜感激。
重要提示 - 我正在使用.Net framework 4,因此我需要一个适合此版本的解决方案。
答案 0 :(得分:31)
我肯定会建议使用IDataErrorInfo进行WPF验证,因为WPF已经了解如何使用它,并且易于实现。
首先,将接口添加到包含要验证的数据的类中。所需的方法可能如下所示:
public class Customer : IDataErrorInfo
{
...
#region IDataErrorInfo Members
string IDataErrorInfo.Error
{
get { return null; }
}
string IDataErrorInfo.this[string columnName]
{
get
{
if (columnName == "Name")
{
// Validate property and return a string if there is an error
if (string.IsNullOrEmpty(Name))
return "Name is Required";
}
// If there's no error, null gets returned
return null;
}
}
#endregion
}
接下来,您需要在TextBox绑定中设置ValidatesOnDataErrors=True
,以便在Name
属性更改时运行验证:
<TextBox Text="{Binding Path=Customer.Name, ValidatesOnDataErrors=True}" ... />
最后,在XAML中创建一个验证模板,告诉WPF如何绘制验证错误。这是我经常使用的样式/模板:
<!-- ValidatingControl Style -->
<Style TargetType="{x:Type FrameworkElement}" x:Key="ValidatingControl">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding
Path=(Validation.Errors)[0].ErrorContent,
RelativeSource={x:Static RelativeSource.Self}}" />
</Trigger>
</Style.Triggers>
</Style>
另外,请确保您的Customer
类实现INotifyPropertyChanged
,以便它正确响应UI更新。我没有在你的代码中看到这一点,但为了简单起见,人们常常将其留下:)
答案 1 :(得分:2)
您未指定验证规则。在保留控件之前将调用验证规则,然后可以执行您想要验证输入的任何内容。
一个简单的例子 - 我想这就是你想要做的 - 提供here。
答案 2 :(得分:2)
使用IDataErrorInfo进行验证。这个link会对你有帮助。
答案 3 :(得分:0)
我认为问题可能是您的类没有实现INotifyPropertyChanged,因此不会像您期望的那样绑定。
实现INotifyPropertyChanged接口,在属性发生变化时引发事件并且它应该有效。
请参阅http://msdn.microsoft.com/en-us/library/ms743695(v=vs.110).aspx了解演示。
答案 4 :(得分:0)
<Binding Path=Name UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
http://msdn.microsoft.com/en-us/library/ms752347%28v=vs.110%29.aspx#what_is_data_binding
请使用此博客:prasadcsharp.blogspot.com
答案 5 :(得分:0)
我知道这则帖子很旧,但这对我来说还是不错的。没有延迟或长时间编码,但是我只在双精度值上使用了它。您可以根据需要进行更改。
private void search_box_TextChanged(object sender, TextChangedEventArgs e)
{
// box text and background to normal state if user types numbers
search_box.Foreground = Brushes.Black;
search_box.Background = Brushes.White;
if (search_id.IsSelected == true)
{
try
{
//convert while user is typing
if (string.IsNullOrEmpty(search_box.Text)==false)
Convert.ToDouble(search_box.Text);
search_error.Text = null;
}
//if user types a letter or a space or a symbol ====>
catch (Exception)
{
// user cant type any value other than numbers as exception prevents it and clears the box text value <======
search_box.Text = null;
search_box.Foreground = Brushes.White;
search_box.Background = Brushes.Red;
search_error.Text="id is numberic value";
}
}
}
希望有帮助。
答案 6 :(得分:0)
1)当您使用异常进行验证时,我建议在将值分配给属性后备字段之前抛出异常,因此您拒绝它,并且数据对象(在这种情况下为Customer
对象)将仅包含有效数据:
using System;
namespace TestValidation
{
public class Customer
{
private string _name;
public string Name
{
get => this._name;
set
{
if (String.IsNullOrEmpty(value))
throw new ArgumentException("Customer name is mandatory.", nameof(Name));
_name = value;
}
}
}
}
2)默认情况下,WPF数据绑定引擎将忽略数据objetc的setter过程中引发的异常。您已将ValidatesOnExceptions
正确设置为true
,以指示数据绑定系统对异常做出反应。但是,您在UpdateSourceTrigger
上设置了PropertyChanged
,因此只有当Target属性({目标元素(Name
)的{1}}已更改。如果您从一个空的Customer
开始,然后依次跳入标签,然后再移开,则Text
属性尚未更改,因此将不会触发Source属性(名称)的更新(此操作即使将TextBox
作为UpdateSourceTrigger模式也会发生)。您可以在构造函数或TextBox
事件处理程序中将Text
属性初始化为LostFocus
或Text
,将其更正。这样,一旦呈现窗口,该文本框将显示为红色边框。如果您将null
设置为String.Empty
(这是Loaded
的{{1}}属性的默认设置),则UpdateSourceTrigger
最初会出现错误,但是如果您标签进入和退出,它将以预期的红色边框突出显示。
注意:所有这些工作都是有效的,因为LostFocus
的{{1}}属性使用TextBox
作为默认绑定模式,因此数据可以从目标到源。
Text
TextBox
3)在这种情况下,不需要INotifyPropertyChanged,因为您只想通过用户在TextBox中的交互来更改Source属性(名称)的值,而无需与其他属性一起修改Name属性C#代码。实现INotifyPropertyChanged的目的是为了通知WPF数据绑定系统有关数据对象的更改,以便WPF可以更新用户界面中的数据(由于代码过程而更改Source时,请更新Target)。
答案 7 :(得分:0)
您没有实现INotifyPropertyChanged。
另外,请注意IDataErrorInfo和INotifyDataErrorInfo..。如果要将验证逻辑移出设置器,将会用到它们。
还需要承认,在现代应用程序中,最好将验证逻辑移到单独的类型中。 (请参阅fluentValidation)
using System;
namespace TestValidation
{
public class Customer : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => this._name;
set
{
if(_name == value) return;
if (String.IsNullOrEmpty(value))
throw new ArgumentException("Customer name is mandatory.", nameof(Name));
_name = value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
// TODO: Impelemnt interface INotifyPropertyChanged
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
#endregion
}
}
答案 8 :(得分:-1)
<ControlTemplate x:Key="TextBoxErrorTemplate">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Image Height="16" Margin="0,0,5,0"
Source="Assets/warning_48.png"/>
<AdornedElementPlaceholder x:Name="Holder"/>
</StackPanel>
<Label Foreground="Red" Content="{Binding ElementName=Holder,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
</StackPanel>
</ControlTemplate>
<TextBox x:Name="Box"
Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}">
<TextBox.Text>
<Binding Path="ValueInBox" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ValidationExpamples:DoubleRangeRule Min="0.5" Max="10"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
IntRangeRule类:
public class IntRangeRule : ValidationRule
{
private int _min;
private int _max;
public IntRangeRule()
{
}
public int Min
{
get { return _min; }
set { _min = value; }
}
public int Max
{
get { return _max; }
set { _max = value; }
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
int l_input = 0;
try
{
if (((string)value).Length > 0)
{
l_input = Int32.Parse((String)value);
}
}
catch (Exception e)
{
return new ValidationResult(false, "Illegal characters or " + e.Message);
}
if ((l_input < Min) || (l_input > Max))
{
return new ValidationResult(false, "Please enter an value in the range: " + Min + " - " + Max + ".");
}
return new ValidationResult(true, null);
}
}