首页 > 解决方案 > XAML - IValueConverter 和 Behavior 相互冲突,导致发生无限循环

问题描述

使用 Xamarin Forms 应用程序时,我有一个 IValueConverter 和 Behavior,它们相互冲突,导致发生无限循环。我创建了一个简单的应用程序来演示可以下载的这个问题(下面的链接),并且我在下面包含了相关代码。

以下是我试图在这种情况下实现的要求。

  1. 用户必须能够为 int 输入空值。
  2. 用户只能输入一个整数值。

对于#1,我在后端模型中使用了一个可为空的 int。如果我只使用一个“int”,那么如果它被清除,该字段将始终以一个“0”结束。因此,IValueConverter 实现 StringToIntConverter 用于将值从字符串转换为 int,如果传递的是空字符串,则将属性设置为 null。

对于 #2,行为 IntegerValidationBehavior 检查每个击键并消除任何非整数值,包括句点。另外,对于这个例子,我只展示了数字键盘。但是,它允许一些非整数字符,如句点,因此需要 IntegerValidationBehavior。

对于正常输入,它工作得很好。但是,如果您以“0”开头,然后输入另一个数字,它就会变得混乱,最终陷入无限循环。我已经在各种 XF 版本以及 iOS 和 Android 平台上验证了这一点。

我将如何更改代码以满足我的要求?

重现步骤

  1. 运行下面的 github repo 中的演示
  2. 在输入框中输入“05”,应用程序在无限循环中冻结

复制链接

https://github.com/JohnLivermore/SampleXamarinApp/tree/endlessloop


整数验证行为

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

标签: c#xamlxamarinxamarin.forms

解决方案


用 IntegerValidationBehavior 文件中的 OnEntryTextChanged 方法替换下面的方法并检查它是否有效。

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

推荐阅读