XAML - IValueConverter и Behavior конфликтуют друг с другом, вызывая бесконечное l oop - PullRequest
3 голосов
/ 04 февраля 2020

С приложением Xamarin Forms у меня есть IValueConverter и Behavior, которые конфликтуют друг с другом, вызывая бесконечное l oop. Я создал простое приложение, демонстрирующее эту проблему, которое можно загрузить (ссылка ниже), и включил соответствующий код ниже.

Вот требования, которые я пытаюсь выполнить в этом сценарии.

  1. Пользователь должен иметь возможность ввести пустое значение для целого числа.
  2. Пользователю разрешено вводить только целое значение.

Для # 1, Я использую nullable int в бэкэнд-модели. Если бы я использовал только «int», то поле всегда заканчивалось бы «0», если оно было очищено. Итак, реализация IValueConverter StringToIntConverter используется для преобразования значения из строки в int, и если передается пустая строка, для свойства устанавливается значение null.

Для # 2 поведение IntegerValidationBehavior проверяет каждое нажатие клавиши и устраняет любые нецелочисленные значения, включая точки. Кроме того, для этого примера я показываю только цифровую клавиатуру c. Тем не менее, он допускает некоторые нецелые символы, такие как точка, поэтому необходим IntegerValidationBehavior.

Для обычных вводов он отлично работает. Но если вы начинаете с «0», а затем вводите другое число, оно становится беспорядочным и заканчивается бесконечным l oop. Я проверял это на различных версиях XF, а также на платформах iOS и Android.

Как изменить код в соответствии с моими требованиями?

Шаги для воспроизведения

  1. Запустите демонстрацию, как показано в репозитории github ниже
  2. Введите '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;
}

Ответы [ 2 ]

2 голосов
/ 04 февраля 2020

Замените приведенный ниже метод на метод OnEntryTextChanged в файле IntegerValidationBehavior и проверьте, что он работает.

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));

                    if (isValid && args.NewTextValue.Length > 1 && args.NewTextValue.StartsWith("0"))
                        return;

                    ((Entry)sender).Text = isValid ? args.NewTextValue : args.NewTextValue.Remove(args.NewTextValue.Length - 1);
                }
            }
0 голосов
/ 04 февраля 2020

Я изменил ваш код на

public class StringToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
            return null;
        else
            return ((int)value).ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var val = value as string;

        if ( int.TryParse( val, out var result ) )
            return result;
        else
            return null;
    }
}

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 (args.NewTextValue != null)
        {
            //make sure all characters are numbers
            var isValid = int.TryParse( args.NewTextValue, out _ );

            if ( !isValid )
                ((Entry)sender).Text = args.OldTextValue; // = isValid ? args.NewTextValue : args.NewTextValue.Remove(args.NewTextValue.Length - 1);
        }
    }
}

и бесконечный l oop исчез.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...