开发者

WPF Validation and Controlling TextBox Styles

I have build my own custom validation framework for WPF using attribute based validation. I am stuck on the last step which is to highlight the TextBox. Actually, it does highlight the textboxes but all the textboxes are dependent on a single property HasError.

public class RegistrationViewModel  : ViewModel
    {
        [NotNullOrEmpty("FirstName should not be null or empty")] 
        public string FirstName { get; set; }

        [NotNullOrEmpty("Middle Name is required!")]
        public string MiddleName { get; s开发者_C百科et; } 

        [NotNullOrEmpty("LastName should not be null or empty")] 
        public string LastName { get; set; }

        public bool HasError
        {
            get
            {
                **return Errors.Count > 0; // THIS IS THE PROBLEM** 
            }
        }

    }

And here is the XAML code:

 <Style x:Key="textBoxStyle" TargetType="{x:Type TextBox}">                   

            <Style.Triggers>

                <DataTrigger Binding="{Binding Path=HasError}" Value="True">
                    <Setter Property="Background" Value="Red" />
                </DataTrigger>

            </Style.Triggers>

        </Style>

The problem with the above code is that it will highlight all the textboxes that uses "textBoxStyle" even though they are valid. This is because the HasError does not validate on individual property basis but as a whole.

Any ideas?

UPDATE 1:

The ViewModel contains the Errors collection:

 public class ViewModel : ContentControl, INotifyPropertyChanged
    {
        public static DependencyProperty ErrorsProperty; 

        static ViewModel()
        {
            ErrorsProperty = DependencyProperty.Register("Errors", typeof(ObservableCollection<BrokenRule>), typeof(ViewModel)); 
        }

        public ObservableCollection<BrokenRule> Errors
        {
            get { return (ObservableCollection<BrokenRule>)GetValue(ErrorsProperty); }
            set 
            { 
                SetValue(ErrorsProperty,value);
                OnPropertyChanged("HasError");
             }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

    }

UPDATE 2:

My validation engine:

public bool Validate(object viewModel)
        {
            _brokenRules = new List<BrokenRule>();

            // get all the properties 

            var properties = viewModel.GetType().GetProperties(); 

            foreach(var property in properties)
            {
                // get the custom attribute 

                var attribues = property.GetCustomAttributes(typeof (EStudyValidationAttribute), false); 

                foreach(EStudyValidationAttribute att in attribues)
                {
                    bool isValid = att.IsValid(property.GetValue(viewModel,null));

                    if(isValid) continue; 

                    // add the broken rule 

                    var brokenRule = new BrokenRule()
                                         {
                                             PropertyName = property.Name,
                                             Message = att.Message
                                         }; 

                    _brokenRules.Add(brokenRule);
                }

            }

            var list = _brokenRules.ToObservableCollection(); 

            viewModel.GetType().GetProperty("Errors").SetValue(viewModel,list,null);

            return (_brokenRules.Count() == 0); 
        }


You can implement IDataErrorInfo interface in your ViewModels, and in XAML check attached properties Validation.HasError on elements for controls with validation error; it's better because it's standart mechanizm in .Net.

<Style x:Key="textBoxStyle" TargetType="{x:Type TextBox}">                   

            <Style.Triggers>

                <DataTrigger Binding="{Binding Path=Validation.HasError}" Value="True">
                    <Setter Property="Background" Value="Red" />
                </DataTrigger>

            </Style.Triggers>

        </Style>

When binding to property in TextBoxes, you need to set binding ValidatesOnDataError property to true.

<TextBox x:Name="someTextBox" Text="{Binding Path=someProperty, ValidatesOnDataErrors=True}">



public class ViewModel : ContentControl, INotifyPropertyChanged,IDataErrorInfo
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        public string this[string propertyName]
        {
            get
            {
                return ValidateProperty(this,propertyName);
            }
        }

        public string Error
        {
            get
            {
                            return "";
            }
        }

    }

You can even use your implemented validation method, but check validation by property.


You might be interested in the WPF Application Framework (WAF) and the sample applications BookLibrary and EmailClient. They are using the validation attributes from the DataAnnotations namespace together with the IDataErrorInfo interface. You need only two additional lines of code in the validated classes to get this work (e.g. BookLibrary.Domain / Book.cs).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