C# XAML-IValueConverter和行为相互冲突,导致无休止的循环发生

C# XAML-IValueConverter和行为相互冲突,导致无休止的循环发生,c#,xaml,xamarin,xamarin.forms,C#,Xaml,Xamarin,Xamarin.forms,对于Xamarin表单应用程序,我有一个IValueConverter和行为,它们相互冲突,导致无休止的循环发生。我已经创建了一个简单的应用程序来演示这个问题,可以下载(下面的链接),并在下面包含了相关代码 以下是我在这个场景中试图实现的需求 用户必须能够为int输入空值 用户只允许输入整数值 对于#1,我在后端模型中使用可为null的int。如果我只使用一个“int”,那么如果该字段被清除,它将始终以一个“0”结尾。因此,IValueConverter实现StringToIntConverte

对于Xamarin表单应用程序,我有一个IValueConverter和行为,它们相互冲突,导致无休止的循环发生。我已经创建了一个简单的应用程序来演示这个问题,可以下载(下面的链接),并在下面包含了相关代码

以下是我在这个场景中试图实现的需求

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

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

    对于正常输入,它工作得很好。但如果你从一个“0”开始,然后输入另一个数字,它就会失控,最终进入一个无休止的循环。我已经在各种XF版本以及iOS和Android平台上验证了这一点

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

    复制步骤
  • 运行下面github repo中的演示
  • 在输入框中输入'05',应用程序将在无休止的循环中冻结
  • 复制链接


    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);
            }
        }
    }
    
    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;
    }
    
    公共类MainPageModel:FreshBasePageModel
    {
    公共主页模型()
    {
    模型=新模型();
    }
    公共模型模型{get;set;}
    }
    公共类模型:INotifyPropertyChanged
    {
    私有整数长度;
    公共整数长度
    {
    获取{return_length;}
    set{SetProperty(ref _length,value);}
    }
    受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
    {
    PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
    }
    受保护的bool SetProperty(ref T storage,T value,[CallerMemberName]string propertyName=null)
    {
    if(EqualityComparer.Default.Equals(存储,值))
    {
    返回false;
    }
    储存=价值;
    OnPropertyChanged(propertyName);
    返回true;
    }
    公共事件属性更改事件处理程序属性更改;
    }
    
    在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);
                    }
                }
    
    我把你的密码改成

    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类IntegerValidationBehavior:Behavior
    {
    受保护的覆盖无效附加到(条目)
    {
    entry.TextChanged+=OnEntryTextChanged;
    基本数据(条目);
    }
    受保护的覆盖无效OnDetachingFrom(条目)
    {
    entry.TextChanged-=OnEntryTextChanged;
    base.OnDetachingFrom(条目);
    }
    私有静态void OnEntryTextChanged(对象发送方,textchangedventargs args)
    {
    如果(args.NewTextValue!=null)
    {
    //确保所有字符都是数字
    var isValid=int.TryParse(args.NewTextValue,out 2;);
    如果(!isValid)
    ((条目)sender.Text=args.OldTextValue;/=isValid?args.NewTextValue:args.NewTextValue.Remove(args.NewTextValue.Length-1);
    }
    }
    }
    

    无止境的循环消失了。

    谢谢,这样更好。但是一旦属性有了值,就不能再将其设为null。为了进行测试,我输入了'02',它变为'2',但是我不能将2倒出来,使字段为空。
    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);
            }
        }
    }