You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WPF TextBox小数输入过滤异常:无法输入小数点

解决WPF TextBox小数点无法输入的数字过滤问题

兄弟,你这个情况我太熟了——用PreviewKeyDown处理输入过滤的时候,小数点总是闹脾气,明明设了Handled=false就是输不进去。问题出在你单纯靠按键码转字符的方式不靠谱,因为WPF里的Key和实际输入的字符不是完全一一对应的,比如小键盘的小数点是Key.Decimal,主键盘的是Key.OemPeriod,而且不同键盘布局下转换逻辑可能出错。

给你一套更靠谱的方案,结合PreviewTextInputPreviewKeyDown和粘贴事件,既能处理键盘输入,也能防粘贴非法内容:

第一步:XAML配置

先给TextBox绑定三个事件,覆盖键盘输入、功能键和粘贴场景:

<TextBox x:Name="NumericInputBox"
         PreviewTextInput="NumericInputBox_PreviewTextInput"
         PreviewKeyDown="NumericInputBox_PreviewKeyDown"
         DataObject.Pasting="NumericInputBox_Pasting"/>

第二步:后台逻辑实现

1. 处理普通文本输入(最关键的一步)

PreviewTextInput事件直接拿到用户要输入的文本内容,比按键码判断准确得多:

private void NumericInputBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
    var textBox = sender as TextBox;
    if (textBox == null) return;

    // 只允许数字和小数点
    if (!char.IsDigit(e.Text, 0) && e.Text != ".")
    {
        e.Handled = true;
        return;
    }

    // 避免重复输入小数点
    if (e.Text == "." && textBox.Text.Contains("."))
    {
        e.Handled = true;
    }
}

2. 处理功能键和特殊按键

PreviewKeyDown用来放行退格、方向键这些必要的功能键,同时处理小键盘的小数点:

private void NumericInputBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    // 放行导航和编辑功能键
    var allowedFunctionKeys = new List<Key> 
    { 
        Key.Back, Key.Delete, Key.Left, Key.Right, 
        Key.Up, Key.Down, Key.Tab, Key.Enter 
    };
    if (allowedFunctionKeys.Contains(e.Key))
    {
        e.Handled = false;
        return;
    }

    // 处理主键盘和小键盘的小数点
    if (e.Key == Key.OemPeriod || e.Key == Key.Decimal)
    {
        var textBox = sender as TextBox;
        e.Handled = textBox?.Text.Contains(".") ?? true;
        return;
    }

    // 阻止其他非数字按键
    if (!char.IsDigit((char)KeyInterop.VirtualKeyFromKey(e.Key)))
    {
        e.Handled = true;
    }
}

3. 拦截非法粘贴

用户可能会粘贴非数字内容,这个事件必须处理:

private void NumericInputBox_Pasting(object sender, DataObjectPastingEventArgs e)
{
    if (!e.DataObject.GetDataPresent(typeof(string)))
    {
        e.CancelCommand();
        return;
    }

    var pastedText = e.DataObject.GetData(typeof(string)) as string;
    if (!IsValidPastedContent(pastedText))
    {
        e.CancelCommand();
    }
}

private bool IsValidPastedContent(string content)
{
    if (string.IsNullOrWhiteSpace(content)) return true;
    // 最多一个小数点,其余必须是数字
    int dotCount = content.Count(c => c == '.');
    return dotCount <= 1 && content.All(c => char.IsDigit(c) || c == '.');
}

为什么之前的方法不行?

你之前用按键转字符的方式,很可能没正确识别小键盘的Key.Decimal键,或者转换时因为键盘布局问题得到了错误的字符,导致即使设了Handled=false,实际输入还是被拦截了。而PreviewTextInput直接获取输入的文本内容,逻辑更直接准确。

如果需要支持负数,只需要在PreviewTextInput里加个判断:允许负号出现在文本开头且只能出现一次就行。

内容的提问来源于stack exchange,提问作者Matt

火山引擎 最新活动