如何计算这些事件之后 WPF 文本框的文本

本文关键字:文本 之后 事件 WPF 何计算 计算 | 更新日期: 2023-09-27 17:56:32

我想将此TextBox限制为仅取正整数,最多包括Int32.MaxValue

<TextBox Name="CurrentPageNumber"
         PreviewTextInput="CurrentPageNumber_PreviewTextInput" 
         DataObject.Pasting="CurrentPageNumber_Pasting" />

我有以下事件:

private void CurrentPageNumber_Pasting(object sender, DataObjectPastingEventArgs e)
{
    if (!e.DataObject.GetDataPresent(typeof(String)))
        e.CancelCommand();
    String text = (String)e.DataObject.GetData(typeof(String));
    if (!IsPositiveInteger(text))
        e.CancelCommand();
}
private bool IsPositiveInteger(String text)
{
    if (text.Length <= 0 || ((int)text[0] == 48 && text.Length != 1)) // Restricts empty strings and numbers with preceding 0's except for 0 itself.
        return false;
    for (int i = 0; i < text.Length; i++)
    {
        var c = (int)text[i];
        if (c < 48 || c > 57) // Check that all characters are between 0 and 9.
            return false;
    }
    int result;
    return Int32.TryParse(text, out result);
}

这还不够好,因为有人可以在文本框中已经有等效的整数值 Int32.MaxValue2147483647),然后在它的右侧再加 1。如何通过预测事件操作后Text的结果来使用我的IsPositiveInteger方法?

如何计算这些事件之后 WPF 文本框的文本

这与阻止更改文本不同,但这是告诉用户"你不能把该文本放在这里"的习惯方式。我建议这样做,因为这是框架支持的方式,所以它最简单,通常框架支持的内容是人们最习惯在用户界面中看到的。我们习惯的,我们称之为"直觉"。

当用户更改该文本框中的文本时,将调用ValidationRule。如果它返回一个false ValidationResult,用户将获得一个红色的错误边框,绑定属性不会更新,并且工具提示会告诉他他做错了什么。

如果你真的想坚持你最初的想法,你会发现你需要做很多工作。我不认为投资回报率证明这种努力是合理的,但是当我年轻的时候,我曾经用Perl写过一个二进制加法器,所以我没有资格扔任何石头。

在 WPF 中执行此类操作的传统方法是在Binding上使用ValidationRule。您没有绑定,因为您没有视图模型。这是一个几乎无法理解的异端邪说,它让我感到寒冷,但我们会努力的。我们将向代码隐藏添加一个属性,以便具有要绑定的内容。

public int NonNegativeIntValue { get; set; }

如果希望能够通过在代码隐藏中设置该属性来更新文本框,则必须采用INotifyPropertyChanged路线。更好的是,使用视图模型;每次在 WPF 中没有视图模型的情况下执行任何操作时,事实证明,使用视图模型会更好。框架就像一个交通警察;承认它的权威,它会更容易相处。

然后我们会写一个ValidationRule

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Controls;
namespace ValidationRules
{
    public class PositiveIntegerRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            int result;
            if (value.GetType() == typeof(String) && Int32.TryParse((String)value, out result) && result > 0)
                return new ValidationResult(true, null);
            return new ValidationResult(false, "Value must be a positive integer");
        }
    }
}

最后,我们将修复您的TextBox

<TextBox 
    Name="CurrentPageNumber"
    xmlns:vr="clr-namespace:ValidationRules"
    ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"
    >
    <TextBox.Text>
        <Binding 
            Path="NonNegativeIntValue"
            RelativeSource="{RelativeSource AncestorType=Window}"
            >
            <Binding.ValidationRules>
                <vr:PositiveIntegerRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

我在这里做一个假设:您的代码隐藏类继承自Window.如果它是UserControl,则必须将绑定中的RelativeSource AncestorType=Window更改为RelativeSource AncestorType=UserControl - 无论正确。

除了 Ed 的答案之外,另一种方法是这样的,这种方式将确保 UI 始终显示一个可用的、可见的值,但它不那么干净,所以我建议人们像另一个答案一样使用验证规则(它更像 WPF)。另外,我的IsPositiveInteger方法有错误;最初我想要 0,但现在我意识到因为我正在分页数据,我希望 1 作为起始页。

XAML

<TextBox Name="CurrentPageNumber" TextChanged="CurrentPageNumber_TextChanged"/>

C#

private bool IsPositive32BitInteger(String text)
{
    if (text.Length <= 0 || (int)text[0] == 48) // Restricts empty strings and numbers with preceding 0's or 0 itself.
        return false;
    for (int i = 0; i < text.Length; i++)
    {
        var c = (int)text[i];
        if (c < 48 || c > 57) // Check that all characters are between 0 and 9.
            return false;
    }
    int result;
    return Int32.TryParse(text, out result);
}
private void CurrentPageNumber_TextChanged(object sender, TextChangedEventArgs e)
{
    var textBox = (TextBox)sender;
    if (textBox.Text.Length <= 0)
    {
        textBox.Text = "1";
        return; // Set text to "1" as default and return. The event will fire again since we set the Text property.
    }
    if (textBox.Text.StartsWith("0"))
    {
        textBox.Text = textBox.Text.TrimStart(new char[] { '0' });
        return; // Trim the text and return. The event will fire again since we set the Text property.
    }
    if (!IsPositive32BitInteger(textBox.Text)) {
        textBox.Text = "1";
        return; // Set text to "1" as default and return. The event will fire again since we set the Text property.
    }
    // At this point the value is forced into the state you want and you can do other stuff.
}
相关文章: