WPF TextBox 添加范围验证
添加范围验证,若出现范围错误添加信息捕捉
使用到技术:使用ValidationRules实现范围验证,当范围出现错误时,可以通过触发器Validation.HasError=True设置自定义错误样式。
使用Behavior技术捕捉所有验证出错的消息,用于检查界面是否出错,实现行为捕获。
Behavior使用NuGet获取Microsoft.Xaml.Behaviors.Wpf
Mvvm使用NuGet获取CommunityToolkit.Mvvm
DI使用NuGet获取Microsoft.Extensions.DependencyInjection
界面库NuGet获取WeDraft.Toolkit.FirstDraft
第一步:正常MVVM模式定义View和ViewModel
View
<UserControl x:Class="Forwarding.Views.One2ManyView"
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:local="clr-namespace:Forwarding.Views"
mc:Ignorable="d"
DataContext="{Binding One2Many, Source={StaticResource Locator}}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Margin="20">
<StackPanel>
<TextBlock Text="0~10000以内的加法(a和b的取值范围在[0,10000])" />
<WrapPanel Margin="0 5">
<TextBlock Text="a:" Width="50" TextAlignment="Right"/>
<TextBox Text="{Binding Param1}" MinWidth="50" TextAlignment="Right"/>
</WrapPanel>
<WrapPanel>
<TextBlock Text="b:" Width="50" TextAlignment="Right"/>
<TextBox Text="{Binding Param2}" MinWidth="50" TextAlignment="Right"/>
</WrapPanel>
<Border HorizontalAlignment="Stretch" Height="1" Background="Brown" Margin="0 5"/>
<WrapPanel>
<Button Content="a+b=" Width="50" Command="{Binding CalCommand}"/>
<TextBox Text="{Binding Result}" MinWidth="50" TextAlignment="Right"/>
</WrapPanel>
</StackPanel>
</Grid>
</UserControl>
ViewModel
public class One2ManyViewModel : ObservableObject
{
private int param2;
public int Param2
{
get { return param2; }
set { SetProperty(ref param2, value); }
}
private int param1;
public int Param1
{
get { return param1; }
set { SetProperty(ref param1, value); }
}
private int result;
public int Result
{
get { return result; }
set { SetProperty(ref result, value); }
}
public RelayCommand CalCommand => new RelayCommand(() =>
{
Result = Param1 + Param2;
});
}
DI
App.xaml
<Application x:Class="Forwarding.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Forwarding"
xmlns:di="clr-namespace:Forwarding.Data"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/FirstDraft;component/Themes/Ui.xaml"/>
</ResourceDictionary.MergedDictionaries>
<di:ServiceLocator x:Key="Locator"/>
</ResourceDictionary>
</Application.Resources>
</Application>
App.xaml.cs
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
Services = ConfigureServices();
this.InitializeComponent();
}
/// <summary>
/// Gets the current <see cref="App"/> instance in use
/// </summary>
public new static App Current => (App)Application.Current;
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
/// </summary>
public IServiceProvider Services { get; }
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<One2ManyViewModel>();
return services.BuildServiceProvider();
}
}
ServiceLocator
internal class ServiceLocator
{
public One2ManyViewModel One2Many => App.Current.Services.GetService<One2ManyViewModel>();
}
第二步 添加最大最小值范围验证
定义范围验证类
public class RangeValidationRule : ValidationRule
{
private int min;
private int max;
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 val = 0;
var strVal = (string)value;
try
{
if (strVal.Length > 0)
{
if (strVal.EndsWith("."))
{
return CheckRanges(strVal.Replace(".", ""));
}
// Allow dot character to move to next box
return CheckRanges(strVal);
}
}
catch (Exception e)
{
return new ValidationResult(false, "Illegal characters or " + e.Message);
}
if ((val < Min) || (val > Max))
{
return new ValidationResult(false,
"Please enter the value in the range: " + Min + " - " + Max + ".");
}
else
{
return ValidationResult.ValidResult;
}
}
private ValidationResult CheckRanges(string strVal)
{
if (int.TryParse(strVal, out var res))
{
if ((res < Min) || (res > Max))
{
return new ValidationResult(false,
"Please enter the value in the range: " + Min + " - " + Max + ".");
}
else
{
return ValidationResult.ValidResult;
}
}
else
{
return new ValidationResult(false, "Illegal characters entered");
}
}
}
TextBox使用范围验证
<TextBox MinWidth="50" TextAlignment="Right">
<TextBox.Text>
<Binding Path="Param1" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RangeValidationRule Min="0" Max="10000"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
定义TextBox错误时界面显示
<Style TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<TextBlock Margin="1,2"
DockPanel.Dock="Right"
FontWeight="Bold"
Foreground="Red"
Text="" />
<AdornedElementPlaceholder />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<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="BorderBrush" Value="Red" />
<Setter Property="Background" Value="Red" />
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
添加验证后的View
<UserControl x:Class="Forwarding.Views.One2ManyView"
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:local="clr-namespace:Forwarding.Views"
mc:Ignorable="d"
DataContext="{Binding One2Many, Source={StaticResource Locator}}"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<TextBlock Margin="1,2"
DockPanel.Dock="Right"
FontSize="14"
FontWeight="Bold"
Foreground="Red"
Text="" />
<AdornedElementPlaceholder />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<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="BorderBrush" Value="Red" />
<Setter Property="Background" Value="Red" />
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid Margin="20">
<StackPanel>
<TextBlock Text="0~10000以内的加法(a和b的取值范围在[0,10000])" />
<WrapPanel Margin="0 5">
<TextBlock Text="a:" Width="50" TextAlignment="Right"/>
<TextBox MinWidth="50" TextAlignment="Right">
<TextBox.Text>
<Binding Path="Param1" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RangeValidationRule Min="0" Max="10000"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</WrapPanel>
<WrapPanel>
<TextBlock Text="b:" Width="50" TextAlignment="Right"/>
<TextBox MinWidth="50" TextAlignment="Right">
<TextBox.Text>
<Binding Path="Param2" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RangeValidationRule Min="0" Max="10000"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</WrapPanel>
<Border HorizontalAlignment="Stretch" Height="1" Background="Brown" Margin="0 5"/>
<WrapPanel>
<Button Content="a+b=" Width="50" Command="{Binding CalCommand}"/>
<TextBox Text="{Binding Result}" MinWidth="50" TextAlignment="Right"/>
</WrapPanel>
</StackPanel>
</Grid>
</UserControl>
第三步 引入Behavior对错误进行捕捉
NuGet 引用 项目 Microsoft.Xaml.Behaviors.Wpf
定义行为捕捉类
public interface ITextValidationError
{
ObservableCollection<string> Errors { get; set; }
}
public class TextValidationErrorBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
// 事件监听
this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(OnValidationError));
}
private void OnValidationError(Object sender, ValidationErrorEventArgs e)
{
if (AssociatedObject.DataContext is ITextValidationError mainModel)
{
// 表示新产生的行为
if (e.Action == ValidationErrorEventAction.Added)
{
mainModel.Errors.Add(e.Error.ErrorContent.ToString());
}
else if (e.Action == ValidationErrorEventAction.Removed) // 该行为被移除,即代表验证通过
{
mainModel.Errors.Remove(e.Error.ErrorContent.ToString());
}
}
}
protected override void OnDetaching()
{
// 移除事件监听
this.AssociatedObject.RemoveHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(OnValidationError));
}
}
注意:接口ITextValidationError用于定义ViewModel获取错误消息的接口,示例使用DataContext进行传递。
ViewModel中对ITextValidationError接口实现并捕获错误
public class One2ManyViewModel : ObservableObject, ITextValidationError
{
private int param2;
public int Param2
{
get { return param2; }
set { SetProperty(ref param2, value); }
}
private int param1;
public int Param1
{
get { return param1; }
set { SetProperty(ref param1, value); }
}
private int result;
public int Result
{
get { return result; }
set { SetProperty(ref result, value); }
}
private string errorMsg = string.Empty;
public string ErrorMsg
{
get { return errorMsg; }
set { SetProperty(ref errorMsg, value); }
}
public RelayCommand CalCommand => new RelayCommand(() =>
{
ErrorMsg = "";
if (Errors.Count > 0)
{
ErrorMsg = "参数错误,原因:" + Errors[0];
return;
}
Result = Param1 + Param2;
});
public ObservableCollection<string> Errors { get; set; } = new ObservableCollection<string>();
}
View中添加使用行为捕捉
引入命名空间:xmlns:b=“http://schemas.microsoft.com/xaml/behaviors”
使用行为
<b:Interaction.Behaviors>
<local:TextValidationErrorBehavior />
</b:Interaction.Behaviors>
View完整代码
<UserControl x:Class="Forwarding.Views.One2ManyView"
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:local="clr-namespace:Forwarding.Views"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
DataContext="{Binding One2Many, Source={StaticResource Locator}}"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<TextBlock Margin="1,2"
DockPanel.Dock="Right"
FontSize="14"
FontWeight="Bold"
Foreground="Red"
Text="" />
<AdornedElementPlaceholder />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<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="BorderBrush" Value="Red" />
<Setter Property="Background" Value="Red" />
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<b:Interaction.Behaviors>
<local:TextValidationErrorBehavior />
</b:Interaction.Behaviors>
<Grid Margin="20">
<StackPanel>
<TextBlock Text="0~10000以内的加法(a和b的取值范围在[0,10000])" />
<WrapPanel Margin="0 5">
<TextBlock Text="a:" Width="50" TextAlignment="Right"/>
<TextBox MinWidth="50" TextAlignment="Right">
<TextBox.Text>
<Binding Path="Param1" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RangeValidationRule Min="0" Max="10000"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</WrapPanel>
<WrapPanel>
<TextBlock Text="b:" Width="50" TextAlignment="Right"/>
<TextBox MinWidth="50" TextAlignment="Right">
<TextBox.Text>
<Binding Path="Param2" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RangeValidationRule Min="0" Max="10000"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</WrapPanel>
<Border HorizontalAlignment="Stretch" Height="1" Background="Brown" Margin="0 5"/>
<WrapPanel>
<Button Content="a+b=" Width="50" Command="{Binding CalCommand}"/>
<TextBox Text="{Binding Result}" MinWidth="50" TextAlignment="Right"/>
<TextBlock Text="{Binding ErrorMsg}" Foreground="Red"/>
</WrapPanel>
</StackPanel>
</Grid>
</UserControl>
积跬步以至千里:) (:一阵没来由的风