【问题标题】:Xamarin entry control TextChanged event looping roundXamarin 入口控件 TextChanged 事件循环循环
【发布时间】:2018-11-25 10:38:45
【问题描述】:

在我的表单上,我有 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;
        }
     }

【问题讨论】:

  • 那是因为当你设置entry.Text OnAgeTextChanged 会被再次调用。我的建议是将您的验证更改为entry.Unfocused,当用户应该完成编辑文本时,或者您可以在方法开始时取消订阅您对OnAgeTextChanged 的条目,然后在结束时设置所有文本重新订阅方法。
  • @Nick 我将如何做第二个?我知道在 vb.NET 中我可以使用RemoveHandler,但是在 c# 中如何实现呢?
  • 您已经在方法中将 entry.TextChanged -= OnAgeTextChanged; 移动到 try 的开头,并且可能在 catch 之后添加 finally 并将 entry.TextChanged += OnAgeTextChanged; 移动到那里。
  • @Nick 嗯,照你说的做之后还是会循环多次
  • 老实说,我会删除事件取消订阅和订阅事件('entry.TextChanged += OnAgeTextChanged;'),它们不会像您认为的那样工作,无论如何在这种情况下都不会。

标签: c# android xamarin xamarin.forms logic


【解决方案1】:

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

我的验证类:

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】:

    您从事件中收到的object sender 看起来与您的XAML 生成的Entry 实例并不完全相同。

    所以,这可以解释问题:

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

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

    entry.TextChanged += OnAgeTextChanged; 现在可以了。您正在“永生”这个实例 (sender)。

    我想知道这是否是解决您的特定问题的一种优雅方式(稍后我会尝试发布一些替代方案,如果它有效)但为了使其有效,我想您可以尝试取消订阅并通过 @ 再次订阅987654327@直接引用对象:

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

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      这应该可以解决您的问题:

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

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

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

      【讨论】:

        【解决方案4】:

        最好使用entry unfocused事件,这样它就不会对事件处理程序进行太多调用,从而提高性能!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-10-26
          • 1970-01-01
          • 2012-05-27
          • 2012-05-27
          • 1970-01-01
          • 2015-10-13
          相关资源
          最近更新 更多