Property validation, IDataErrorInfo "Instance"

Sep 1, 2011 at 12:52 PM
Edited Sep 1, 2011 at 12:54 PM

Problem definition:

  • I wanted to validate fields in WPG
  • I didn't want to make big changes in WPG implementation
  • I wanted to put WPG into "OK|Cancel" dialog and disable "OK" button if values in property grid are not valid.
  • Instance of my edited object implements IDataErrorInfo

Here is my Solution:

  1. Implementation IDataErrorInfo on Property.
  2. Implementation IsValid and Error on PropertyGrid.
  3. Set validation rules for bindings  in Templates.

Implementation: 

  • Implementation IDataErrorInfo on Property

 

		#region IDataErrorInfo Members

		public string Error
		{
			get 
			{
				if (this._instance is IDataErrorInfo)
				{
					return (_instance as IDataErrorInfo).Error;
				}
				else 
					return null;
			}
		}

		public string this[string columnName]
		{
			get 
			{
				if (this._instance is IDataErrorInfo)
				{
					return (_instance as IDataErrorInfo)[this._property.Name];
				}
				else
					return null;
			}
		}

		#endregion


  • Implementation IsValid and Error on PropertyGrid

In PropertyGrid i need to know about every change in my internal Instance to provide global "IsValid" and "Error" property. So I Added this code snippet to PropertyGrid.

 

        /// <summary>
        /// Invoked on Instance change.
        /// </summary>
        /// <param name="d">The object that was changed</param>
        /// <param name="e">Dependency property changed event arguments</param>
        private static void OnInstanceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var propertyGrid = d as PropertyGrid;

            propertyGrid.SelectedProperty = null;

            propertyGrid.Refresh();

            propertyGrid.RaiseInstanceChanged();

            //My method. Hook to new instance
            propertyGrid.InstanceObjectChanged(e.NewValue, e.OldValue);
            
            //First time initialize an Error
            propertyGrid.InstancePropertyChanged(propertyGrid.Instance, null);
        }

        /// <summary>
        /// Registers my PropertyChanged event listener to Instance object.
        /// Unregisters my PropertyChaned event listener from old Instance.
        /// </summary>
        private void InstanceObjectChanged(object newInstace, object oldInstance)
        {
            if (newInstace is INotifyPropertyChanged)
            {
                (newInstace as INotifyPropertyChanged)
                    .PropertyChanged += InstancePropertyChanged;
            }

            if (oldInstance is INotifyPropertyChanged)
            {
                (newInstace as INotifyPropertyChanged)
                    .PropertyChanged -= InstancePropertyChanged;
            }
        }

        /// <summary>
        /// Change the Error property.
        /// </summary>
        void InstancePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.Error = Instance is IDataErrorInfo ?
                     (Instance as IDataErrorInfo).Error :
                     string.Empty;
        }


        #region IsValid Dependency Property
        
        public bool IsValid
        {
            get { return (bool)GetValue(IsValidProperty); }
            set { SetValue(IsValidProperty, value); }
        }

        public static readonly DependencyProperty IsValidProperty = 
            DependencyProperty.Register("IsValid", typeof(bool), typeof(PropertyGrid), new UIPropertyMetadata(true));

        #endregion

        #region Error Dependency Property
        public string Error
        {
            get { return (string)GetValue(ErrorProperty); }
            set { SetValue(ErrorProperty, value); }
        }

        public static readonly DependencyProperty ErrorProperty = 
            DependencyProperty.Register("Error", typeof(string), typeof(PropertyGrid), new UIPropertyMetadata(string.Empty, OnErrorChanged));

        //When Error changes we need to change IsValid property
        private static void OnErrorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PropertyGrid pg = d as PropertyGrid;
            if (pg.Instance is IDataErrorInfo)
            {
                pg.IsValid = string.IsNullOrEmpty((pg.Instance as IDataErrorInfo).Error);
            }
        }
        #endregion

  • Set validation rules for bindings in Templates.

Finally i needed to set Validation rules to Templates to show error templates on every field:

 

                    <Binding.ValidationRules>
                        <DataErrorValidationRule/>
                        <ExceptionValidationRule/>
                    </Binding.ValidationRules>

 

on every template: Here is for example "default" template:

 

	<DataTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:PropertyGrid}, ResourceId=default}">
		<TextBox IsReadOnly="{Binding Path=IsReadOnly}" Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type local:PropertyGrid}, ResourceId=TextBoxStyle}}">
			<TextBox.Text>
				<Binding Mode="TwoWay" Path="Value">
					<Binding.ValidationRules>
						<DataErrorValidationRule/>
						<ExceptionValidationRule/>
					</Binding.ValidationRules>
				</Binding>
			</TextBox.Text>
		</TextBox>
	</DataTemplate>

 

 

Jun 29, 2012 at 7:44 AM

Hi,

 I am a newbie.. Could u please explain everything fully rather than telling it in parts.. I dont know where to put the data template that u mentioned in this discussion. It would be nice if u explain the above code through a simple example again..

 

Thanks,

Nadal

Jun 29, 2012 at 9:03 AM

Hello Rafael,

I will start from down down to up the last piece of code has number 1:

1. piece is located should be located in WPGTemplates.xaml (Themes/THEME/WPGTemplates.xaml)

This piece causes that default template (textbox) starts normal wpf validation. Before that there was only ExceptionValidateRule or nothing. I wanted to use DataErrorValidationRule.

2. You should add this to other editors templates if you want to use them. again in WPGTemplates.xaml or in custom templates. It is standard in WPF.

3. I wanted to create form, that you can't close until your property grid is valid. So if you want to do something like that, just add this code to the propertyGrid.cs and bind for example enabled property of "ok" button to IsValid property.

4. This is implementation of IDataErrorInfo interface on "Property" class. It causes validation only if yours instance object implements IDataErrorInfo.

 

So Example: Just add this code to the concreate files (WPGTemplates.xaml, PropertyViewModel, PropertyGrid. And try to show any object which implements IDataErrorInfo interface. If you have any validation error there yours textxtboxes appears "red" bordered.

Actualy i stopped using WPG. I don't want to write to this forum the reasons. if you want to know my reasons, and solution write me an email:

\P\E\T\E\R\.\L\A\P\I\N\.\1\1\8\@\G\M\A\I\L\.\C\O\M

WITHOUT SLASHES...

 

 

 

Jul 2, 2012 at 12:53 PM

Hi hisss,

Thanks for your help...

-Rafael