UWP: x: привязка и проверка данных для числового поля
Я немного борюсь с UWP, x:Bind
и проверка данных. У меня очень простой вариант использования: я хочу, чтобы пользователь ввел int
в TextBox
и отобразить номер в TextBlock
как только пользователь покидает TextBox
, Я могу установить InputScope="Number"
для TextBox
, но это не мешает тому, кто печатает с клавиатуры, вводить альфа-символ (или вставлять что-то). Проблема в том, когда я связываю поле с Mode=TwoWay
кажется, что вы не можете предотвратить System.ArgumentException
если связываемое вами поле объявлено как int
, Я хотел проверить в 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
, все хорошо. Но если я наберу нечисловое значение (скажем, "d") (оно даже не достигает точки останова в первой скобке set
метод для MyFieldToValidate
):
Есть ли лучшая практика делать то, что я хочу делать? Самое простое решение - это, во-первых, запретить пользователю вводить другой символ, кроме числового, но я искал часы, не находя простого способа... Другим решением будет проверка данных при выходе из поля, но я не нашел что-то актуальное для UWP и x:Bind
(Несколько вещей для WPF, но они не могут быть воспроизведены с помощью UWP). Спасибо!
1 ответ
Как сказал @RTDev, ваше исключение вызвано тем, что система не может преобразовать строку в int.
Вы можете создать класс, который позволит вам преобразовывать формат ваших данных между источником и целью путем наследования от IValueConverter.
Вы всегда должны реализовывать Convert(Object, TypeName, Object, String) с функциональной реализацией, но довольно часто реализуется ConvertBack(Object, TypeName, Object, String), чтобы он сообщал о неосуществленном исключении. Вам нужен только метод ConvertBack(Object, TypeName, Object, String) в конвертере, если вы используете конвертер для двусторонних привязок или XAML для сериализации.
Для получения дополнительной информации см. Интерфейс IValueConverter.
Например:
<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:
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;
}
}
}
Если вы не хотите, чтобы пользователи вводили буквенно-цифровые символы, я думаю, что наиболее элегантным решением будет создание нового класса NumberBox, который наследуется от класса 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 добавьте пространство имен для ссылки на пространство имен, в котором находится ваш класс NumberBox, а затем замените 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>