Hi,
WPF provides two main ways for implementing data validation: ValidationRule and IDataErrorInfo. In this post is focused on IDataErrorInfo.

The demo project is available here and the below images displays the result from an UI point of view:

Using ValidationRule you are able to validate user inputs directly into UI (View). This way consists in implementing a class that extends .Net ValidationRule and using your class into Binding. You can check an example here.

Using IDataErrorInfo you are able to validate user inputs in both UI and Business Logic too (ViewModel). This way is more MVVM oriented and allow performing validation in different layers (View and ViewModel) sharing a common validation code.

The first step consists into implementing IDataErrorInfo interface into the class that contains a Property that will be involved in UI binding. IDataErrorInfo consists into two properties: the Error property that tells what is wrong with this object, and the Item property that gets the error message of the specified property. Regardless of your object you can implement IDataErrorInfo interface in the following way:

        #region IDataErrorInfo Members

        string IDataErrorInfo.Error { get { return null; } }

        string IDataErrorInfo.this[string propertyName]
        {
            get { return this.GetValidationError(propertyName); }
        }

        #endregion // IDataErrorInfo Members

You might ask “why”? The answer is easier you can imagine: the GetValidationError method we are going to implement will be also used by IsValid property into other layers (typically ViewModel).
In other words the IDataErrorInfo is used by .Net into binding expressions, while IsValid property can be used into code behind layers.

Here below the rest of the code:

        public bool IsValid
        {
            get
            {
                foreach (string property in ValidatedProperties)
                    if (GetValidationError(property) != null)
                        return false;

                return true;
            }
        }

        static readonly string[] ValidatedProperties = 
        { 
            "ServerIPAddress"
        };

        private string GetValidationError(string propertyName)
        {
            string error = null;

            switch (propertyName)
            {

                case "ServerIPAddress":
                    error = ValidateIPAddress();
                    break;

            }

            return error;
        }

        private string ValidateIPAddress()
        {

            if (String.IsNullOrEmpty(ServerIPAddress))
            {
                return "Please enter an IP Address.";
            }

            if (ServerIPAddress.Contains(" "))
            {
                return "Blank characters are not allowed in IP Address.";
            }

            string[] parts = ServerIPAddress.Split('.');
            if (parts.Length != 4)
            {
                return "IP Address should be four octets, seperated by decimals.";
            }

            foreach (string p in parts)
            {
                int intPart;

                if (!int.TryParse(p, out intPart))
                {
                    return "Each octet of an IP Address should be a number.";
                }

                if (intPart < 0 || intPart > 255)
                {
                    return "Each octet of an IP Address should be between 0 and 255.";
                }
            }

            return null;
        }

Finally the following XAML Code is used for validating data into the View:

        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="True">
                            <TextBlock Text="*" 
                                       Foreground="Red"  
                                       DockPanel.Dock="Right" 
                                       Width="16" Height="16" 
                                       Margin="4,0,0,0" />
                            <Border BorderBrush="Red" BorderThickness="1">
                                <AdornedElementPlaceholder />
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" 
                            Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
...
        <TextBox MaxLength="15"
                    Width="150" 
                    Text="{Binding Path=MyDummyItem.ServerIPAddress, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />

Stay tuned and happy coding!

Advertisements