WPF - MVVM 文本框限制为特定字符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32572460/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 13:24:51  来源:igfitidea点击:

WPF - MVVM Textbox restrict to specific characters

c#wpfmvvm

提问by Robert J.

I am trying to make text box accept only specific characters.

我试图让文本框只接受特定字符。

My TextBox is bound to the following:

我的 TextBox 绑定到以下内容:

    private string _CompanyID;
    public string CompanyID
    {
        get { return _CompanyID; }
        set
        {
            _CompanyID = UniversalHelpers.sReturnCorrectColumnName(value);
            OnPropertyChanged("CompanyID");
        }
    }

Where this is the function that is being called:

这是被调用的函数:

    public static string sReturnCorrectColumnName(string sInput)
    {
        if(!string.IsNullOrWhiteSpace(sInput))
            return Regex.Replace(sInput, @"[^a-zA-Z]", string.Empty).ToUpper();
        else
            return sInput;
    }

(I am allowing only a-z & A-Z, nothing else).

(我只允许 az 和 AZ,没有别的)。

Finally my TextBox looks like this:

最后我的 TextBox 看起来像这样:

<TextBox Text="{Binding ExcelBindings.CompanyID, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />

What I don't understand is, that user can still write anything he wants, even though my Mode is set to TwoWay.

我不明白的是,即使我的模式设置为双向,用户仍然可以写任何他想要的东西。

What am I doing wrong?

我究竟做错了什么?

回答by poke

You should use a custom UI element there that restricts the input on the view-side using “classic” solutions like change listeners.

您应该在那里使用自定义 UI 元素,使用更改侦听器等“经典”解决方案来限制视图端的输入。

For example, you can just create a simple subtype of TextBoxthat overrides the OnPreviewTextInputmethod. There, you can decide when some input should go through, or when you want to prevent it.

例如,您可以只创建一个TextBox覆盖该OnPreviewTextInput方法的简单子类型。在那里,您可以决定何时应该通过某些输入,或者何时要阻止它。

For example, this is a custom TextBox that takes only characters from the ASCII alphabet:

例如,这是一个仅从 ASCII 字母表中获取字符的自定义 TextBox:

public class AlphabetTextBox : TextBox
{
    private static readonly Regex regex = new Regex("^[a-zA-Z]+$");

    protected override void OnPreviewTextInput(TextCompositionEventArgs e)
    {
        if (!regex.IsMatch(e.Text))
            e.Handled = true;
        base.OnPreviewTextInput(e);
    }
}

Of course, you could also make the regular expression a property of the text box and allow people to set it from XAML. That way, you would get a very reusable component which you can use for various applications.

当然,您也可以使正则表达式成为文本框的属性,并允许人们从 XAML 设置它。这样,您将获得一个非常可重用的组件,您可以将其用于各种应用程序。

回答by Bobby

I do this with the PreviewtextInput event. I have a generic event used for multiple TextBoxes which takes the regex from a configuration table, but I have hard-coded the regex in this example.

我用 PreviewtextInput 事件来做到这一点。我有一个用于多个 TextBox 的通用事件,它从配置表中获取正则表达式,但在本例中我对正则表达式进行了硬编码。

private void GenericTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
   e.Handled = !IsTextAllowed(e.Text, @"[^a-zA-Z]");
}

private static bool IsTextAllowed(string Text, string AllowedRegex)
{
    try
    {
        var regex = new Regex(AllowedRegex);
        return !regex.IsMatch(Text);
    }
    catch
    {
        return true;
    }
}

回答by Steve Hibbert

The problem is, humans type in numbers sequentially, the fools.
To type in "0.1", a legitimate string, you have to type in "0.", which fails. Also, re the accepted answer from @poke (which is great), the e.Text value is the changeto the textbox (keystroke).
You must add this change to the current textbox string, and then validate the concatenated candidate string, and see if that is valid. Humans are also wiley, so they will paste from the clipboard to get around the restriction.
With a textbox, you will never be able to block all garbage in, because at some point the user will have to go through garbage, to get to a valid string.
So you can block illegal character entry using e.Text, or allow sequential step failure. But you will still have to check the final string for validity too.
Below is an example of a textbox that allows users to type in a decimal value with a max of 8 dec places in, but they could still cheat this by pasting from the clipboard.

