Xamarin入口控制TextChanged事件循环

时间:2018-06-15 14:53:38

标签: c# android xamarin xamarin.forms logic

在我的表单上,我有3个entry控件。我正在尝试使用以下验证规则来验证“年龄”控件:

  • 无法输入超过3位

  • 无法输入小数位(。)

  • 无法输入连字符( - )

为此,我将控件的'TextChanged'属性设置为

TextChanged="OnAgeTextChanged"

我的OnAgeTextChanged方法是:

 private void OnAgeTextChanged(object sender, TextChangedEventArgs e)
    {
       var entry = (Entry)sender;

        try
        {

           if (entry.Text.Length > 3)
            {
               string entryText = entry.Text;

               entry.TextChanged -= OnAgeTextChanged;

                entry.Text = e.OldTextValue;
                entry.TextChanged += OnAgeTextChanged;
            }

            string strName = entry.Text;

            if (strName.Contains(".") || strName.Contains("-"))
            {
                strName = strName.Replace(".", "").Replace("-", "");
                entry.Text = strName;
            }
        }

        catch(Exception ex)
        {
            Console.WriteLine("Exception caught: {0}", ex);
        }

    }

但是,当满足if条件时,事件将多次循环,导致应用程序运行缓慢。

例如,如果我输入我的年龄为1234,它会多次循环代码,因此会有延迟,每次文本更改时延迟都会增加。

我可以通过其他方式实现此验证,但不会多次调用该事件?

修改

更新代码以删除控件上的TextChanged触发器,然后在方法结束时重新分配它时,它仍会循环多次,并且每次按键时循环次数都会增加。

进入控制xaml

<Entry x:Name="txtAge"
       Placeholder="Age"
       Keyboard="Numeric"
       TextColor="DarkBlue"
       PlaceholderColor="DarkBlue"
       Completed="AgeCompleted"
       HorizontalOptions="Start"
       WidthRequest="55"
       TextChanged="OnAgeTextChanged"
/>

TextChanged事件

 private void OnAgeTextChanged(object sender, TextChangedEventArgs e)
    {

        var entry = (Entry)sender;

        try
        {

            entry.TextChanged -= OnAgeTextChanged;

            if (entry.Text.Length > 3)
            {

                entry.Text = e.OldTextValue;
            }

            string strName = entry.Text;

            if (strName.Contains(".") || strName.Contains("-"))
            {
                strName = strName.Replace(".", "").Replace("-", "");
                entry.Text = strName;
            }
        }

        catch(Exception ex)
        {
            Console.WriteLine("Exception caught: {0}", ex);
        }

        finally
        {

            entry.TextChanged += OnAgeTextChanged;
        }
     }

4 个答案:

答案 0 :(得分:3)

您从事件中收到的object sender似乎与Entry生成的XAML完全相同。

所以,这可以解释这个问题:

  

entry.TextChanged -= OnTextChanged;对您的代码完全没有影响,此对象还没有订阅者

然后,在你的东西结束时,你设置:

  

entry.TextChanged += OnAgeTextChanged;现在确实如此。你永远不朽&#39;这个实例(sender)。

我想知道它是否是一种优雅的方式来解决您的具体问题(以后我会尝试发布一些替代品,如果它有效)但为了让它有效,我想你可以试试要直接取消订阅并直接订阅txtAge对象引用:

private void OnAgeTextChanged(object sender, TextChangedEventArgs e)
{
    try
    {
        txtAge.TextChanged -= OnAgeTextChanged;

        if (e.NewTextValue?.Length > 3 ?? false)
            txtAge.Text = e.OldTextValue;

        string strNumber = txtAge.Text;

        if (strNumber.Contains(".") || strNumber.Contains("-"))
        {
            strNumber = strNumber.Replace(".", "").Replace("-", "");
            txtAge.Text = strNumber;
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine("Exception caught: {0}", ex);
    }
    finally
    {
        txtAge.TextChanged += OnAgeTextChanged;
    }
 }

我希望它有所帮助。

答案 1 :(得分:2)

我最终解决此问题的方法是使用一个单独的类来处理我的验证。

我的验证班:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class viewModel : INotifyPropertyChanged
{
 public event PropertyChangedEventHandler PropertyChanged;

 private string age_;
 public string Age { get { return age_; } set { if (age_ != value) { age_ = ProcessAge(value); OnPropertyChanged(); } } }

 private string ProcessAge(string age)
 {
    if (string.IsNullOrEmpty(age))
        return age;

    if (age.Length > 3)
        age = age.Substring(0, 3);

    if (age.StartsWith("0"))
        age = age.Remove(0, 1);

    return age.Replace(".", "").Replace("-", "");
 }

 private void OnPropertyChanged([CallerMemberName] string propertyName = null)
 {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 }
}

然后,我可以通过以下方式绑定表单以使用此类:

public MainPage()
    {
        InitializeComponent();
        BindingContext = new viewModel();
    }

最后,为了绑定进入控件以使用Age属性,我设置了Text属性

Text="{Binding Age, Mode=TwoWay}"

现在要做的是,每次Age控件中的值更改时,它都会查看新类中的Age属性,并看到要进行设置,它需要通过ProcessAge进行验证,这是现在进行检查的地方。

这是更快的方法,因为它每次按键仅发生一次,订阅和取消订阅TextChanged事件并没有循环的麻烦。

答案 2 :(得分:0)

这可以解决您的问题:

private void OnAgeTextChanged(object sender, TextChangedEventArgs e)
{
   var entry = (Entry)sender;
   entry.TextChanged -= OnAgeTextChanged;
    try
    { //[...] Your stuff
    }

    catch(Exception ex)
    {
        //[...] Your other stuff
    }
    finally{
          entry.TextChanged += OnAgeTextChanged;
    }
}

因为在下面的代码中,您在事件处理程序仍在侦听时更改了文本,因此它将至少再次触发一次。

if (strName.Contains(".") || strName.Contains("-"))
{
    strName = strName.Replace(".", "").Replace("-", "");
    entry.Text = strName;
}

答案 3 :(得分:0)

最好使用条目无焦点事件,这样它就不会对事件处理程序进行如此多的调用,因此性能会得到提升!