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

8

我将尝试让文本框仅接受特定字符。

我的文本框绑定到以下内容:

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

以下是被调用的函数:

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

最终我的文本框是这样的:

(我只允许使用a-z和A-Z字母,不允许使用其他字符。)

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

我不理解的是,即使我的 Mode 被设置为 TwoWay,用户仍然可以自由输入任何内容。
我做错了什么?

是的,这样做通常不会有好结果。实现IDataErrorInfo和/或INotifyDataErrorInfo接口,并在包含不正确字符时将实例标记为错误即可。 - user1228
当用户正在输入时,UI不会自动更新您所做的更改。它已正确保存(仅限a-z和A-Z),但即使使用双向绑定,您的UI也不会自动更新。 - rmn36
1
使用 MaskedTextBox。在 Extended WPF Toolkit 中有一个。 - Glen Thomas
1
你确定你正确地绑定了DataContext吗?在setter中设置一个断点——当你在TextBox中输入时,它是否被触发?调试输出中是否有绑定错误? - svinja
当您在setter中更改值时,即使您引发了PropertyChanged事件,该更改也不会反映在UI中,因为UI不希望您修改它传入的值,并且不会重新查询该属性。 - Glen Thomas
显示剩余3条评论
4个回答

18

你应该使用自定义 UI 元素,使用像更改监听器这样的“传统”解决方案在视图端限制输入。

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

例如,这是一个只接受 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);
    }
}
当然,您也可以将正则表达式作为文本框的属性,并允许人们从XAML中设置它。这样,您将获得一个非常可重用的组件,可以用于各种应用程序。

1
我会使用PreviewtextInput事件来实现此功能。我有一个通用的事件,可用于多个文本框,它从配置表中获取正则表达式,但在此示例中,我已经将正则表达式硬编码了。
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;
    }
}

0
问题在于,人类按顺序输入数字,真是傻瓜。
要输入“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})?$");
        }
    }
}

-1
Public Shared 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
    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

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