You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ValidationRule的Validate中decimal.TryParse对1.0.1返回true?及输入异常问题

关于decimal.TryParse与WPF数值绑定的两个问题

问题1:decimal.TryParse为何对“1.0.1”返回true?

当WPF的TextBox传入值“1.0.1”时,decimal.TryParse(valueString, out valueParsed)返回true,但该字符串显然不是合法的decimal格式。

相关代码

using System.Windows.Controls;

namespace MyTest.validations
{
    public class DecimalValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            decimal valueParsed;
            string? valueString = value.ToString();
            if (decimal.TryParse(valueString, out valueParsed))
            {
                return new ValidationResult(true, null);
            }

            return new ValidationResult(false, "Inserisci un valore Decimale corretto");
        }
    }
}

原因分析

decimal.TryParse默认使用NumberStyles.Number规则,该规则允许解析包含千位分隔符的字符串。如果你的系统文化(比如意大利语it-IT)使用点(.)作为千位分隔符、逗号(,)作为小数点,那么“1.0.1”会被TryParse的容错机制误解析:第一个点被识别为千位分隔符,“1.0”被解析为10,后续的“.1”被忽略或错误处理,最终返回true。

解决方法

使用更严格的NumberStyles限制解析规则,只允许正负号、数字和小数点,不允许千位分隔符:

public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
    decimal valueParsed;
    string? valueString = value.ToString();
    // 定义严格的解析规则:允许正负号、小数点、前后空白
    var strictStyles = System.Globalization.NumberStyles.AllowLeadingSign | 
                       System.Globalization.NumberStyles.AllowDecimalPoint | 
                       System.Globalization.NumberStyles.AllowLeadingWhite | 
                       System.Globalization.NumberStyles.AllowTrailingWhite;
    
    if (decimal.TryParse(valueString, strictStyles, cultureInfo, out valueParsed))
    {
        return new ValidationResult(true, null);
    }

    return new ValidationResult(false, "Inserisci un valore Decimale corretto");
}

如果需要更极致的格式检查,可以结合正则表达式:

using System.Text.RegularExpressions;

// ...
// 匹配正负号开头,整数部分+可选小数部分的格式
if (Regex.IsMatch(valueString.Trim(), @"^[-+]?\d+(\.\d+)?$") && 
    decimal.TryParse(valueString, strictStyles, cultureInfo, out valueParsed))
{
    return new ValidationResult(true, null);
}

问题2:输入“1,0”后值变为10

采纳建议修改验证规则后,输入“1,0”(意大利语文化下的1.0)时,ViewModel中的值变成了10而非预期的1.0。

相关代码

ViewModelData类

using System.ComponentModel;

namespace MyTest.models
{
    public class ViewModelData : INotifyPropertyChanged
    {
        private decimal? valoreDecimal;
        public decimal? ValoreDecimal
        {
            get { return valoreDecimal; }
            set
            {
                valoreDecimal = value + 0.0m;
                OnPropertyChanged(nameof(ValoreDecimal));
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

XAML代码

<TextBox 
    x:Name="ValoreDecimal"
    Width="130" 
    Height="40"
    HorizontalAlignment="Left" 
    VerticalAlignment="Top" 
    Margin="22,70,0,0" 
    Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="ValoreDecimal">
            <Binding.ValidationRules>
                <validators:DecimalValidationRule ValidationStep="RawProposedValue"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
    <Validation.ErrorTemplate>
        <ControlTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <AdornedElementPlaceholder x:Name="textBox" Grid.Column="0"/>
                <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red" Grid.Column="1" Margin="5,0,0,0"/>
            </Grid>
        </ControlTemplate>
    </Validation.ErrorTemplate>
</TextBox>

原因分析

WPF绑定默认使用当前线程的UI文化,如果线程文化是InvariantCulture(或与输入格式不匹配的文化),就会出现解析错误:

  • 意大利语文化(it-IT)中,逗号(,)是小数点分隔符;
  • 而InvariantCulture中,逗号是千位分隔符,因此“1,0”会被解析为10(把逗号当成千位分隔符,1,0即10)。

解决方法

  1. 在绑定中指定正确的ConverterCulture
    修改XAML中的Binding,明确使用当前系统文化:

    <!-- 添加命名空间 -->
    xmlns:SystemGlobalization="clr-namespace:System.Globalization;assembly=mscorlib"
    
    <!-- 修改Binding -->
    <Binding Path="ValoreDecimal" ConverterCulture="{x:Static SystemGlobalization:CultureInfo.CurrentCulture}">
        <Binding.ValidationRules>
            <validators:DecimalValidationRule ValidationStep="RawProposedValue"/>
        </Binding.ValidationRules>
    </Binding>
    
  2. 简化ViewModel中的赋值逻辑
    原代码中valoreDecimal = value + 0.0m;是多余的,直接赋值即可,该操作不会导致数值变化,但没必要保留:

    public decimal? ValoreDecimal
    {
        get { return valoreDecimal; }
        set
        {
            valoreDecimal = value;
            OnPropertyChanged(nameof(ValoreDecimal));
        }
    }
    
  3. 全局设置应用文化
    在App.xaml.cs的OnStartup方法中统一设置应用的文化,确保数值解析和格式化一致:

    using System.Globalization;
    using System.Threading;
    using System.Windows;
    
    namespace MyTest
    {
        public partial class App : Application
        {
            protected override void OnStartup(StartupEventArgs e)
            {
                var currentCulture = CultureInfo.CurrentCulture;
                Thread.CurrentThread.CurrentCulture = currentCulture;
                Thread.CurrentThread.CurrentUICulture = currentCulture;
                CultureInfo.DefaultThreadCurrentCulture = currentCulture;
                CultureInfo.DefaultThreadCurrentUICulture = currentCulture;
                base.OnStartup(e);
            }
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 23:44:53