С приложением Xamarin Forms у меня есть IValueConverter и Behavior, которые конфликтуют друг с другом, вызывая бесконечное l oop. Я создал простое приложение, демонстрирующее эту проблему, которое можно загрузить (ссылка ниже), и включил соответствующий код ниже.
Вот требования, которые я пытаюсь выполнить в этом сценарии.
- Пользователь должен иметь возможность ввести пустое значение для целого числа.
- Пользователю разрешено вводить только целое значение.
Для # 1, Я использую nullable int в бэкэнд-модели. Если бы я использовал только «int», то поле всегда заканчивалось бы «0», если оно было очищено. Итак, реализация IValueConverter StringToIntConverter используется для преобразования значения из строки в int, и если передается пустая строка, для свойства устанавливается значение null.
Для # 2 поведение IntegerValidationBehavior проверяет каждое нажатие клавиши и устраняет любые нецелочисленные значения, включая точки. Кроме того, для этого примера я показываю только цифровую клавиатуру c. Тем не менее, он допускает некоторые нецелые символы, такие как точка, поэтому необходим IntegerValidationBehavior.
Для обычных вводов он отлично работает. Но если вы начинаете с «0», а затем вводите другое число, оно становится беспорядочным и заканчивается бесконечным l oop. Я проверял это на различных версиях XF, а также на платформах iOS и Android.
Как изменить код в соответствии с моими требованиями?
Шаги для воспроизведения
- Запустите демонстрацию, как показано в репозитории github ниже
- Введите '05' в поле ввода и приложение зависнет в бесконечном l oop
Воспроизведение Ссылка
https://github.com/JohnLivermore/SampleXamarinApp/tree/endlessloop
IntegerValidationBehavior
public class IntegerValidationBehavior : Behavior<Entry>
{
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
private static void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
if (!string.IsNullOrWhiteSpace(args.NewTextValue))
{
//make sure all characters are numbers
var isValid = args.NewTextValue.ToCharArray().All(x => char.IsDigit(x));
((Entry)sender).Text = isValid ? args.NewTextValue : args.NewTextValue.Remove(args.NewTextValue.Length - 1);
}
}
}
StringToIntConverter
public class StringToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return "";
else
return ((int)value).ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var val = value as string;
if (string.IsNullOrWhiteSpace(val))
return null;
else
{
var result = 0;
int.TryParse(val, out result);
return result;
}
}
}
XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:behaviors="clr-namespace:SampleApp"
mc:Ignorable="d"
x:Class="SampleApp.MainPage">
<StackLayout>
<Entry Keyboard="Numeric"
Text="{Binding Model.Length, Mode=TwoWay, Converter={StaticResource StringToInt}}">
<Entry.Behaviors>
<behaviors:IntegerValidationBehavior />
</Entry.Behaviors>
</Entry>
<Label Text="{Binding Model.LengthString}"
TextColor="Black" />
<Button Text="Process"
Command="{Binding Process}" />
</StackLayout>
</ContentPage>
Модель
public class MainPageModel : FreshBasePageModel
{
public MainPageModel()
{
Model = new Model();
}
public Model Model { get; set; }
}
public class Model : INotifyPropertyChanged
{
private int? _length;
public int? Length
{
get { return _length; }
set { SetProperty(ref _length, value); }
}
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
}