WPF - Validação com IDataErroInfo


Em meu artigo - SilverLight - Fazendo a validação no databinding (C#) eu mostrei como podemos realizar a validação usando o evento BindingValidationError em aplicações SilverLight usando a linguagem C#.

No artigo de hoje eu mostro como implementar a validação de dados usando a interface IDataErrorInfo em aplicações WPF com C#. Lembrando que este recurso esta disponível a partir da versão 3.5.

Ao criar aplicações WPF com dados vinculados nunca devemos nos esquecer da importante tarefa de validar a entrada de dados (ou você vai deixar que dados inválidos sejam aceitos ?). A partir da versão 3.5 a WPF oferece um novo suporte a validação de dados fornecido pela interface IDataErrorInfo que pode ser usada para oferecer informações de erros personalizadas.

Você pode utilizar a interface IDataErrorInfo para que o WPF pergunte aos seu objetos de negócio se eles estão em um estado válido ou não. Isso remove a necessidade de colocar a lógica de negócio em objetos separados da camada de negócio e permite que você crie objetos independentes da plataforma da interface do usuário. A interface IDataErrorInfo não é nova ela existe desde os tempos do Windows Forms , e, isso facilita a reutilização de aplicações herdadas do Windows Forms.

A interface IDataErrorInfo fornece a funcionalidade para oferecer a informação de erros personalizados que pode ser vinculados a interface de usuário. Esta interface expõe os seguintes membros:

Nome Descrição
Error Obtem uma mensagem de erro indicando oque esta errado com o objeto
Item Obtém a mensagem de erro para a propriedade com o nome fornecido.

Para realizar a validação com IDataErrorInfo você tem que implementar a propriedade Item colocando a lógica de validação para cada propriedade que o seu modelo de validação exige. Feito isso a próxima etapa e definir a partir do código XAML a propriedade ValidatesOnDataErrors como True e decidir quando você quer que a lógica da validação seja invocada através de UpdateSourceTrigger.

Projeto para Validação com IDataErrorInfo

Vamos criar um projeto WPF usando o Visual Basic 2010 Express Edition no menu File->New Project selecionando o template WPF Application e informando o nome Alunos_Wpf;

Nosso projeto é bem simples: teremos uma interface definida com 1 Button, 3 Label, 3 TextBox onde iremos validar a entrada de dados para o código do aluno, nome do aluno e nota do aluno.

A seguir vemos o leiaute da janela WPF e logo a seguir o código XAML do arquivo MainWindow.xaml;

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Alunos" Height="300" Width="525"
    xmlns:src="clr-namespace:Alunos.Alunos_Wpf" WindowStartupLocation="CenterScreen">
    
    <Window.Resources>
        <src:Aluno x:Key="Alunos"></src:Aluno>
        <!--definindo o estilo da tooltip do erro  para o textbox-->
        <Style x:Key="txterror" TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                           Path=(Validation.Errors)[0].ErrorContent}">
                    </Setter>
                    <Setter Property="Background" Value="LightSalmon"></Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Grid>
        <Label Height="28" HorizontalAlignment="Left" Margin="12,27,0,0" Name="label1" VerticalAlignment="Top" Width="151"
 Content="Código do Aluno" FontSize="14" Foreground="Lime"></Label>
        <TextBox Height="23" Margin="262,27,96,0" Name="txtCodigo" VerticalAlignment="Top" Style="{StaticResource txterror}">
            <TextBox.Text>
                <Binding Path="Codigo" Source="{StaticResource Alunos}" 
                      ValidatesOnDataErrors="True"
                      UpdateSourceTrigger="PropertyChanged">
                      <Binding.ValidationRules>
                          <ExceptionValidationRule></ExceptionValidationRule>
                      </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        <Label Height="28" HorizontalAlignment="Left" Margin="12,72,0,0" Name="label2" VerticalAlignment="Top" Width="151" 
Content="Nome do Aluno" Foreground="#FF00F300" FontSize="14"></Label>
        <Label HorizontalAlignment="Left" Margin="12,122,0,112" Name="label3" Width="151" Content="Nota" FontSize="14" Foreground="Lime"></Label>
        <TextBox Height="23" Margin="262,74,96,0" Name="txtNome" VerticalAlignment="Top" Style="{StaticResource txterror}">
            <TextBox.Text>
                <Binding Path="Nome" Source="{StaticResource Alunos}"
                      ValidatesOnDataErrors="True"
                      UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule></ExceptionValidationRule>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        <TextBox Margin="262,127,96,112" Name="txtNota" Style="{StaticResource txterror}">
            <TextBox.Text>
                   <Binding Path="Nota" Source="{StaticResource Alunos}"
                      ValidatesOnDataErrors="True"
                      UpdateSourceTrigger="PropertyChanged">
                    <Binding.ValidationRules>
                        <ExceptionValidationRule></ExceptionValidationRule>
                    </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>
        <Button Height="32" Margin="166,0,197,31" Name="btnEnvia" VerticalAlignment="Bottom" Content="Enviar"></Button>
        <Grid.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="Black" Offset="0" />
                <GradientStop Color="#002DAEE1" Offset="0.988" />
            </LinearGradientBrush>
        </Grid.Background>
    </Grid>
</Window>

Definido o leiaute devemos criar a classe Aluno que será a fonte de dados vinculada via Resources.

No menu Project selecione Add Class e informe o nome Aluno.vb. A seguir defina o seguinte código :

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.ComponentModel

Namespace Alunos_Wpf

    Public Class Aluno
        Implements IDataErrorInfo

        Private _codigo As Integer

        Public Property Codigo() As Integer
            Get
                Return _codigo
            End Get
            Set(ByVal value As Integer)
                _codigo = value
            End Set
        End Property
        Private _nome As String = ""

        Public Property Nome() As String
            Get
                Return _nome
            End Get
            Set(ByVal value As String)
                _nome = value
            End Set
        End Property
        Private _nota As Integer
        Public Property Nota() As Integer
            Get
                Return _nota
            End Get
            Set(ByVal value As Integer)
                _nota = value
            End Set
        End Property

#Region "IDataErrorInfo Membros"

        Public ReadOnly Property [Error]() As String Implements IDataErrorInfo.Error
            Get
                Return Nothing
            End Get
        End Property

        Default Public ReadOnly Property Item(ByVal fieldName As String) As String Implements IDataErrorInfo.Item
            Get
                Dim resultado As String = Nothing

                'Validação do Código"
                If fieldName = "Codigo" Then
                    If Me._codigo < 0 Then
                        resultado = "O código do aluno não pode ser menor que 0"
                    End If
                End If

                'Validação do Nome
                If fieldName = "Nome" Then
                    If Me._nome = String.Empty Then
                        resultado = "O nome do aluno deve ser informado"
                    Else
                        For Each c As Char In Me._nome
                            If AscW(c) < 65 OrElse AscW(c) > 122 Then 'OrElse (c >= 91 AndAlso c <= 96) Then
                                resultado = "O Nome do aluno não deve conter caracteres especiais"
                            End If
                        Next c
                    End If
                End If

                'Validação da nota"
                If fieldName = "Nota" Then
                    If Me._nota < 0 Then
                        resultado = "A nota não pode ser menor que zero."
                    End If
                End If

                Return resultado
            End Get
        End Property

#End Region
    End Class
End Namespace

Neste código temos a definição da classe Aluno que contém as propriedades : Codigo, Nome e Nota;

A classe Aluno implementa a interface IDataErrorInfo e implementa as propriedades Item (somente leitura) e Error.

Na implementação de Item estamos definindo a lógica da validação para os campos Codigo , Nome e Nota.

O código XAML destacamos :

A utilização de Resources para definir a fonte de dados e o estilo da mensagem de erro para o usuário:

<Window.Resources>
        <src:Aluno x:Key="Alunos"></src:Aluno>
        <!--definindo o estilo da tooltip do erro  para o textbox-->
        <Style x:Key="txterror" TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                           Path=(Validation.Errors)[0].ErrorContent}">
                    </Setter>
                    <Setter Property="Background" Value="LightSalmon"></Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

Na tag : <src:Aluno x:Key="Alunos"></src:Aluno>

Estamos definindo a fonte de dados para a vinculação no código XAML aos controles TextBox.

A seguir temos a definição do estilo que será aplicado para exibir as mensagens de erro.

A seguir temos a declaração das tags XAML para definir cada controle usado na janela.

Abaixo vemos a tag para o campo Codigo onde temos:

O DataErrorValidationRule é regra de validação pré-definida que verifica se há erros que são gerados pela implementação IDataErrorInfo do objeto de origem.

A propriedade UpdateSourceTrigger obtém ou define um valor que determina o quando a fonte de dados vinculada será atualizada.

Os valores possíveis para UpdateSourceTrigger são:

Nome Descrição
Default O valor padrão da propriedade da propriedade destino vinculada. O valor padrão para a maioria das propriedades de dependência é PropertyChanged, enquanto a propriedade de texto tem um valor padrão igual a LostFocus;
PropertyChanged Atualiza a origem vinculada imediatamente sempre que a propriedade de destino vinculada sofre alteração;
LostFocus Atualiza a origem vinculada imediatamente sempre que o elemento de destino vinculado perder o foco;
Explicit Atualiza a origem vinculada somente quando for chamada o método UpdateSource;

Se você definir o valor Explicit para UpdateSourceTrigger deverá chamar o método UpdateSource ou as alterações não serão propagadas de volta a fonte.

O método UpdateSource envia o valor do destino atual vinculado para a propriedade de origem vinculada em vinculações TwoWay ou OneWaySource.

<TextBox Height="23" Margin="262,27,96,0" Name="txtCodigo" VerticalAlignment="Top" Style="{StaticResource txterror}">
            <TextBox.Text>
                <Binding Path="Codigo" Source="{StaticResource Alunos}" 
                      ValidatesOnDataErrors="True"
                      UpdateSourceTrigger="PropertyChanged">
                      <Binding.ValidationRules>
                          <ExceptionValidationRule></ExceptionValidationRule>
                      </Binding.ValidationRules>
                </Binding>
            </TextBox.Text>
        </TextBox>

O efeito pretendido é que quando uma regra de validação for violada a cor do controle seja alterada para um tom de vermelho (Salmão-LightSalmon) e uma tooltip seja exibida no controle conforme mostra a figura abaixo:

Acabei de mostrar como realizar validações em aplicações WPF com databinding usando a interface IDataErrorInfo. É claro que o assunto não acabou , pelo contrário, esta apenas começando.

Aguarde em breve mais artigos sobre validação em aplicações WPF.

Pegue o projeto completo aqui: Alunos_Wpf.zip

Eu sei é apenas WPF, mas eu gosto...

Referências:

José Carlos Macoratti