WPF - Validação de dados com IDataErrorInfo

No artigo de hoje vou mostrar uma maneira simples de realizar a validação de dados usando a interface IDataErrorInfo em uma aplicação WPF.

Existem diferentes maneiras de realizar a validação de dados em aplicações WPF e isso pode confundir um pouco quem esta iniciando no aprendizado dos recursos da WPF.

Assim, neste artigo eu vou mostrar uma forma bem simples e versátil de realizar a validação de dados usando a interface IDataErrorInfo que pode ser usada para oferecer informações de erros personalizadas.

A interface IDataErrorInfo pode ser usada para que a 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. Esse interface 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  Obtém uma mensagem de erro indicando o que 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.

Recursos usados:

Nota: Baixe e use a versão Community 2015 do VS ela é grátis e é equivalente a versão Professional.

Criando o projeto no VS Community

No exemplo deste artigo vou criar uma classe Funcionario com as propriedades Nome, Cargo e Salario e fazer a vinculação dos dados no XAML.  A validação será feita pela interface IDataErrorInfo e pelas propriedades ValidatesOnDataErrors e UpdateSourceTrigger

Abra o VS Community e clique em New Project;

Selecione a linguagem C# e o template WPF Application;

Informe o nome da solução como Validacao_WPF e clique no botão OK;

A seguir no menu Project clique em Add Class e informe o nome Funcionario.cs:

Inclua o código abaixo na classe Funcionario:

using System;
using System.ComponentModel;
namespace Validacao_WPF
{
    class Funcionario : IDataErrorInfo
    {
        public string Nome { get; set; }
        public string Cargo { get; set; }
        public int Salario { get; set; }
        public string Error
        {
            get { throw new NotImplementedException(); }
        }
        public string this[string nomeColuna]
        {
            get
            {
                string resultado = null;
                if (nomeColuna == "Nome")
                {
                    if (string.IsNullOrEmpty(Nome) || Nome.Length < 3)
                        resultado = "Informe o nome";
                }
                if (nomeColuna == "Cargo")
                {
                    if (string.IsNullOrEmpty(Cargo) || Cargo.Length < 3)
                        resultado = "Informe o cargo";
                }
                if (nomeColuna == "Salario")
                {
                    if (Salario <= 1000 || Salario >= 50000)
                        resultado = "Informe um valor válido para o Salário";
                }
                return resultado ;
            }
        }
    }
}

Neste código temos a definição da classe Funcionario que contém as propriedades : Nome, Cargo e Salario e que implementa a interface IDataErrorInfo e toda a lógica de validação é tratada no método indexer da classe Funcionario,

Agora abra o arquivo MainWindow.xaml e inclua o código XAML abaixo:

<Window x:Class="Validacao_WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Validacao_WPF"
        mc:Ignorable="d"
        Title="Funcionários" Height="250" Width="550">

    <Window.Resources>
        <Style TargetType="{x:Type Label}">
            <Setter Property="Margin" Value="5,0,5,0" />
            <Setter Property="HorizontalAlignment" Value="Left" />
        </Style>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Margin" Value="0,2,40,2" />
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="true">
                            <Border Background="OrangeRed" DockPanel.Dock="right" Margin="5,0,0,0" 
                                Width="20" Height="20" CornerRadius="5"
                                ToolTip="{Binding ElementName=customAdorner, 
                                          Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                                <TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" 
                                   FontWeight="Bold" Foreground="white" />
                            </Border>
                            <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
                                <Border BorderBrush="red" BorderThickness="1" />
                            </AdornedElementPlaceholder>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Grid x:Name="grid_FuncionariosDados" Margin="0,20">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <Grid.CommandBindings>
            <CommandBinding Command="New" CanExecute="Confirm_CanExecute" Executed="Confirm_Executed" />
        </Grid.CommandBindings>
        <Label Target="{Binding ElementName=textBox_Nome}" Content="Nome:" Grid.Column="0" Grid.Row="0" />
        <Label Target="{Binding ElementName=textBox_Cargo}" Content="Cargo:" Grid.Column="0" Grid.Row="1" />
        <Label Target="{Binding ElementName=textBox_Salario}" Content="Salario:" Grid.Column="0" Grid.Row="2" />
        <TextBox x:Name="textBox_Nome" Grid.Row="0" Grid.Column="1" 
             Validation.Error="Validation_Error"
             Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=Nome,
                    ValidatesOnDataErrors=true, NotifyOnValidationError=true}" />

        <TextBox x:Name="textBox_Cargo" Grid.Row="1" Grid.Column="1" 
             Validation.Error="Validation_Error"
             Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=Cargo, 
                    ValidatesOnDataErrors=true, NotifyOnValidationError=true}" />

        <TextBox x:Name="textBox_Salario" Grid.Row="2" Grid.Column="1" 
             Width="50" HorizontalAlignment="left" Validation.Error="Validation_Error" MaxLength="5"
             Text="{Binding UpdateSourceTrigger=PropertyChanged, Path=Salario, 
                    ValidatesOnDataErrors=true, NotifyOnValidationError=true}" />
        <Button Content="Confirmar" Grid.ColumnSpan="2" Grid.Row="3" Grid.Column="1" Margin="0,0,10,0"
            HorizontalAlignment="right" VerticalAlignment="Center" Command="New"/>
    </Grid>
</Window>

Observe que a propriedade UpdateSourceTrigger foi definida como PropertyChanged e isso faz com que o processo de validação ocorra a cada vez que o texto no interior da caixa de texto (TextBox) for alterado.

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;

O método Validation_Error calcula o número de erros para todo o Grid e assim o método Confirm_CanExecute sabe quando definir o parâmetro CanExecute para true, e isso indica que o botão Confirmar fica habilitado e o método Confirm_Executed pode ser processado.

Note também que definimos um estilo para o TextBox que indica a aparência das mensagens de validação.

Para completar falta implementar o código no code-behind do arquivo MainWindow.xaml.cs que faz com que tudo funcione corretamente:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Validacao_WPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private int _errors = 0;
        private Funcionario _funcionario = new Funcionario();
        public MainWindow()
        {
            InitializeComponent();
            grid_FuncionariosDados.DataContext = _funcionario;
        }
        private void Confirm_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _errors == 0;
            e.Handled = true;
        }
        private void Confirm_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            _funcionario = new Funcionario();
            grid_FuncionariosDados.DataContext = _funcionario;
            e.Handled = true;
        }
        private void Validation_Error(object sender, ValidationErrorEventArgs e)
        {
            if (e.Action == ValidationErrorEventAction.Added)
                _errors++;
            else
                _errors--;
        }
    }
}

Executando o projeto iremos obter:

Pegue o projeto completo aqui :   Validacao_WPF.zip

(Disse Jesus) "Eu sou a videira, vós as varas; quem está em mim, e eu nele, esse dá muito fruto; porque sem mim nada podeis fazer.
Se alguém não estiver em mim, será lançado fora, como a vara, e secará; e os colhem e lançam no fogo, e ardem."

João 15:5,6

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti