简体   繁体   中英

Setter of property bound to Entry.Text loops infinitely

This is driving me crazy. I have been looking for the source of the issue for hours, but i am starting to suspect this is not an issue in my logic... Maybe i am wrong.


Issue description

I have a simple Entry . Its Text property is bound to a property with type double in a ViewModel . At the same time i subscribe to the Unfocused Event whose EventHandler simply sets the entry.Text property to "1.0" (actually i can reproduce the issue for x.y0, that is any decimal whose final digit is 0). If now i write in the Entry anything (except "1" or "1." or "1.0"!!!) and leave the Entry (by tapping outside or taping on Done) so that Unfocused is fired, the App becomes unresponsive.

Note: I know that it sounds a bit strange to set entry.Text = 1.0 in the event handler. The truth is that i came across this issue by trying to format the entry.Text value as follows.

if (double.TryParse(entry.Text, out double result))
{
    entry.Text = String.Format("{0:F2}", result);
}

There String.Format tries to round the decimal to two decimals. If i give 6.999 the expected value should be 7.00 , but the App becomes unresponsive instead.


Steps to reproduce the issue

  1. Create blank Xamarin.Forms proyect.
  2. Remove default Label in MainPage.xaml file to include following Entry , instead:
<StackLayout>
    <Entry Text="{Binding Weight}"
    Unfocused="entry_Unfocused"/>
</StackLayout>
  1. In the code behind add the following EventHandler and set the BindingContext property of the page as follows:
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        BindingContext = new viewmodel();
    }

    private void entry_Unfocused(object sender, FocusEventArgs e)
    {
        ((Entry)sender).Text = "1.0";
    }

}
  1. Create the ViewModel like:
public class viewmodel : INotifyPropertyChanged
{
    public viewmodel()
    {
    }

    private double _Weight;
    public double Weight
    {
        get => _Weight;
        set
        {
            if (_Weight != value)
            {
                _Weight = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
  1. Run the App and type anything into the Entry .
  2. Leave the Entry so that Unfocused is fired.

My System configuration:

  • Visual Studio v. 16.3.8
  • Xamarin.Forms 4.2.0.709249
  • Android 8

Could anyone explain what is going on here, and anyway to work around this issue?

i test your codes,and debug it,i found it would infinite loop in

set
    {
        if (_Weight != value)
        {
            _Weight = value;
            OnPropertyChanged();
        }
    }
}

i think it bescause your Weight property is double type,it could not set it as string correctly,you could try to define the Weight as string.

private string _Weight;
public string Weight
    {
        get => _Weight;
        set
        {
            if (_Weight != value)
            {
                _Weight = value;
                OnPropertyChanged();
            }
        }
    }

It's possibly because your unfocused handler sets the entry with the value entered causing a loop.

private void entry_Unfocused(object sender, FocusEventArgs e)
{
    ((Entry)sender).Text = "1.0";
}

I found the source of the Issue!

Thanks to @LeoZhu-MSFT for his insight!

The thing is that when entry.Text = "3.00" is set, this value is apparently parsed to double and then send to my property setter. In fact:

double _Weight = double.Parse("3.00"); // _Weight ends up having value 3!!! 

So entry.Text and _Weight have always different values!!! and the binding tries to set indefinitely _Weight to 3.00 but all _Weight gets in the setter is 3 ...

To solve that, instead of changing my property _Weight to String as suggested by @LeoZhu-MSFT, which is sub-optimal (i still want to perform mathematical operations with my Weight property,), i can change it to decimal !!!

In fact:

decimal _Weight = decimal.Parse("3.00"); // _Weight has now value 3.00. As wanted!!!

So after i change _Weight and Weight type to decimal in my View Model , the infinite-loop-behavior disappeared:D

I think the best way to resolve this problem is to use a Converter

public class DecimalConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is decimal)
                return value.ToString();
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (decimal.TryParse(value as string, out var dec))
                return dec;
            return value;
        }
    }

Usage

<Entry Text="{Binding Weight, Converter={StaticResource DecimalConverter}}" />

that All:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM