在Xamarin.Forms中限制输入字段的长度和字符

27
如何限制 Xamarin.Forms 中 Entry 控件中输入的长度和字符?需要创建自定义控件吗?是否可以派生自 Entry(或其他控件),以便可以应用必要的平台特定的输入限制。
例如,数值字段应该限制最多只能输入三个数字字符。
将 Entry 控件的 Keyboard 属性设置为 Keyboard.Numeric 仅适用于 iOS 平台。它不会限制实际文本输入 - 即我仍然可以输入非数字字符。也没有看到限制输入长度的方法。
13个回答

19

我会使用Behaviors。更多信息请参见:https://developer.xamarin.com/guides/xamarin-forms/behaviors/creating/

示例是用于数字键盘的输入框,但可用于任何键盘。

XAML用法:

<ContentPage
   xmlns:behaviors="clr-namespace:myApp.Behaviors;assembly=myApp"

   <Entry
       Keyboard="Numeric"
       Text="{Binding EntryText}" >
       <Entry.Behaviors>
           <behaviors:EntryLengthValidatorBehavior MaxLength="3" />
       </Entry.Behaviors>
   </Entry>

行为

public class EntryLengthValidatorBehavior : Behavior<Entry>
{
    public int MaxLength { get; set; }

    protected override void OnAttachedTo(Entry bindable)
    {
        base.OnAttachedTo(bindable);
        bindable.TextChanged += OnEntryTextChanged;
    }

    protected override void OnDetachingFrom(Entry bindable)
    {
        base.OnDetachingFrom(bindable);
        bindable.TextChanged -= OnEntryTextChanged;
    }

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

        // if Entry text is longer then valid length
        if (entry.Text.Length > this.MaxLength)
        {
            string entryText = entry.Text;

            entryText = entryText.Remove(entryText.Length - 1); // remove last char

            entry.Text = entryText;
        }
    }
}

非常好的答案,正是我在寻找的! - aherrick
1
这种方法的问题在于,假设最大长度为3,并且您已经在文本框中输入了3个字符,当您输入第4个字符时,您可以在文本框中看到该字符被显示了一秒钟,然后被删除。 - A-Sharabiani
非常棒的帖子!我使用了一个编辑器(而不是输入框)。我将其与一个标签结合使用,该标签绑定到编辑器文本长度的x:Reference,以向用户显示他们还剩下多少字符。 - maplemale
这也有一个缺陷,只能删除最后一个字符。如果用户复制/粘贴了一个很长的字符串怎么办? - Burgler-dev

18

您可以按照以下方式在输入字段中限制字符数:

  int restrictCount = <your restriction length> //Enter your number of character restriction
  Entry entry = new Entry();
  entry.TextChanged += OnTextChanged;

  void OnTextChanged(object sender, EventArgs e)
  {
    Entry entry = sender as Entry;
    String val = entry.Text; //Get Current Text

    if(val.Length > restrictCount)//If it is more than your character restriction
    {
     val = val.Remove(val.Length - 1);// Remove Last character 
     entry.Text = val; //Set the Old value
    }
  }

3
不要删除最后一个字符,改用以下方法:val = e.NewTextValue.Substring(0, MaxLength),因为用户可能会将多个字符复制/粘贴到输入框中。 - A-Sharabiani
如果用户在单词中间添加一个字符会发生什么?例如,输入框限制为4个字符,最初包含“aple”。我添加了一个“p”以获得“apple”,但最终得到的是“appl”。 - Gabriel Bourgault

