WPF文本框validationC#

我有3个文本框( Id1NameSalary )。 IdSalary应该包含整数, Name只能包含字符。 我需要validation我的文本框,它应该显示错误,因为我input错误的字符或整数。 也可以这样做只有在Xaml没有代码隐藏? 我是新来的Wpf和validation请帮我所需的代码

这是Xaml代码:

 <TextBox Name="tb1" HorizontalAlignment="Left" Height="20" Margin="60,10,0,0" TextWrapping="NoWrap" Text="{Binding SelectedItem.Id,ElementName=dgsample}" VerticalAlignment="Top" Width="100" /> <TextBox Name="tb2" HorizontalAlignment="Left" Height="20" Margin="60,60,0,0" TextWrapping="NoWrap" Text="{Binding SelectedItem.Name, ElementName=dgsample}" VerticalAlignment="Top" Width="100"/> <TextBox Name="tb3" HorizontalAlignment="Left" Height="20" Margin="60,110,0,0" TextWrapping="NoWrap" Text="{Binding SelectedItem.Salary, ElementName=dgsample}" VerticalAlignment="Top" Width="100"/> 

有三种方法来实现validation:

  1. validation规则
  2. INotifyDataErrorInfo的实现
  3. 实现IDataErrorInfo

validation规则示例

  public class NumericValidationRule : ValidationRule { public Type ValidationType { get; set; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { string strValue = Convert.ToString(value); if (string.IsNullOrEmpty(strValue)) return new ValidationResult(false, $"Value cannot be coverted to string."); bool canConvert = false; switch (ValidationType.Name) { case "Boolean": bool boolVal = false; canConvert = bool.TryParse(strValue, out boolVal); return canConvert ? new ValidationResult(true, null) : new ValidationResult(false, $"Input should be type of boolean"); case "Int32": int intVal = 0; canConvert = int.TryParse(strValue, out intVal); return canConvert ? new ValidationResult(true, null) : new ValidationResult(false, $"Input should be type of Int32"); case "Double": double doubleVal = 0; canConvert = double.TryParse(strValue, out doubleVal); return canConvert ? new ValidationResult(true, null) : new ValidationResult(false, $"Input should be type of Double"); case "Int64": long longVal = 0; canConvert = long.TryParse(strValue, out longVal); return canConvert ? new ValidationResult(true, null) : new ValidationResult(false, $"Input should be type of Int64"); default: throw new InvalidCastException($"{ValidationType.Name} is not supported"); } } } 

XAML:

非常重要 :不要忘记设置ValidatesOnTargetUpdated="True" ,如果没有这个定义,它将无法正常工作。

  <TextBox x:Name="Int32Holder" IsReadOnly="{Binding IsChecked,ElementName=CheckBoxEditModeController,Converter={converters:BooleanInvertConverter}}" Style="{StaticResource ValidationAwareTextBoxStyle}" VerticalAlignment="Center"> <!--Text="{Binding Converter={cnv:TypeConverter}, ConverterParameter='Int32', Path=ValueToEdit.Value, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"--> <TextBox.Text> <Binding Path="Name" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" Converter="{cnv:TypeConverter}" ConverterParameter="Int32" ValidatesOnNotifyDataErrors="True" ValidatesOnDataErrors="True" NotifyOnValidationError="True"> <Binding.ValidationRules> <validationRules:NumericValidationRule ValidationType="{x:Type system:Int32}" ValidatesOnTargetUpdated="True" /> </Binding.ValidationRules> </Binding> </TextBox.Text> <!--NumericValidationRule--> </TextBox> 

INotifyDataErrorInfo示例

  public abstract class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo { #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged([CallerMemberName] string propertyName = null) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } ValidateAsync(); } #endregion public virtual void OnLoaded() { } #region INotifyDataErrorInfo private ConcurrentDictionary<string, List<string>> _errors = new ConcurrentDictionary<string, List<string>>(); public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; public void OnErrorsChanged(string propertyName) { var handler = ErrorsChanged; if (handler != null) handler(this, new DataErrorsChangedEventArgs(propertyName)); } public IEnumerable GetErrors(string propertyName) { List<string> errorsForName; _errors.TryGetValue(propertyName, out errorsForName); return errorsForName; } public bool HasErrors { get { return _errors.Any(kv => kv.Value != null && kv.Value.Count > 0); } } public Task ValidateAsync() { return Task.Run(() => Validate()); } private object _lock = new object(); public void Validate() { lock (_lock) { var validationContext = new ValidationContext(this, null, null); var validationResults = new List<ValidationResult>(); Validator.TryValidateObject(this, validationContext, validationResults, true); foreach (var kv in _errors.ToList()) { if (validationResults.All(r => r.MemberNames.All(m => m != kv.Key))) { List<string> outLi; _errors.TryRemove(kv.Key, out outLi); OnErrorsChanged(kv.Key); } } var q = from r in validationResults from m in r.MemberNames group r by m into g select g; foreach (var prop in q) { var messages = prop.Select(r => r.ErrorMessage).ToList(); if (_errors.ContainsKey(prop.Key)) { List<string> outLi; _errors.TryRemove(prop.Key, out outLi); } _errors.TryAdd(prop.Key, messages); OnErrorsChanged(prop.Key); } } } #endregion } 

查看模型实现:

  public class MainFeedViewModel : BaseViewModel//, IDataErrorInfo { private ObservableCollection<FeedItemViewModel> _feedItems; [XmlIgnore] public ObservableCollection<FeedItemViewModel> FeedItems { get { return _feedItems; } set { _feedItems = value; OnPropertyChanged("FeedItems"); } } [XmlIgnore] public ObservableCollection<FeedItemViewModel> FilteredFeedItems { get { if (SearchText == null) return _feedItems; return new ObservableCollection<FeedItemViewModel>(_feedItems.Where(x => x.Title.ToUpper().Contains(SearchText.ToUpper()))); } } private string _title; [Required] [StringLength(20)] //[CustomNameValidationRegularExpression(5, 20)] [CustomNameValidationAttribute(3, 20)] public string Title { get { return _title; } set { _title = value; OnPropertyChanged("Title"); } } private string _url; [Required] [StringLength(200)] [Url] //[CustomValidation(typeof(MainFeedViewModel), "UrlValidation")] /// <summary> /// Validation of URL should be with custom method like the one that implemented below, or with /// </summary> public string Url { get { return _url; } set { _url = value; OnPropertyChanged("Url"); } } public MainFeedViewModel(string url, string title) { Title = title; Url = url; } /// <summary> /// /// </summary> public MainFeedViewModel() { } public MainFeedViewModel(ObservableCollection<FeedItemViewModel> feeds) { _feedItems = feeds; } private string _searchText; [XmlIgnore] public string SearchText { get { return _searchText; } set { _searchText = value; OnPropertyChanged("SearchText"); OnPropertyChanged("FilteredFeedItems"); } } #region Data validation local /// <summary> /// Custom URL validation method /// </summary> /// <param name="obj"></param> /// <param name="context"></param> /// <returns></returns> public static ValidationResult UrlValidation(object obj, ValidationContext context) { var vm = (MainFeedViewModel)context.ObjectInstance; if (!Uri.IsWellFormedUriString(vm.Url, UriKind.Absolute)) { return new ValidationResult("URL should be in valid format", new List<string> { "Url" }); } return ValidationResult.Success; } #endregion } 

XAML:

 <UserControl x:Class="RssReaderTool.Views.AddNewFeedDialogView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <FrameworkElement.Resources> <Style TargetType="{x:Type TextBox}"> <Setter Property="Validation.ErrorTemplate"> <Setter.Value> <ControlTemplate x:Name="TextErrorTemplate"> <DockPanel LastChildFill="True"> <AdornedElementPlaceholder> <Border BorderBrush="Red" BorderThickness="2" /> </AdornedElementPlaceholder> <TextBlock FontSize="20" Foreground="Red">*?*</TextBlock> </DockPanel> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="ToolTip" Value="{Binding RelativeSource= {x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"></Setter> </Trigger> </Style.Triggers> </Style> <!--<Style TargetType="{x:Type 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>--> </FrameworkElement.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="5" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="5" /> <RowDefinition Height="Auto" /> <RowDefinition Height="5" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Text="Feed Name" ToolTip="Display" /> <TextBox Text="{Binding MainFeedViewModel.Title,UpdateSourceTrigger=PropertyChanged,ValidatesOnNotifyDataErrors=True,ValidatesOnDataErrors=True}" Grid.Column="2" /> <TextBlock Text="Feed Url" Grid.Row="2" /> <TextBox Text="{Binding MainFeedViewModel.Url,UpdateSourceTrigger=PropertyChanged,ValidatesOnNotifyDataErrors=True,ValidatesOnDataErrors=True}" Grid.Column="2" Grid.Row="2" /> </Grid> </UserControl> 

IDataErrorInfo

查看模型:

 public class OperationViewModel : ViewModelBase, IDataErrorInfo { private const int ConstCodeMinValue = 1; private readonly IEventAggregator _eventAggregator; private OperationInfoDefinition _operation; private readonly IEntityFilterer _contextFilterer; private OperationDescriptionViewModel _description; public long Code { get { return _operation.Code; } set { if (SetProperty(value, _operation.Code, o => _operation.Code = o)) { UpdateDescription(); } } } public string Description { get { return _operation.Description; } set { if (SetProperty(value, _operation.Description, o => _operation.Description = o)) { UpdateDescription(); } } } public string FriendlyName { get { return _operation.FriendlyName; } set { if (SetProperty(value, _operation.FriendlyName, o => _operation.FriendlyName = o)) { UpdateDescription(); } } } public int Timeout { get { return _operation.Timeout; } set { if (SetProperty(value, _operation.Timeout, o => _operation.Timeout = o)) { UpdateDescription(); } } } public string Category { get { return _operation.Category; } set { if (SetProperty(value, _operation.Category, o => _operation.Category = o)) { UpdateDescription(); } } } public bool IsManual { get { return _operation.IsManual; } set { if (SetProperty(value, _operation.IsManual, o => _operation.IsManual = o)) { UpdateDescription(); } } } void UpdateDescription() { //some code } #region Validation #region IDataErrorInfo public ValidationResult Validate() { return ValidationService.Instance.ValidateNumber(Code, ConstCodeMinValue, long.MaxValue); } public string this[string columnName] { get { var validation = ValidationService.Instance.ValidateNumber(Code, ConstCodeMinValue, long.MaxValue); return validation.IsValid ? null : validation.ErrorContent.ToString(); } } public string Error { get { var result = Validate(); return result.IsValid ? null : result.ErrorContent.ToString(); } } #endregion #endregion } 

XAML:

 <controls:NewDefinitionControl x:Class="DiagnosticsDashboard.EntityData.Operations.Views.NewOperationView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:views="clr-namespace:DiagnosticsDashboard.EntityData.Operations.Views" xmlns:controls="clr-namespace:DiagnosticsDashboard.Core.Controls;assembly=DiagnosticsDashboard.Core" xmlns:c="clr-namespace:DiagnosticsDashboard.Core.Validation;assembly=DiagnosticsDashboard.Core" mc:Ignorable="d"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="40" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Label Grid.Column="0" Grid.Row="0" Margin="5">Code:</Label> <Label Grid.Column="0" Grid.Row="1" Margin="5">Description:</Label> <Label Grid.Column="0" Grid.Row="2" Margin="5">Category:</Label> <Label Grid.Column="0" Grid.Row="3" Margin="5">Friendly Name:</Label> <Label Grid.Column="0" Grid.Row="4" Margin="5">Timeout:</Label> <Label Grid.Column="0" Grid.Row="5" Margin="5">Is Manual:</Label> <TextBox Grid.Column="1" Text="{Binding Code,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}" Grid.Row="0" Margin="5"/> <TextBox Grid.Column="1" Grid.Row="1" Margin="5" Text="{Binding Description}" /> <TextBox Grid.Column="1" Grid.Row="2" Margin="5" Text="{Binding Category}" /> <TextBox Grid.Column="1" Grid.Row="3" Margin="5" Text="{Binding FriendlyName}" /> <TextBox Grid.Column="1" Grid.Row="4" Margin="5" Text="{Binding Timeout}" /> <CheckBox Grid.Column="1" Grid.Row="5" Margin="5" IsChecked="{Binding IsManual}" VerticalAlignment="Center" /> </Grid> </controls:NewDefinitionControl> 

您还可以在视图模型中按如下方式实现IDataErrorInfo 。 如果你实现了IDataErrorInfo ,你可以在那里做validation,而不是一个特定属性的setter,那么只要有错误,就返回一个错误信息,以便出错的文本框在其周围有一个红色的框,错误。

 class ViewModel : INotifyPropertyChanged, IDataErrorInfo { private string m_Name = "Type Here"; public ViewModel() { } public string Name { get { return m_Name; } set { if (m_Name != value) { m_Name = value; OnPropertyChanged("Name"); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public string Error { get { return "...."; } } /// <summary> /// Will be called for each and every property when ever its value is changed /// </summary> /// <param name="columnName">Name of the property whose value is changed</param> /// <returns></returns> public string this[string columnName] { get { return Validate(columnName); } } private string Validate(string propertyName) { // Return error message if there is error on else return empty or null string string validationMessage = string.Empty; switch (propertyName) { case "Name": // property name // TODO: Check validiation condition validationMessage = "Error"; break; } return validationMessage; } } 

而且您必须在XAML中设置ValidatesOnDataErrors=True ,才能按如下方式调用IDataErrorInfo的方法:

 <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" /> 

要仅通过XAML来完成,您需要为各个属性添加validation规则。 但我会build议你去后面的代码方法。 在您的代码中,在属性设置器中定义您的规范,并在不符合规范时抛出exception。 并使用错误模板在UI中向用户显示错误。 您的XAML将如下所示

 <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <Style x:Key="CustomTextBoxTextStyle" TargetType="TextBox"> <Setter Property="Foreground" Value="Green" /> <Setter Property="MaxLength" Value="40" /> <Setter Property="Width" Value="392" /> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Trigger.Setters> <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/> <Setter Property="Background" Value="Red"/> </Trigger.Setters> </Trigger> </Style.Triggers> </Style> </Window.Resources> <Grid> <TextBox Name="tb2" Height="30" Width="400" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}" Style="{StaticResource CustomTextBoxTextStyle}"/> </Grid> 

代码背后:

 public partial class MainWindow : Window { private ExampleViewModel m_ViewModel; public MainWindow() { InitializeComponent(); m_ViewModel = new ExampleViewModel(); DataContext = m_ViewModel; } } public class ExampleViewModel : INotifyPropertyChanged { private string m_Name = "Type Here"; public ExampleViewModel() { } public string Name { get { return m_Name; } set { if (String.IsNullOrEmpty(value)) { throw new Exception("Name can not be empty."); } if (value.Length > 12) { throw new Exception("name can not be longer than 12 charectors"); } if (m_Name != value) { m_Name = value; OnPropertyChanged("Name"); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } 

我已经实现了这个validation。 但是你会使用后面的代码。 这太容易和最简单的方法。

XAML:对于名称validation,只能input来自AZ和az的字符。

 <TextBox x:Name="first_name_texbox" PreviewTextInput="first_name_texbox_PreviewTextInput" > </TextBox> 

代码在后面。

 private void first_name_texbox_PreviewTextInput ( object sender, TextCompositionEventArgs e ) { Regex regex = new Regex ( "[^a-zA-Z]+" ); if ( regex.IsMatch ( first_name_texbox.Text ) ) { MessageBox.Show("Invalid Input !"); } } 

对于工资和IDvalidation,将正则expression式构造函数传递值replace为[0-9]+ 。 这意味着你只能input从1到无限的数字。

您也可以用[0-9]{1,4}定义长度。 这意味着你只能inputless于或等于4位数字。 这个baracket意味着{至less,多less个数字}。 通过这样做,您可以在文本框中定义数字的范围。

愿它帮助别人

XAML:

代码在后面。

 private void salary_texbox_PreviewTextInput ( object sender, TextCompositionEventArgs e ) { Regex regex = new Regex ( "[^0-9]+" ); if ( regex.IsMatch ( salary_texbox.Text ) ) { MessageBox.Show("Invalid Input !"); } } 

XAML:

 <TextBox x:Name="t_size" HorizontalAlignment="Left" TextChanged="int_box_validation" Height="23" Margin="122,31,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/> 

代码隐藏:

 private void int_box_validation(object sender,RoutedEventArgs e) { TextBox _target = sender as TextBox; Regex regex = new Regex("^[0-9]?$"); if(!regex.IsMatch(_target.Text)) { MessageBox.Show("only nums!"); _target.Text = null; } } 

那么当你把不合法的价值放在combobox中时,它会告诉你。