C# 实现WPF文本框的验证
我有3个文本框(C# 实现WPF文本框的验证,c#,wpf,validation,xaml,data-binding,C#,Wpf,Validation,Xaml,Data Binding,我有3个文本框(Id1、Name和Salary)Id和Salary应包含整数,Name应仅包含字符。我需要验证我的文本框,它应该显示错误,因为我输入了错误的字符或整数。也可以只在Xaml中完成,而不使用codebehind吗?请帮我输入所需的代码 这是Xaml代码: <TextBox Name="tb1" HorizontalAlignment="Left" Height="20" Margin="60,10,0,0" TextWrapping="NoWrap" Text="{Bindin
Id1
、Name
和Salary
)Id
和Salary
应包含整数,Name
应仅包含字符。我需要验证我的文本框,它应该显示错误,因为我输入了错误的字符或整数。也可以只在Xaml中完成,而不使用codebehind吗?请帮我输入所需的代码
这是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"/>
要仅使用XAML完成,您需要为各个属性添加验证规则。但我建议您使用代码隐藏方法。 在代码中,在属性设置器中定义规范,并在不符合规范时抛出异常。 并使用错误模板在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>
您还可以在视图模型中实现
IDataErrorInfo
,如下所示。如果实现了IDataErrorInfo
,则可以在其中执行验证,而不是特定属性的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;
}
}
有三种方法可以实现验证:
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");
}
}
}
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
}
XAML:
非常重要:不要忘记设置validateOnTargetUpdated=“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>
XAML:
代码:
说明:
类别:
友好名称:
超时:
是手册:
我已经实现了此验证。但是你会被代码隐藏使用。这太简单了
XAML:
对于名称有效性,仅输入A-Z和A-Z中的字符
<TextBox x:Name="first_name_texbox" PreviewTextInput="first_name_texbox_PreviewTextInput" > </TextBox>
对于薪资和ID验证,将regex构造函数传递的值替换为[0-9]+
。这意味着您只能输入从1到无限的数字
您还可以使用[0-9]{1,4}
定义长度。这意味着您只能输入小于或等于4位数的数字。这个巴拉克的意思是{至少,多少}。通过这样做,您可以在文本框中定义数字的范围
愿它对其他人有所帮助
XAML:
代码隐藏。
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 !");
}
}
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 !");
}
}
当谈到穆罕默德·迈赫迪的答案时,最好是:
private void salary_texbox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex ( "[^0-9]+" );
if(regex.IsMatch(e.Text))
{
MessageBox.Show("Error");
}
}
因为与TextCompositionEventArgs比较时,它也会得到最后一个字符,而与textbox比较时,它不会得到最后一个字符。对于文本框,错误将在下一个插入字符后显示。当涉及灾难的答案时,我注意到了一种不同的行为。Text显示用户输入新字符之前文本框中的文本,而e.Text显示用户刚刚输入的单个字符。一个挑战是该字符可能不会附加到末尾,但会插入到carret位置:
private void salary_texbox_PreviewTextInput(object sender, TextCompositionEventArgs e){
Regex regex = new Regex ( "[^0-9]+" );
string text;
if (textBox.CaretIndex==textBox.Text.Length) {
text = textBox.Text + e.Text;
} else {
text = textBox.Text.Substring(0, textBox.CaretIndex) + e.Text + textBox.Text.Substring(textBox.CaretIndex);
}
if(regex.IsMatch(text)){
MessageBox.Show("Error");
}
}
当我需要这样做时,我使用Binding.ValidationRules跟随微软的例子,它第一次起作用 请参阅他们的文章,如何:实现绑定验证:
我在上找到了与您的需求相关的SO,并指向codeproject Article您是否使用MVVM格式?我的猜测是否定的。@philip Gullick我没有使用MVVM格式接受答案,TS?-1使用异常进行验证可能太慢了。不是一个好的练习慢下来的方法?我们在这里处理的是用户界面。我不认为实例化异常的额外工作会对这里的性能造成影响。我知道这已经有一年了,但我同意w Mattias。异常是捕获某些类型的输入无效的唯一方法之一,并在MS文档中显式使用。另外,这是UI,单输入验证,而不是整个对象。我知道这比以前老了一年——但我同意drasto。使用异常进行验证是一个坏习惯,应该尽可能避免。我知道这比验证早了一年,但我同意Mattias的观点。有没有人可以引用微软官方消息来源的一句话,告诉我更喜欢哪种风格?这对我来说很有用,因为它在文本框周围添加了一个红色边框。很酷。唯一的问题是,每次属性发生更改时,它都会对其进行验证。这是惊人的缓慢。我打字时有点耽搁。但是+1代表我所做的need@Nlinscott,我知道已经快两年了,但是如果您更改了
UpdateSourceTrigger
,使其仅在焦点丢失时更新,这会更快。还有例外验证。只需设置ValidatesOnExceptions=“True”
。然后从Setter抛出异常。这是如何工作的?validationRules在代码中只出现一次。Paul McCarthy,您只需选择1个选项。取决于您的框架版本,但最好是INotifyDataErrorInfo-来自最新的.NET版本。@先生B:在您的NumericValidationRule示例中,您在XAML中引用“这里有些地方添加了您的名称空间:类似于此,我注意到e.Text不包含您所说的完整文本,只包含用户刚刚输入的字符。看看我的答案,我必须做什么,它真的有效。现在,我想知道我必须做些什么才能使您的解决方案对我有效,这更简单。欢迎链接到解决方案,但请确保您的答案在没有它的情况下是有用的:这样您的其他用户就会知道它是什么以及它为什么存在,然后引用你链接到的页面最相关的部分,以防目标页面不可用。
<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>
<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 !");
}
}
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 !");
}
}
private void salary_texbox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex ( "[^0-9]+" );
if(regex.IsMatch(e.Text))
{
MessageBox.Show("Error");
}
}
private void salary_texbox_PreviewTextInput(object sender, TextCompositionEventArgs e){
Regex regex = new Regex ( "[^0-9]+" );
string text;
if (textBox.CaretIndex==textBox.Text.Length) {
text = textBox.Text + e.Text;
} else {
text = textBox.Text.Substring(0, textBox.CaretIndex) + e.Text + textBox.Text.Substring(textBox.CaretIndex);
}
if(regex.IsMatch(text)){
MessageBox.Show("Error");
}
}