9
你可以使用绑定(Binding); 例如,我想保存一个支付值,不能超过100。所以我写了一个类。
puclic class Payment : INotifyPropertyChanged
{
    private int _amountDecimals;
    public int AmountDecimals
    {
        get
        {
            return _amountDecimals;
        }

        set
        {
            if (value <= 100)
            {
                _amountDecimals = value;
            }
            OnPropertyChanged();
        }
    public event PropertyChangedEventHandler PropertyChanged;

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

这个属性将在用户输入的值超过100之前设置AmountDecimals的值。

然后只需在页面构造函数(或从xaml)中通过代码设置绑定即可。

var myPayment =new Payment(); //this will hold page(view) data
BindingContext = myPayment;
var paymentEntry = new Entry();
paymentEntry.Keyboard = Keyboard.Numeric;
paymentEntry.SetBinding(Entry.TextProperty, "AmountDecimals");            

用户输入一个数字值到输入框,但如果他/她试图输入一个大于100的值,绑定将其还原为旧值。您可以像这样在类属性上编写代码(在设置器中)。因此,如果您想让某个属性仅携带5个字符,可以编写类似于以下内容的代码(代码可能有误,我没有编译它们:))

    private string _name;
    public string Name
    {
        get
        {
            return _name;
        }

        set
        {
            if ((value!= null && value.length <= 5) || value == null)
            {
                _name = value;
            }
            OnPropertyChanged();
        }

1
如果你只需要用它来做一些简单的事情,那么这可能是最简单/最快的方法。 - Ewan

5

我使用了一个具有可绑定属性的自定义输入控件,用于大写和最大长度。

控件(MyEntry.cs)

class NewPaymentEntry : Entry
{
   public NewPaymentEntry()
    {
        base.TextChanged += EditText;
    }
    public void EditText(object sender, TextChangedEventArgs args)
    {
        Entry e = sender as Entry;
        String val = e.Text;

        if (string.IsNullOrEmpty(val))
            return;

        if (Uppercase )
            val = val.ToUpper();

        if(MaxLength > 0 && val.Length > MaxLength)
        {
                val = val.Remove(val.Length - 1);
        }
        e.Text = val;

    }

    public static readonly BindableProperty UppercaseProperty = BindableProperty.Create<MyEntry, bool>(p => p.Uppercase, false);

    public bool Uppercase
    {
        get
        {
            return (bool)GetValue(UppercaseProperty);
        }
        set
        {
            SetValue(UppercaseProperty, value);
        }
    }

    public static readonly BindableProperty MaxLengthProperty = BindableProperty.Create<MyEntry, int>(p => p.MaxLength, 0);

    public int MaxLength
    {
        get
        {
            return (int)GetValue(MaxLengthProperty);
        }
        set
        {
            SetValue(MaxLengthProperty, value);
        }
    }
}

在xaml中调用它的方式如下:

                  <MyEntry  Text="{Binding MyBindingProp}"
                            Uppercase="True"
                            MaxLength="11"/>

5
我有很多自定义控件,你会认为Xamarin偶尔会更新它们的默认控件以包含这些常见的东西! - Ewan
我在使用xaml时遇到了问题。当我尝试使用MyEntry时,会出现异常:Xamarin.Forms.Xaml.XamlParseException: 位置10:6。在xmlns http://xamarin.com/schemas/2014/forms中找不到类型MyEntry。 - user4081625
我已经解决了。在内容页中添加以下行:xmlns:local="clr-namespace:MyProject;assembly=MyProject",然后当您想要引用MyEntry时使用<local:MyEntry ... />。我认为您应该将此添加到您的XAML答案中,但除此之外它可以正常工作,所以谢谢您。 - user4081625
这可以简化:我们不需要使用 BindableProperty,而 MaxLengh 和 Uppercase 可以像这样成为简单属性:public int MaxLenght {get; set;} - A-Sharabiani

4

定义一个具有所需属性的新自定义条目:

public class CustomEntry : Entry
{
    public int MaxLength { get; set; }
    public bool IsNumeric { get; set; }
    public int MinValue { get; set; }
    public int MaxValue { get; set; }

    public CustomEntry()
    {

        // Initialize properties.
        IsNumeric = false;
        MaxLength = int.MaxValue;
        MinValue = int.MinValue;
        MaxValue = int.MaxValue;

        // Set the events.
        base.TextChanged += OnTextChanged;
    }

   public void OnTextChanged(object sender, TextChangedEventArgs e)
    {
        Entry entry = sender as Entry;

        // If empty, set it to empty string.
        if (string.IsNullOrWhiteSpace(e.NewTextValue))
        {
            entry.Text = string.Empty;
            return;
        }

        // Check if it is numeric.
        if (IsNumeric)
        {
            int value;
            var isValid = int.TryParse(e.NewTextValue, out value);

            if (!isValid)
            {
                entry.Text = e.OldTextValue;
                return;
            }

            // Check the min/max values.
            if (value > MaxValue || value < MinValue)
            {
                entry.Text = e.OldTextValue;
            }
        }

        // If not numeric, check the length.
        if (e.NewTextValue.Length > MaxLength)
            entry.Text = e.OldTextValue;
    }
}

在XAML中使用它:

   <controls:CustomEntry Text="{Binding months}" Keyboard="Numeric" IsNumeric="true" MaxLength="2" MinValue="1" MaxValue="12"/>

注意:与大多数答案不同的是,不要移除最后一个字符,更安全的做法是将文本设置为旧文本,因为用户可能会将多个字符粘贴到文本框中。


如果用户输入了非法值,向用户发出最大和最小值警告会很好。 - Aman
我该如何输入像0.85这样的数字? - Aman
1
@Aman,你可以将代码行int.TryParse(...)修改为float.TryParse(...) - A-Sharabiani

3

输入控件(Entry)中似乎没有内置的属性来限制字符/长度。您可以通过以下两种方法之一来实现文本限制和字符输入:

  1. 是的 - 您可以直接从输入控件(Entry)派生出自己的衍生版本,然后进一步自定义它,例如挂钩到TextChanged事件处理程序。没有按键事件处理程序,因此您必须对传递给e.NewTextValue的完整值进行有效性检查。如果新条目不符合您的要求,则可以将.Text = e.OldTextValue设置为返回上一个有效条目。

  2. 如果您想要挂钩到每个平台本地控件的事件处理程序,则可以编写自己的自定义渲染器控件以获得更精细的控制。


2
我肯定会为此使用一个行为。
public class TextValidationBehavior : Behavior<Entry>
{
 // This can be bound to view model property to be informed
 public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
    public bool IsValid
    {
        get { return (bool)base.GetValue(IsValidProperty); }
        private set { base.SetValue(IsValidPropertyKey, value); }
    }

// Attach delegate to text changed event
protected override void OnAttachedTo(Entry entry)
{
    entry.TextChanged += OnEntryTextChanged;
    base.OnAttachedTo(entry);
}
// Detach delegate from text changed event
protected override void OnDetachingFrom(Entry entry)
{
    entry.TextChanged -= OnEntryTextChanged;
    base.OnDetachingFrom(entry);
}

void OnEntryTextChanged(object sender, TextChangedEventArgs e)
{
    var text = e.NewTextValue;
    IsValid = Validate(text); // Implement this as needed
}
}

然后在 XAML 中像这样使用它:
 <ContentPage xmlns:local="clr-namespace:Behaviors;assembly=Behaviors" ... >

 <Entry Placeholder="Enter some text">
     <Entry.Behaviors>
        <local:TextValidationBehavior IsValid={Binding IsEntryValid} />
   </Entry.Behaviors>
 </Entry>
 </ContentPage>

很棒,你包含了一个XAML示例! - Ewan

1

以下是Femil的回答的延续:

这里有一个自定义控件,用于限制字符数量,但它也可以用于任何你想使用TextChanged事件的情况:

public class CustomEntry : Entry
{
    private CustomEntryParams parameters { get; set; }

    public CustomEntry(CustomEntryParams customParams)
    {
        if (customParams.MaxLength > 0)
        {
            base.TextChanged += EnforceMaxLength;
            parameters = customParams;
        }
    }

    public void EnforceMaxLength(object sender, TextChangedEventArgs args)
    {
        Entry e = sender as Entry;
        String val = e.Text;

        if (val.Length > parameters.MaxLength)
        {
            val = val.Remove(val.Length - 1);
            e.Text = val;
        }
    }
}

public class CustomEntryParams {
    public int MaxLength { get; set; }
}

不要尝试在XAML中使用此代码,否则会收到解析器错误,而应该在代码后端使用:

new CustomEntry(new CustomEntryParams { MaxLength = 5 });

你可以在稍微努力一点的情况下将其用于 XAML 中(或者只是将其作为字符串)。 - Ewan

1
请查看Xamarin Behaviors。您可以使用TextChangedBehavior.cs作为模板,开发自己的行为以满足格式化掩码文本输入字段的需求。我已经为此开发了FormattedTextChangedBehavior : Behavior<Entry>

2
最好在你的回答中提供一个代码示例,而不是简单地链接到外部资源。 - Casey Rule

0

您可以在自定义渲染器的OnElementChanged方法中设置以下过滤器

this.Control.SetFilters(new Android.Text.IInputFilter[] { new Android.Text.InputFilterLengthFilter(MaxLength)});

有趣但也令人困惑 - Ewan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接