x:bind 和数字字段的数据验证
x:bind and data validation for numeric field
我在 UWP、x:Bind
和数据验证方面遇到了一些困难。
我有一个非常简单的用例:我希望用户在 TextBox
中输入 int
并在用户离开 [=16] 时立即在 TextBlock
中显示数字=].
我可以为 TextBox
设置 InputScope="Number"
,但这不会阻止使用键盘键入字母字符(或粘贴某些内容)的人。
问题是,当我用 Mode=TwoWay
绑定字段时,如果您绑定的字段声明为 似乎无法 阻止 System.ArgumentException
=15=]。如果输入是数字,我想检查 set
方法,但异常发生在这之前。
我的(非常简单的)ViewModel(这里没有模型,我尽量保持简单):
public class MyViewModel : INotifyPropertyChanged
{
private int _MyFieldToValidate;
public int MyFieldToValidate
{
get { return _MyFieldToValidate; }
set
{
this.Set(ref this._MyFieldToValidate, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisedPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
else
{
storage = value;
this.RaisedPropertyChanged(propertyName);
return true;
}
}
}
我背后的代码:
public sealed partial class MainPage : Page
{
public MyViewModel ViewModel { get; set; } = new MyViewModel() { MyFieldToValidate = 0 };
public MainPage()
{
this.InitializeComponent();
}
}
还有我的整个XAML:
<Page
x:Class="SimpleFieldValidation.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SimpleFieldValidation"
xmlns:vm="using:SimpleFieldValidation.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="1" Grid.Column="0" Text="{x:Bind ViewModel.MyFieldToValidate, Mode=TwoWay}" x:Name="inputText" InputScope="Number" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{x:Bind ViewModel.MyFieldToValidate, Mode=OneWay}" x:Name="textToDisplay" />
</Grid>
</Page>
如果我在 TextBox
中键入一个数字字符,一切正常。但是,如果我键入一个 non-numeric 值(比如 "d")(它甚至没有到达 MyFieldToValidate
的 set
方法的第一个括号处的断点):
是否有最佳实践来做我想做的事?最简单的解决方案是首先阻止用户输入除数字以外的其他字符,但我一直在搜索数小时而没有找到简单的方法......另一种解决方案是在离开字段时验证数据,但我没有找到与 UWP 和 x:Bind
相关的东西(WPF 想的东西很少,但它们不能用 UWP 复制)。
谢谢!
如@RTDev所说,你的异常是系统无法将string转int造成的
You can create a class that allows you to convert the format of your data between the source and the target by inheriting from IValueConverter.
You should always implement Convert(Object, TypeName, Object, String) with a functional implementation, but it's fairly common to implement ConvertBack(Object, TypeName, Object, String) so that it reports a not-implemented exception. You only need a ConvertBack(Object, TypeName, Object, String) method in your converter if you are using the converter for two-way bindings, or using XAML for serialization.
有关详细信息,请参阅 IValueConverter Interface。
例如:
<Page.Resources>
<local:IntFormatter x:Key="IntConverter" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="1" Grid.Column="0" Text="{x:Bind ViewModel.MyFieldToValidate, Mode=TwoWay,Converter={StaticResource IntConverter}}" x:Name="inputText" InputScope="Number" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{x:Bind ViewModel.MyFieldToValidate, Mode=OneWay}" x:Name="textToDisplay" />
</Grid>
IntFormatter class:
internal class IntFormatter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value != null)
{
return value.ToString();
}
else
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
int n;
bool isNumeric = int.TryParse(value.ToString(), out n);
if (isNumeric)
{
return n;
}
else
{
return 0;
}
}
}
如果您不希望用户键入字母数字字符,我认为最优雅的解决方案是创建一个新的 class NumberBox 继承自 class InputBox 并重载 OnKeyDown 方法来拦截字母数字击键,像这样:
using Windows.System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
namespace MyProject.Controls
{
public sealed class NumberBox : TextBox
{
protected override void OnKeyDown(KeyRoutedEventArgs e)
{
if (e.Key >= VirtualKey.Number0 && e.Key <= VirtualKey.Number9 ||
e.Key >= VirtualKey.NumberPad0 && e.Key <= VirtualKey.NumberPad9 ||
e.Key >= VirtualKey.Left && e.Key <= VirtualKey.Down ||
e.Key == VirtualKey.Delete ||
e.Key == VirtualKey.Tab ||
e.Key == VirtualKey.Back ||
e.Key == VirtualKey.Enter)
base.OnKeyDown(e);
else
e.Handled = true;
}
}
}
然后在你的XAML中添加一个命名空间来引用你的NumberBoxclass所在的命名空间,然后替换InputBox 与 control:NumberBox,像这样:
<Page
x:Class="MyProject.View.CalibrarEnfoque"
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:local="using:MyProject.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:MyProject.Controls"
mc:Ignorable="d">
<Grid>
<controls:NumberBox Text="{x:Bind ViewModel.MyValue, Mode=TwoWay}"/>
</Grid>
</Page>
我在 UWP、x:Bind
和数据验证方面遇到了一些困难。
我有一个非常简单的用例:我希望用户在 TextBox
中输入 int
并在用户离开 [=16] 时立即在 TextBlock
中显示数字=].
我可以为 TextBox
设置 InputScope="Number"
,但这不会阻止使用键盘键入字母字符(或粘贴某些内容)的人。
问题是,当我用 Mode=TwoWay
绑定字段时,如果您绑定的字段声明为 似乎无法 阻止 System.ArgumentException
=15=]。如果输入是数字,我想检查 set
方法,但异常发生在这之前。
我的(非常简单的)ViewModel(这里没有模型,我尽量保持简单):
public class MyViewModel : INotifyPropertyChanged
{
private int _MyFieldToValidate;
public int MyFieldToValidate
{
get { return _MyFieldToValidate; }
set
{
this.Set(ref this._MyFieldToValidate, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisedPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
else
{
storage = value;
this.RaisedPropertyChanged(propertyName);
return true;
}
}
}
我背后的代码:
public sealed partial class MainPage : Page
{
public MyViewModel ViewModel { get; set; } = new MyViewModel() { MyFieldToValidate = 0 };
public MainPage()
{
this.InitializeComponent();
}
}
还有我的整个XAML:
<Page
x:Class="SimpleFieldValidation.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SimpleFieldValidation"
xmlns:vm="using:SimpleFieldValidation.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="1" Grid.Column="0" Text="{x:Bind ViewModel.MyFieldToValidate, Mode=TwoWay}" x:Name="inputText" InputScope="Number" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{x:Bind ViewModel.MyFieldToValidate, Mode=OneWay}" x:Name="textToDisplay" />
</Grid>
</Page>
如果我在 TextBox
中键入一个数字字符,一切正常。但是,如果我键入一个 non-numeric 值(比如 "d")(它甚至没有到达 MyFieldToValidate
的 set
方法的第一个括号处的断点):
是否有最佳实践来做我想做的事?最简单的解决方案是首先阻止用户输入除数字以外的其他字符,但我一直在搜索数小时而没有找到简单的方法......另一种解决方案是在离开字段时验证数据,但我没有找到与 UWP 和 x:Bind
相关的东西(WPF 想的东西很少,但它们不能用 UWP 复制)。
谢谢!
如@RTDev所说,你的异常是系统无法将string转int造成的
You can create a class that allows you to convert the format of your data between the source and the target by inheriting from IValueConverter.
You should always implement Convert(Object, TypeName, Object, String) with a functional implementation, but it's fairly common to implement ConvertBack(Object, TypeName, Object, String) so that it reports a not-implemented exception. You only need a ConvertBack(Object, TypeName, Object, String) method in your converter if you are using the converter for two-way bindings, or using XAML for serialization.
有关详细信息,请参阅 IValueConverter Interface。
例如:
<Page.Resources>
<local:IntFormatter x:Key="IntConverter" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="*" />
<RowDefinition Height="10*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="1" Grid.Column="0" Text="{x:Bind ViewModel.MyFieldToValidate, Mode=TwoWay,Converter={StaticResource IntConverter}}" x:Name="inputText" InputScope="Number" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{x:Bind ViewModel.MyFieldToValidate, Mode=OneWay}" x:Name="textToDisplay" />
</Grid>
IntFormatter class:
internal class IntFormatter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value != null)
{
return value.ToString();
}
else
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
int n;
bool isNumeric = int.TryParse(value.ToString(), out n);
if (isNumeric)
{
return n;
}
else
{
return 0;
}
}
}
如果您不希望用户键入字母数字字符,我认为最优雅的解决方案是创建一个新的 class NumberBox 继承自 class InputBox 并重载 OnKeyDown 方法来拦截字母数字击键,像这样:
using Windows.System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
namespace MyProject.Controls
{
public sealed class NumberBox : TextBox
{
protected override void OnKeyDown(KeyRoutedEventArgs e)
{
if (e.Key >= VirtualKey.Number0 && e.Key <= VirtualKey.Number9 ||
e.Key >= VirtualKey.NumberPad0 && e.Key <= VirtualKey.NumberPad9 ||
e.Key >= VirtualKey.Left && e.Key <= VirtualKey.Down ||
e.Key == VirtualKey.Delete ||
e.Key == VirtualKey.Tab ||
e.Key == VirtualKey.Back ||
e.Key == VirtualKey.Enter)
base.OnKeyDown(e);
else
e.Handled = true;
}
}
}
然后在你的XAML中添加一个命名空间来引用你的NumberBoxclass所在的命名空间,然后替换InputBox 与 control:NumberBox,像这样:
<Page
x:Class="MyProject.View.CalibrarEnfoque"
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:local="using:MyProject.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:MyProject.Controls"
mc:Ignorable="d">
<Grid>
<controls:NumberBox Text="{x:Bind ViewModel.MyValue, Mode=TwoWay}"/>
</Grid>
</Page>