Windows Phone 8.1文本框字符虚拟键验证

4

我将开发Windows Phone 8.1应用程序。在文本框中,我希望防止用户输入任何非数字字符,只允许输入[0-9]。

所以这是我的代码:

 private void NumKeyDown(object sender, KeyRoutedEventArgs e)
        {

           bool isNumber = (e.Key == Windows.System.VirtualKey.Number0 ||
                 e.Key == VirtualKey.Number1 ||
                 e.Key == VirtualKey.Number2 ||
                 e.Key == VirtualKey.Number3 ||
                 e.Key == VirtualKey.Number4 ||
                 e.Key == VirtualKey.Number5 ||
                 e.Key == VirtualKey.Number6 ||
                 e.Key == VirtualKey.Number7 ||
                 e.Key == VirtualKey.Number8 ||
                 e.Key == VirtualKey.Number9 ||
                 e.Key == VirtualKey.Back);

            CoreVirtualKeyStates shiftState = Window.Current.CoreWindow.GetKeyState(VirtualKey.Shift);


            bool shiftIsNotDown = true;
            if ((shiftState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down)
                shiftIsNotDown = false;

            e.Handled = !(isNumber && shiftIsNotDown);

        }

它可以工作,但有两个主要缺陷:

1- 它也接受了移位字符(即!@#$%^& *),而这不是想要的。

2- 当我启动应用程序时,文本框填充有一个数字。我可以输入任何数字或字符(!@#$%^),也可以使用 BACKSPACE 删除。在此期间, ShiftState NONE 。然而,一旦我使用 BACKSPACE 删除文本框中的所有内容, ShiftState 就会更改为[ locked | Down ],我将无法在该文本框中输入任何内容。

请注意,我从未触摸过换挡按钮!

更新

我发现使用滑动功能将输入文本字母而不触发我正在应用的keyDown事件:(

任何帮助都将不胜感激


你尝试过通过在XAML TextBox中设置InputScope="Number"来限制输入吗?或者你坚持要全键盘? - Romasz
输入范围不执行任何验证,也不会阻止用户通过硬件键盘或其他输入设备提供任何输入。 - stackunderflow
1
错误的事件。请使用CoreWindow.CharacterReceived。 - Hans Passant
@HansPassant 我喜欢那些能用一两个词让你找到正确方向的人。你的评论对我帮助很大,让我找到了一个不是100%令人满意但却可行的解决方案。请看看我的回答... 致意 :) - stackunderflow
2个回答

7

好的,根据我在这里收到的回复和最近几天的研究,我会尝试回答这个问题。答案将分为两个部分[附注-解决方案]

如果您对我发现有趣的问题的细节不感兴趣,可以跳转到(解决方案部分)

附注:

我注意到以下问题:

  1. 最重要的是,window 8.1和xaml和winRT文档非常差,几乎没有用处。我希望微软winRT团队中的某人正在阅读这篇文章。除非他们故意这样做以迫使大多数开发人员转向C ++。

  2. 关于测试我的应用程序。我使用了3种[[不同]]输入方法。如下图所示:

keyBoards

结果与KeyDown事件不同且不一致,如下所示:

假设我想输入字符(&)。

  • 在模拟器键盘上:keyDown事件参数e.Key = Number7。
  • 在PC触摸键盘上:KeyDown事件将会触发两次。第一次按下时,e.Key代码将为shift,在第二个keyDown事件中,e.Key将是Number7。
  • 在物理键盘上,当然会触发两个keyDown事件,因为您必须先按shift,然后按Number7才能得到(&)。

此外,我注意到在物理键盘上,无论按哪个shift键(即右侧或左侧),KeyDown事件的e.Key都会显示LeftShift!!

我不知道这是否是我的计算机键盘的特殊情况,但所有这些发现都表明,KeyDown不是真正可信赖的,文档在这里缺乏。

还有另一个我注意到无法控制的发现:

  • 每当您将焦点放在空文本框上时,Shift键将进入锁定状态(以大写字母开始句子)。因此,任何keyDown事件都会首先触发Shift键,然后才是您按下的字母。如果您不知道这一点,可能会感到困惑。

我要感谢@Bryan Stump,他带我去了微软链接MSFT Forum,在那里我了解到了重要的信息,以理解这种情况:

"跟踪KeyUp和KeyDown,如Shiva代码所建议的,将适用于有限的情况,但并非所有情况。例如,它无法与墨水或(我认为)IME输入配合使用。它还对键盘布局做出了假设,这些假设并不适用于所有键盘。除非您可以将要求限制在非常具体、基本的场景中,否则成功地限制输入的唯一方法是在事后这样做。" Rob Caplan [MSFT]

这个链接告诉我,唯一可行的方法是先接受字符,然后在不符合验证要求时将其删除,因此引用了“事后”这句话。
最后,我想感谢@Hans Passant的简短评论,让我找到了正确的方向:
“使用CoreWindow.CharacterReceived代替”。
之后,我开始搜索,关于CoreWindow.CharacterReceived唯一好的例子是在StackOverflow上找到的。
然后,我按照以下方式解决了问题。
解决方案:
介绍:
  • 首先:您无法拦截字符并防止其到达文本框。

  • 其次:您无法使用keyDown或keyUp事件来知道是什么字符。您只能对按下的键有一个想法,而不是产生的字符。

  • 第三:将为您提供接收字符的事件命名为“CoreWindow.CharacterReceived”,但请注意,您将在将字符写入文本框后才会知道字符。在这一点上,您可以选择接受它或删除它

  • 第四:由于字符是在textBox中接收的,因此处理它的正确方法是textChanged事件。

  • 第五:最重要的是,CharacterReceived事件将在单词中的每个字母上循环触发,这需要特殊的操作和验证

因此,基于以上五个事实,伪代码将如下:

依靠RegEx验证文本并接受它;否则,如果输入无效,则恢复textBox.Text的先前状态。

string txtTemp = "";
    private void changedText(object sender, TextChangedEventArgs e)
    {

    Regex regex = new Regex(@"^\d{1,4}$");


string txtToTest = txtNumber.Text;


    if (regex.IsMatch(txtToTest)|| txtNumber.Text=="")
    {
//do nothing 
    }
    else
    {
        txtNumber.Text = txtTemp;
        txtNumber.Select(txtNumber.Text.Length, 0);
    }

//Save the current value to resume it if the next input was invalid
    txtTemp = txtNumber.Text;
}

上述解决方案适用于我需要确保用户只输入数字的情况。但是有些情况下,您需要确保用户只输入特定字母,并且您需要根据按下的字母做出响应!!在这种情况下,您将需要以下解决方案,该解决方案不完整,并且缺乏用户可能从剪贴板(粘贴)或使用键盘的滑动功能输入字母的所有可能情况。
以下是需要逐个控制输入字母的场景的解决方案:
1-由于CoreWindow.CharacterReceived事件不特定于textBox(它是窗口/页面事件),因此您需要在textBox获得焦点时连接它,并在textBox失去焦点时断开连接。
2-监听keyDown事件。每当它被触发时,将textBox.Text值保存到临时变量txtTemp中。
3-设置一个布尔值,指示字符是否被接受(bool acceptChange = true)。并使用CoreWindow.CharacterReceived事件将此布尔值设置为true或false(已接受/未接受)。

在textChange事件中,如果bool类型的acceptChange为true,则不做任何操作。如果bool类型的acceptChange为false,则将textBox.Text的值重置为keyDown事件期间保存的临时值(txtBox.Text = txtTemp)。

通过这种解决方案,我们可以确保只接受我们想要的字符,只剩下一个微小的问题:

假设您设置了验证规则,仅接受数字。而textBox.Text =“752”。 如果用户输入字母“v”,则txtTemp将为“752”,txtBox.Text的新值将为“752v”,在textChange事件中,我们将将该值重置为先前的值(即“752”)。这是通过keyDown事件的帮助完成的。

但是,如果用户没有键入字母“v”,而是从其他地方复制并使用粘贴功能,则txtBox.Text的新值为“752v”,但txtTemp将为“75”,因为keyDown事件未触发以捕获最新的txtBox值:(

这就是textBox事件“paste”的重要性所在。

因此,我的伪代码中的第5步是:

txtBox.paste事件中,确保通过将e.Handled=true;来取消此事件。

现在我来到代码:

//this is critical to wire up the "Window.Current.CoreWindow.CharacterReceived" event when 
//the textBox get focus and to unwire it when the textBox lose focus.
// notice that the whole page is listening not only the textBox

    private void txtBox_GotFocus(object sender, RoutedEventArgs e)
    {
        Window.Current.CoreWindow.CharacterReceived += inputEntered;
    }

    private void txtBox_LostFocus(object sender, RoutedEventArgs e)
    {
        Window.Current.CoreWindow.CharacterReceived -= inputEntered;
    }



// temporary variable for holding the latest textBox value before the textChange event is trigerred
    string txtTemp = "";

    private void txtBox_KeyDown(object sender, KeyRoutedEventArgs e)
    {
        //whenever a key is pressed, capture the latest textBox value
        txtTemp= txtBox.Text;

    }

// this boolean is to be used by the textChanged event to decide to accept changes or not
    bool acceptChange = true;

// here we recieve the character and decide to accept it or not.
    private void inputEntered(CoreWindow sender, CharacterReceivedEventArgs args)
    {
    // reset the bool to true in case it was set to false in the last call
        acceptChange = true;

        Debug.WriteLine("KeyPress " + Convert.ToChar(args.KeyCode)+ "keyCode = "+ args.KeyCode.ToString());
        args.Handled = true;

    //in my case I needed only numeric value and the backSpace button 
        if ((args.KeyCode > 47 && args.KeyCode < 58) || args.KeyCode == 8)
        {
            //do nothing (i.e. acceptChange is still true)
        }
        else
        {
    //set acceptChange to false bec. character is not numeric nor backSpace
            acceptChange = false;
        }
    }



    private void txtBox_TextChanged(object sender, TextChangedEventArgs e)
    {
    //the code here is my validation where I want only 3 digits number with no decimal
        if (txtBox.Text.Length < 4)
        {
            if (acceptChange)
            {
        // do nothing
            }
            else
            {
                txtBox.Text = txtTemp;

        //this is to move the cursor to the end of the text in the textBox
                txtBox.Select(txtBox.Text.Length, 0);
            }
        }
        else
        {
            txtBox.Text = txtTemp;

        //this is to move the cursor to the end of the text in the textBox
            txtBox.Select(txtBox.Text.Length, 0);
        }


    }





// this is for the special case where the user input text using Paste function
    private void txtBox_Paste(object sender, TextControlPasteEventArgs e)
    {
        e.Handled=true;
    }

:)


1
这是一个在 Stack 上非常有用的帖子的绝佳示例。 - Bryan Stump
对于任何有兴趣进一步调查和测试的人,我在这里发布了一个关于Swype键盘的单独问题,并在4天的测试后自己回答了lol:https://dev59.com/questions/wYHba4cB1Zd3GeqPRXbg - stackunderflow

1
这是一篇关于类似主题的帖子,来自MSDN论坛。目前似乎没有好的方法可以防止输入,尽管可以在事后处理它。建议的指导方针是允许无效输入,但不对无效输入做任何操作。例如,允许用户输入字符,但不允许他们提交表单。我不一定同意,但如果你必须防止输入,CoreWindow.AcceleratorKeyActivated事件发生得“太快了,无法确定输入的字符”。在你的情况下,你只需要寻找数字,所以我认为应该没问题。

http://social.msdn.microsoft.com/Forums/windowsapps/en-US/3c0f5251-9356-4f1d-a148-57024ae71724/testing-for-valid-numeric-and-currency-key-strokes?forum=winappswithcsharp

我运行了你的代码,当文本框被移除时,并没有注意到Shift键状态切换。

我还使用了屏幕键盘,并收到了KeyDown事件。

此外,你的代码也没有接受转义字符!@#$%。

总的来说,我无法重现帖子中报告的任何错误。

订阅CoreWindow.AcceleratorKeyActivated事件。您可以检查VirtualKey。 此事件发生在TextBox.TextChanged之前,因此我们使用它来利用路由事件框架,通过过滤文本框将处理哪些按键按下。

    public MainPage()
    {
        this.InitializeComponent();
        Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated += Dispatcher_AcceleratorKeyActivated;
    }

    void Dispatcher_AcceleratorKeyActivated(CoreDispatcher sender, AcceleratorKeyEventArgs args)
    {
        if (MyTextBox.FocusState != FocusState.Unfocused)
        {
            if (args.VirtualKey == VirtualKey.Number0 ||
                args.VirtualKey == VirtualKey.Number1 ||
                args.VirtualKey == VirtualKey.Number2 ||
                args.VirtualKey == VirtualKey.Number3 ||
                args.VirtualKey == VirtualKey.Number4 ||
                args.VirtualKey == VirtualKey.Number5 ||
                args.VirtualKey == VirtualKey.Number6 ||
                args.VirtualKey == VirtualKey.Number7 ||
                args.VirtualKey == VirtualKey.Number8 ||
                args.VirtualKey == VirtualKey.Number9)
            {
                args.Handled = false;
            }
            else
            {
                args.Handled = true;
            }
        }
    }

很奇怪你无法重现这个问题。:( 因为虚拟键和按键事件误导了我,我已经遭受了几天的痛苦。不管怎样,非常感谢你的尝试回答并提供在MSFT论坛中极其有用的链接。它对我帮助很大。请查看我的答案,随时纠正我 :) - stackunderflow

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