65.9K
CodeProject 正在变化。 阅读更多。
Home

带按键验证的 StringBox 控件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2020年7月22日

CPOL

4分钟阅读

viewsIcon

7048

downloadIcon

331

此 StringBox 控件使用正则表达式和一点粘合逻辑实现按键验证

引言

本文介绍了一种使用正则表达式和一些简单逻辑进行按键验证的方法。正则表达式模式用于验证按键输入,而不考虑光标位置。这里提供了6个用于6种不同字符串模式的类。用户可以根据需要创建新模式。一个布尔函数指示StringBox的文本已完成,可供应用程序使用。

背景

我开发的一些桌面应用程序需要输入IP地址。我需要一种简单的方法,让用户在IP地址框中输入字符,同时只允许他们根据光标位置输入正确的字符。正则表达式是验证最终字符串的绝佳方法,但它们无法在输入时验证部分字符串。此代码中的类和模式解决了这个问题。

Using the Code

演示窗口

下面是一个演示这些类的窗口。六个文本框将只接受与其相应标签指定的正确按键。当StringBox拥有100%有效且完整的string时,复选框将变为选中状态。下载可执行文件并试用一下。

此窗口背后的主代码非常简单。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void sbIPv4_TextChanged(object sender, EventArgs e)
    {
        cbIPv4.Checked = ((StringBox.StringBox)sender).IsComplete();
    }

    private void sbPort_TextChanged(object sender, EventArgs e)
    {
        cbPort.Checked = ((StringBox.StringBox)sender).IsComplete();
    }

    private void sbIPv4Port_TextChanged(object sender, EventArgs e)
    {
        cbIPv4Port.Checked = ((StringBox.StringBox)sender).IsComplete();
    }

    private void sbEmailAddress_TextChanged(object sender, EventArgs e)
    {
        cbEmailAddress.Checked = ((StringBox.StringBox)sender).IsComplete();
    }

    private void sbIPv6_TextChanged(object sender, EventArgs e)
    {
        cbIPv6.Checked = ((StringBox.StringBox)sender).IsComplete();
    }

    private void sbNANPPhoneNumber_TextChanged(object sender, EventArgs e)
    {
        cbNANPPhoneNumber.Checked = ((StringBox.StringBox)sender).IsComplete();
    }
}

正如您所见,当各自的StringBox TextChanged事件触发时,复选框就会被设置。

StringBox 类

验证按键以匹配特定模式是一个分两步的过程。第一步是检查是否只允许根据光标位置接受合适的字符。例如,IP地址由四个以点分隔的十进制字节组成。每个部分都在0-255之间。可以轻松创建正则表达式模式来实现这一点。IP地址的正则表达式模式可能如下所示:

[\d]{1,3}[.][\d]{1,3}[.][\d]{1,3}[.][\d]{1,3}

这只会在我们有一个完整的表达式进行检查时匹配。因此,在输入时检查文本将不起作用。为了解决这个问题,我们修改模式,使其匹配完整IP地址的所有可能的较短版本。我们可以使用此模式:

[\d]{0,3}[.]?[\d]{0,3}[.]?[\d]{0,3}[.]?[\d]{0,3}

请注意,使用此模式,无论字符串输入了多少,我们都会获得匹配,但只允许数字和点出现在正确的位置。

现在让我们看看在基类StringBox中使用此模式的按键代码:

private void StringBox_KeyPress(object sender, KeyPressEventArgs e)
{
    if (e.Handled) return;

    // insert key into temp string
    var temp = SelectionLength > 0
        ? Text.Substring(0, SelectionStart) + e.KeyChar + 
          Text.Substring(SelectionStart + SelectionLength)
        : Text.Insert(SelectionStart, e.KeyChar.ToString());

    // if doesn't match pattern or length then reject the key by setting handled = true
    var handled = !(regex.IsMatch(temp) && (regex.Match(temp).Length == temp.Length));

    // do an additional check to make sure individual parts are correct
    if (!handled)
        handled = !IsValid(temp);

    e.Handled = handled;
}

此代码创建了一个temp string,它是Text string加上插入到正确位置的按键。然后,它将此temp string与正则表达式模式进行匹配检查,并检查匹配长度是否与temp长度相同。如果任一条件不满足,则通过设置handled = true来拒绝按键。

到目前为止,一个IP地址999.999.999.999将被接受。这就是第二步的作用。keypress事件还必须使用虚拟函数IsValid验证temp字符串。如果此检查失败,则keystroke也会被拒绝。每个派生类都应重写此虚拟函数。

StringIPv4 类

查看下面的StringIPv4类代码:

public class StringIPv4 : StringBox
{
    public StringIPv4()
    {
        Pattern = @"[\d]{0,3}[.]?[\d]{0,3}[.]?[\d]{0,3}[.]?[\d]{0,3}";
    }

    /// <summary>
    /// Check if entire string is valid IPv4 string
    /// </summary>
    /// <returns>true if valid IPv4 string</returns>
    public override bool IsComplete()
    {
        // make sure there are 4 parts
        var s = Text.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
        return s.Length == 4;
        // do not need to do range check since 'IsValid' already does this
        // alternate IPv4 check method
        //if (!IPAddress.TryParse(Text, out var address)) return false;
        //if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) 
        //    return true;
        //return false;
    }

    /// <summary>
    /// Checks if 'str' matches IPv4 complete pattern
    /// Called once each keystroke
    /// </summary>
    /// <param name="str">temp string to check</param>
    /// <returns>true if string is valid</returns>
    protected override bool IsValid(string str)
    {
        // make sure each part is range limited
        var s = str.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (var t in s)
        {
            var x = Convert.ToInt32(t);
            if (x < 0 || x > 255) return false;
        }
        return true;
    }
}

首先,我们看到这个类是从StringBox类派生的,并在构造函数中定义了一个正则表达式模式。

IsValid函数重写了基类的虚拟函数。该函数将temp字符串按点分隔成离散的部分,并检查每个部分是否在0-255的范围内。如果值不在该范围内,则返回false,这将拒绝按键。

应用程序通常会调用IsComplete函数来检查Text字符串是否已完成并可供使用。在这种情况下,我们只需要检查字符串中是否有4个字节。IsValid已经检查了每个部分是否在范围内。另一种方法是将Text解析为InterNetwork类型的IP地址。然后,完整的string就是一个完全有效的IPv4地址。

所提供的六个特定类涵盖了常见的应用程序string类型。目标是使用正则表达式进行按键检查,并使用简单的、高级别的检查来验证string。这些类将出现在您的Visual Studio工具箱窗口中,以便拖放到您的窗口上。

注释

创建新的StringBox类:

  1. 将新StringBox从基类派生出来。
  2. 创建合适的模式作为第一步。该模式必须匹配最终string的所有可能子字符串。
  3. 创建IsComplete函数,作为对Text string的最终测试。
  4. 创建IsValid函数,作为按键检查第二步的一部分。

关注点

创建这段代码让我看到了使用非传统正则表达式的新方法。基本上是如何使用一个似乎匹配string及其所有子string的模式。或者正则表达式如何用于比仅完全string验证更多的用途。

致谢

这是在textbox中进行按键验证的众多方法中的一种。我没有直接链接到特定来源,但在这里CodeProject上快速搜索一下就会发现很多。

我能引用的唯一链接是一个远房亲戚,NumberBox,我之前发布过。它对SNFS数字格式进行按键验证。

历史

  • 2020年7月22日:初始版本
© . All rights reserved.