问题是,人类按顺序输入数字,傻瓜。
要输入“0.1”,一个合法的字符串,你必须输入“0.”,但失败了。另外,重新接受@poke 的答案(很棒),e.Text 值是对文本框(按键)的更改
您必须将此更改添加到当前文本框字符串,然后验证连接的候选字符串,并查看它是否有效。人类也很狡猾,所以他们会从剪贴板粘贴来绕过限制。
使用文本框,您将永远无法阻止所有垃圾进入,因为在某些时候用户将不得不通过垃圾来获取有效字符串。
因此,您可以使用 e.Text 阻止非法字符输入,或允许顺序步骤失败。但是您仍然必须检查最终字符串的有效性。
下面是一个文本框的例子,它允许用户输入最多 8 个十进制位的十进制值,但他们仍然可以通过从剪贴板粘贴来作弊。

////////////////////////
// REGEXTEXTBOX CLASS //
////////////////////////


using System.Windows.Controls; // Textbox
using System.Windows.Input;
using System.Text.RegularExpressions; // Regex

namespace MyNamespace
{
    public class RegexTextBox : TextBox
    {
        private Regex _regex = null;

        public Regex Regex
        {
            get { return _regex; }
            set { _regex = value; }
        }


        ///////////////////////////////////////////////////////////////////////
        // MEMBERS

        protected override void OnPreviewTextInput(TextCompositionEventArgs e)
        {
            var prefix = "OnPreviewTextInput() - ";
            logger.Debug(prefix + "Entering");

            string currentText = this.Text;
            string candidateText = currentText + e.Text;

            // If we have a set regex, and the current text fails,
            // mark as handled so the text is not processed.
            if (_regex != null && !_regex.IsMatch(candidateText))
            {
                e.Handled = true;
            }           

            base.OnPreviewTextInput(e);
        }

    } // end of class RegexTextbox

} // end of MyNamespace


/////////////////////
// MAINWINDOW.XAML //
/////////////////////

//(Window class needs to know your namespace so it needs xmlns:myNamespace="clr-namespace:MyNamespace")

<myNamespace:RegexTextBox 
 x:Name="textboxPayToAmount" 
 Text="{Binding PayToAmount}">
</myNamespace:RegexTextBox> 


////////////////////////
// MAINWINDOW.XAML.CS //
////////////////////////

namespace MyNamespace
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            textboxPayToAmount.Regex = 
                new System.Text.RegularExpressions.Regex(@"^\d*(\.\d{0,8})?$");
        }
    }
}

回答by David

Public Shared Function GetWordCount(str As String) As Integer Dim collection As MatchCollection = Regex.Matches(str, "\S+") Return collection.Count End Function

公共共享函数 GetWordCount(str As String) As Integer Dim collection As MatchCollection = Regex.Matches(str, "\S+") Return collection.Count End Function

Public Shared Function GetInWordLimit(str As String, max_words As Integer) As String

公共共享函数 GetInWordLimit(str As String, max_words As Integer) As String

    Dim final As String = ""
    Dim count As Integer = Core.StringOperations.GetWordCount(str)
    Dim avg_max_length As Integer = max_words * 7

    Dim words = str.Split(" ")
    If (words.Length > max_words - 1 And count > max_words - 1) Then
        Dim index As Integer = 0
        For Each word In words
            If index >= max_words Then Exit For

            final &= word & " "
            If Not (Char.IsSeparator(word) Or Char.IsWhiteSpace(word) Or word = "") Then
                index += 1
            End If

        Next
        final = final.TrimEnd
    Else
        final = str
    End If

        If final.Length > avg_max_length - 1 Then final = final.Substring(0, avg_max_length)

    Return final
End Function