WPF iDataErrorInfo(验证文本框)未按预期工作

时间:2017-06-06 23:27:16

标签: c# wpf idataerrorinfo

更新:我通过改进我的switch语句来解决这个问题。使用名称

我正在尝试从文本框验证一组用户输入。

我的班级设置了界面。它的片段如下:

public class PatientValidation : INotifyPropertyChanged, IDataErrorInfo
    {
        private string _id;
        private string _fname;

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string p)
        {   
            PropertyChangedEventHandler ph = PropertyChanged;
            if (ph != null)
            {
                ph(this, new PropertyChangedEventArgs(p));
            }

        }

        public string Id
        {
            get
            {
                return _id;
            }    
            set
            {
                _id = value;
            }
        }
        public string Fname
        {
            get
            {
                return _fname;
            }    
            set
            {
                _fname = value;
            }
        }

并根据用户输入返回错误消息的switch语句:

public string this[string PropertyName]
        {
            get
            {
                string result = null;

                switch (PropertyName)
                {
                    case "Id":
                        if (string.IsNullOrEmpty(Id))
                            result = "ID number is required.";
                        break;

                    case "fname":
                        if (string.IsNullOrEmpty(Fname))
                            result = "First name is required.";
                        break;
                  }
                    return result;
             }
          }

我在XAML中的相关代码:

<Style TargetType="TextBox">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>

<TextBox x:Name="textBox_IDNumber" Text="{Binding Id, Mode=TwoWay, ValidatesOnDataErrors=True}"/>
<TextBox x:Name="textBox_FirstName" Text="{Binding Fname, Mode=TwoWay, ValidatesOnDataErrors=True"}/>

这是我遇到的问题:只有第一个文本框(ID)被正确验证,并显示错误工具提示。没有其他文本框。 不同的绑定和触发器还没有解决问题。任何帮助将非常感激。

3 个答案:

答案 0 :(得分:1)

错字错误(您在交换机中使用小写字母):

case "fname":

但在你的装订中:

Text="{Binding Fname

答案 1 :(得分:1)

您似乎没有在属性的设置者中调用OnPropertyChanged(nameof(FName));OnPropertyChanged(nameof(ID)); - 因此不会通知绑定已更新,IDataErrorInfo.<propertyName>将会不被称为。

答案 2 :(得分:0)

您可以使用:

public class DataErrorInfoWrapper : DynamicObject, IDataErrorInfo, INotifyPropertyChanged
{
    private static readonly ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>> Properties = new ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>>();
    private readonly Dictionary<string, PropertyInfo> _typeProperties;
    private readonly Func<string, string> _error;
    private readonly object _target;

    public string this[string columnName] => _error(columnName);

    public string Error { get; }

    public event PropertyChangedEventHandler PropertyChanged;

    public DataErrorInfoWrapper(object target, Func<string, string> error)
    {
        _error = error;
        _target = target;
        _typeProperties = Properties.GetOrAdd(_target.GetType(), t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(i => i.SetMethod != null && i.GetMethod != null).ToDictionary(i => i.Name));
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = null;
        if (!_typeProperties.TryGetValue(binder.Name, out var property))
            return false;
        var getter = property.CreateGetter();
        result = getter.DynamicInvoke(_target);
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (!_typeProperties.TryGetValue(binder.Name, out var property))
            return false;
        var setter = property.CreateSetter();
        setter.DynamicInvoke(_target, value);
        RaisePropertyChanged(binder.Name);
        return true;
    }

    protected virtual bool SetProperty<TValue>(ref TValue storage, TValue value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<TValue>.Default.Equals(storage, value))
            return false;
        storage = value;
        RaisePropertyChanged(propertyName);
        return true;
    }

    protected virtual bool SetProperty<TValue>(ref TValue storage, TValue value, Action onChanged, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<TValue>.Default.Equals(storage, value))
            return false;
        storage = value;
        onChanged?.Invoke();
        RaisePropertyChanged(propertyName);
        return true;
    }

    protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        OnPropertyChanged(propertyName);
    }

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

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
}

和扩展方法:

public static class TypeExtensions
{
    private static readonly ConcurrentDictionary<PropertyInfo, Delegate> Getters = new ConcurrentDictionary<PropertyInfo, Delegate>();
    private static readonly ConcurrentDictionary<PropertyInfo, Delegate> Setters = new ConcurrentDictionary<PropertyInfo, Delegate>();

    public static Delegate CreateGetter(this PropertyInfo property)
    {
        return Getters.GetOrAdd(property, p =>
        {
            var parameter = Expression.Parameter(p.DeclaringType, "o");
            var delegateType = typeof(Func<,>).MakeGenericType(p.DeclaringType, p.PropertyType);
            var lambda = Expression.Lambda(delegateType, Expression.Property(parameter, p.Name), parameter);
            return lambda.Compile();
        });
    }

    public static Delegate CreateSetter(this PropertyInfo property)
    {
        return Setters.GetOrAdd(property, p =>
        {
            var parameter = Expression.Parameter(p.DeclaringType, "o");
            var valueParm = Expression.Parameter(p.PropertyType, "value");
            var delegateType = typeof(Action<,>).MakeGenericType(p.DeclaringType, p.PropertyType);
            var lambda = Expression.Lambda(delegateType, Expression.Assign(Expression.Property(parameter, p.Name), valueParm), parameter, valueParm);
            return lambda.Compile();
        });
    }
}