【问题标题】:WPF iDataErrorInfo (validating textboxes) not working as intendedWPF iDataErrorInfo(验证文本框)未按预期工作
【发布时间】:2017-06-06 23:27:16
【问题描述】:

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

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

我的班级设置了界面。摘录如下:

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) 被正确验证并显示错误工具提示。没有其他文本框。 不同的绑定和触发器并没有解决这个问题。任何帮助将非常感激。

【问题讨论】:

    标签: c# wpf idataerrorinfo


    【解决方案1】:

    拼写错误(您在开关中使用了小写字母):

    case "fname":
    

    但在您的绑定中:

    Text="{Binding Fname
    

    【讨论】:

      【解决方案2】:

      您似乎没有在属性的设置器中调用 OnPropertyChanged(nameof(FName));OnPropertyChanged(nameof(ID)); - 因此不会通知绑定已更新,并且不会调用 IDataErrorInfo.&lt;propertyName&gt;

      【讨论】:

        【解决方案3】:

        您可以使用:

        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();
                });
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-27
          • 2012-09-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多