首页 > 解决方案 > 处理无法转换 WPF 中双属性的值

问题描述

情况

我有文本框,它绑定到类型为double.

当用户尝试在框中输入字母时,会显示:

在此处输入图像描述

而且,这显示在绑定失败中。

在此处输入图像描述

我目前有这个验证来检查null价值并且工作正常。

public Dictionary<string, string> Errors { get; } = new Dictionary<string, string>();

public string this[string propertyName]
{
    get
    {
        CollectionErrors();
        return Errors.ContainsKey(propertyName) ? Errors[propertyName] : string.Empty;
    }
}

private void CollectionErrors()
{
    Errors.Clear();
    if(NumberProp == null)
    {
        Errors.Add(nameof(NumberProp), "This value cannot be empty");
    }
}

public string Error => string.Empty;

public bool HasErrors => !Errors.Any(); // To determine if there are any errors

输出

在此处输入图像描述

问题

我该如何处理could not be converted

到目前为止我试过这个:

if(NumberProp.GetType() == typeof(string))
{
    Errors.Add(nameof(NumberProp), "NumberProp cannot contain any letters!");
}

但是得到这个错误:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

为什么我需要这个

我有一个按钮,在验证失败时禁用或在一切正常时启用。

SendDataCommand = new RelayCommand((x) => { SendData(); }, (y) => HasErrors);

显示错误时启用该按钮Value '' could not be converted.

编辑 1

XAML

 <TextBox Text="{Binding NumberProp, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True, TargetNullValue=''}" />

财产

private double? _numberProp;

public double? NumberProp
{
    get { return _numberProp; }
    set
    {
        _numberProp = value;
        OnPropertyChanged(nameof(NumberProp));
    }
}

标签: wpfvalidationmvvm

解决方案


    private void CollectionErrors()
    {
        Errors.Clear();
        if (string.IsNullOrWhiteSpace(NumberProp))
        {
            Errors.Add(nameof(NumberProp), "This value cannot be empty");
        }
        else if (decimal.TryParse(NumberProp, out _))
        {
            Errors.Add(nameof(NumberProp), "NumberProp is not a decimal number!");
        }
    }

最好不要完全清除所有错误,而只清除选中属性的错误:

public Dictionary<string, string> Errors { get; } = new Dictionary<string, string>();

public string this[string propertyName]
{
    get
    {
        CollectionErrors(propertyName);
        return Errors.ContainsKey(propertyName) ? Errors[propertyName] : string.Empty;
    }
}

private void CollectionErrors(string propertyName)
{
    Errors.Remove(propertyName);
    if (propertyName == nameof(NumberProp))
    {
        if (string.IsNullOrWhiteSpace(NumberProp))
        {
            Errors.Add(nameof(NumberProp), "This value cannot be empty");
        }
        else if (decimal.TryParse(NumberProp, out _))
        {
            Errors.Add(nameof(NumberProp), "NumberProp is not a decimal number!");
        }
    }
}

public string Error => string.Join(Environment.NewLine, Errors.Select(pair => $"[{pair.Key}]=\"{pair.Value}\""));

按照约定(合同)INotifyPropertyChanged 接口,PropertyChanged 事件仅应在属性更改时引发。
因此,正确的实现应该是这样的:

    private double _number;

    public double Number
    {
        get { return _number; }
        private set
        {
            if (Equals(_number, value))
                return;
            _number = value;
            OnPropertyChanged(nameof(Number));
        }
    }
    private string _numberProp;

    public string NumberProp
    {
        get { return _numberProp; }
        set
        {
            if (Equals(_numberProp, value))
                return;
            _numberProp = value;
            OnPropertyChanged(nameof(NumberProp));
            if (double.TryParse(value, out double number))
                Number = number;
        }
    }

具有两个绑定属性的类实现的完整示例。
NumberString - 用于绑定到 TextBox。
Number - 包含转换为 double 的 NumberString 属性的值。
对这两个属性都实施了错误检查。
还要注意 [CallerMemberName] 属性。
它允许您不在方法中指定属性名称。

public class Data : INotifyPropertyChanged, IDataErrorInfo
{

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    private double _number;

    public double Number
    {
        get { return _number; }
        private set
        {
            if (Equals(_number, value))
                return;
            _number = value;
            CollectionErrors();
            OnPropertyChanged();
        }
    }


    private string _numberProp;

    public string NumberProp
    {
        get { return _numberProp; }
        set
        {
            if (Equals(_numberProp, value))
                return;
            _numberProp = value;
            CollectionErrors();
            OnPropertyChanged();
            if (double.TryParse(value, out double number))
                Number = number;
        }
    }
    public Dictionary<string, string> Errors { get; } = new Dictionary<string, string>();

    public string this[string propertyName] => Errors[propertyName];

    private void CollectionErrors([CallerMemberName] string propertyName = null)
    {
        if (propertyName == null)
            return;

        Errors.Remove(propertyName);
        if (propertyName == nameof(NumberProp))
        {
            if (string.IsNullOrWhiteSpace(NumberProp))
            {
                Errors[nameof(NumberProp)] = "This value cannot be empty";
            }
            else if (decimal.TryParse(NumberProp, out _))
            {
                Errors[nameof(NumberProp)] = "NumberProp is not a decimal number!";
            }
        }
        else if (propertyName == nameof(Number))
        {
            if (Number < 0 || Number > 1000)
                Errors[nameof(Number)] = "The number must be in the range 0 ... 1000!";
        }
    }

    public string Error => string.Join(Environment.NewLine, Errors.Select(pair => $"[{pair.Key}]=\"{pair.Value}\""));
}

推荐阅读