Skip to content

ValidationProxy

Sebastian Cygan edited this page Nov 8, 2020 · 3 revisions

Validation proxy

Why should I use it

The validation proxy is currently the most flexible but manageable way to provide validation process in your application with Forvalidate. Although it is especially designed for the best integration with WPF (MVVM/MVP), you can use it in any .Net project with data model validation requirement.

  • The validation proxy maintains the process of validators instancing - prevents from multi-instancing of the same validator if it is not needed.
  • With proxy you can extend your data model object with some metadata - it is used by the library itself to intercept UI validation exceptions.
  • The validation proxy provides loose coupled relation between data model, viem model (presenter) and view without any additional requirements for interface implementing in these classes.
  • Extensions for the validation proxy (provided by extension methods) allows developers to prepare simpler self-documented code.

How to use it

This is a sample data model class - implementation for methods and properties is not provided here for simplicity. As you can see, there are no interface implementations or class inheritance needed. Thanks to loose coupling provided by the validation proxy such elements is not needed.

Public Class Task
    Public Property TaskID() As Int32
    Public Property CreateDate() As DateTime
    Public Property AssignDate() As DateTime?
    Public Property Priority() As Byte
    Public Sub Save()
End Class

Below, there is a sample validator class.

Imports Fortedo.ForValidate
Imports Fortedo.ForValidate.Conditions

Public Class TaskValidator : Inherits FvValidatorBase
   Public Sub New()
       AddRule("CreateDate").Ensure(Function(d As DateTime) d >= #1/1/2010#))
       AddRule("AssignDate").Test(Function(t As Task) t.AssignDate Is Nothing OrElse t.AssigneDate >= t.CreateDate)
   End Sub
End Class

And finally, there is some code using the data model and validator classes.

Imports Fortedo.ForValidate.Wpf

Public Class TaskPresenter
    Private _task As Task
    Public Sub New()
        _task = New Task
        'set proxy for a task object and associate it with the new instance of TaskValidator validator
        FvProxy.Connect(Of TaskValidator)(_task)
    End Sub
    Public Sub ValidateAndSave()
        'note that validation is made using proxy - framework will use previously associated TaskValidator instance
        Dim result As ValidationResult = FvProxy.Validate(_task)
        If result.IsValid Then
            _task.Save
        Else
            MessageBox.Show("There are some errors in task properties.")
        End if
    End Sub
    Public Sub Close()
        'you should disconnect proxy for an object if you do not need it anymore
        FvProxy.Disconnect(_task)
    End Sub
End Class

Magical WPF integration

The WPF is a very flexible and rich framework but extensibility of the validation is really painful task. Although you could use some IDataErrorInfo interface or custom validation rules, there are still some blockers and you cannot get what you want. This is where another part of Forvalidate framework can help you - in the Fortedo.Forvalidate.Wpf namespace there is a special WPF Binding extender - it is the FvBinding class. You can use it every place where you bind object you want to be validated by Forvalidate.

  • You can use FvBinding just like the standard one binding - as a markup extension or markup element, it is possible to use it in a code either.
  • FvBinding is strictly connected with the validation proxy - the other is used to determine which validator to use for validating bound objects.
  • If some exception is invoked while bound property is about to be updated, FvBinding will propagate it to the validadation proxy so retrieved validation result would be extended to UI exceptions.
  • On the other hand, all the rules defined in the associated validator (and even in the nested ones) are executed after every change of the bound property. The results are transferred to the UI and can be used there (e.g. in Validation.ErrorTemplate).
  • FvBinding keeps invalidated state of UI controls in such situations where WPF "forgets" about it.

After the longish introduction... high time for an example.

<?xml version="1.0" encoding="utf-8"?>
<UserControl x:Class="Views.DetailsView" 
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:for="http://forvalidate.fortedo.com">
    <!-- There is URL Forvalidate namespace created for your convenience -->

    <UserControl.Resources>
        <!-- It is a little gift from Forvalidate that converts errors to more user-friendly form -->
        <for:ErrorsToStringConverter x:Key="ErrorsToStringConverter"/>
        <!-- We prepare Validation.ErrorTemplate for each TextBox -->
        <!-- As you can see, it is possible to use FvBinding and default WPF mechanisms together -->
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="true">
                            <Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" CornerRadius="10">
                                <TextBlock VerticalAlignment="center" HorizontalAlignment="Center"
                                           FontWeight="Bold" Foreground="White">
                                    <TextBlock.Text>
                                        <!-- You do not need using FvBinding here - the property is not validate -->
                                        <Binding ElementName="customAdorner"
                                                 Path="AdornedElement.(Validation.Errors)"
                                                 Converter="{StaticResource ErrorsToStringConverter}}"/>
                                    </TextBlock.Text>
                                </TextBlock>
                            </Border>
                            <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center">
                                <Border BorderBrush="red" BorderThickness="1" />
                            </AdornedElementPlaceholder>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>

    <Grid>
        <!-- Assume there is FvProxy connected to the Task object in data context -->
        <StackPanel DataContext="{Binding Task}" Orientation="Vertical">
            <!-- You simply use FvBinding instead of the standard Binding -->
            <!-- Do not bother about any other things - Forvalidate will do the rest -->
            <TextBox Text="{for:FvBinding CloseDate, StringFormat=yyyy-MM-dd}"/>
            <TextBox Text="{for:FvBinding Case.ReceiveDate, StringFormat=yyyy-MM-dd}"/>
        </StackPanel>
    </Grid>
</UserControl>

Clone this wiki locally