XAML - IValueConverter 和行为相互冲突,导致发生无限循环



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

以下是我在此方案中尝试实现的要求。

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

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

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

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

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

重现步骤

  1. 运行下面的 github 存储库中的演示
  2. 在输入
  3. 框中输入"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;
}

将下面的方法e替换为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 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);
}
}
}

无尽的循环消失了。

最新更新