【问题标题】:Setter of property bound to Entry.Text loops infinitely绑定到 Entry.Text 的属性设置器无限循环
【发布时间】:2019-11-13 23:49:10
【问题描述】:

这快把我逼疯了。几个小时以来我一直在寻找问题的根源,但我开始怀疑这不是我的逻辑问题......也许我错了。


问题描述

我有一个简单的条目。它的 Text 属性绑定到 ViewModel 中类型为 double 的属性。同时我订阅了Unfocused Event,它的 EventHandler 只是将entry.Text 属性设置为"1.0"(实际上我可以重现 x.y0 的问题,即任何最后一位为 0 的小数)。如果现在我在 Entry 中写入任何内容(“1”或“1.”或“1.0”!!!)并离开 Entry(通过点击外部或录音on Done)以便Unfocused 被触发,App 变得无响应。

注意:我知道在事件处理程序中设置entry.Text = 1.0 听起来有点奇怪。事实是,我通过尝试格式化 entry.Text 值遇到了这个问题,如下所示。

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

String.Format 尝试将小数点四舍五入到小数点后两位。如果我给6.999 预期值应该是7.00,但应用程序 变得无响应。


重现问题的步骤

  1. 创建空白 Xamarin.Forms 项目。
  2. 删除 MainPage.xaml 文件中的默认 Label 以包含以下 Entry,改为:
<StackLayout>
    <Entry Text="{Binding Weight}"
    Unfocused="entry_Unfocused"/>
</StackLayout>
  1. 在后面的代码中添加如下EventHandler,并设置页面的BindingContext属性如下:
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. 像这样创建 ViewModel
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. 运行应用程序并在Entry中输入任何内容。
  2. 离开 Entry 以便 Unfocused 被解雇。

我的系统配置:

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

谁能解释这里发生了什么,无论如何要解决这个问题?

【问题讨论】:

  • 我假设 Unfocus 事件在无限循环中被调用。这应该很容易验证
  • @Deczaloth 在最新版本的 X.Forms (4.4) 和 Android 9 中也会发生同样的事情。

标签: c# xamarin.forms xamarin.android double string.format


【解决方案1】:

我测试你的代码,调试它,我发现它会无限循环

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

我认为这是因为您的Weight 属性是double 类型,它无法正确设置为string,您可以尝试将Weight 定义为字符串。

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

【讨论】:

  • 感谢您的回答!因为你让我朝着正确的方向前进,所以你得到了我的支持。在您的帮助下,我详细阐述了我也发布的更完整的答案。如果您有什么要补充的,请告诉我。谢谢! :D
【解决方案2】:

这可能是因为您的未聚焦处理程序使用输入的值设置条目导致循环。

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

【讨论】:

  • 事实并非如此。该处理程序只被调用一次:/
【解决方案3】:

我找到了问题的根源!

感谢@LeoZhu-MSFT 的洞察!

问题是,当设置entry.Text = "3.00" 时,这个值显然被解析为加倍,然后发送到我的属性设置器。事实上:

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

所以entry.Text_Weight 总是有不同的值!!!并且 binding 尝试将_Weight 无限期设置为3.00 但所有_Weight 进入setter 是3...

为了解决这个问题,不要像 @LeoZhu-MSFT 建议的那样将我的属性 _Weight 更改为 String,这是次优的(我仍然想用我的 Weight 执行数学运算属性!),我可以将其更改为十进制!!!

事实上:

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

所以在我的 View Model 中将 _WeightWeight 类型更改为 decimal 后,infinite-loop-behavior 消失了:D

【讨论】:

    【解决方案4】:

    我认为解决这个问题的最好方法是使用转换器

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

    用法

    &lt;Entry Text="{Binding Weight, Converter={StaticResource DecimalConverter}}" /&gt;

    全部:)

    【讨论】:

      猜你喜欢
      • 2013-04-15
      • 2017-09-10
      • 2013-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多