Este artigo foi traduzido por máquina.

Validação de entradas

Impondo regras de dados comerciais complexas com o WPF

Brian Noyes

Baixe o código de exemplo

Microsoft Windows Presentation Foundation (WPF) possui um sistema de ligação de dados sofisticado. Além de ser um fator fundamental para menos rigidez da definição de interface do usuário da lógica de suporte e dados através do padrão Model-View-ViewModel (MVVM), o sistema de ligação de dados tem suporte eficiente e flexível para cenários de validação de dados comerciais. Os mecanismos de ligação de dados no WPF incluam várias opções para avaliar a validade dos dados de entrada quando você cria um modo de exibição editável. Além disso, recursos de estilo para controles e modelos do WPF lhe dão a capacidade para facilmente personalizar a maneira que você indicar erros de validação para o usuário.

Para oferecer suporte a regras complexas e exibir erros de validação para o usuário, geralmente, você precisará empregar uma combinação dos mecanismos de validação disponíveis. Até mesmo um formulário de entrada de dados aparentemente simples pode apresentar desafios de validação quando as regras comerciais complexas. Situações comuns envolvem as duas regras simples em um nível de propriedade individual e propriedades combinado de cruz, onde a validade de uma propriedade depende do valor da propriedade de outro. No entanto, a validação de suporte em faz de vinculação de dados do WPF fáceis de resolver esses desafios.

Neste artigo, você aprenderá a usar IDataErrorInfo implementação de interface, ValidationRules, BindingGroups, exceções e propriedades anexadas relacionados a validação e eventos para a validação de dados precisa de endereço. Você também verá como personalizar a exibição dos erros de validação com o seu próprio ErrorTemplates e dicas de ferramentas. Neste artigo, suponho que você já estiver familiarizado com os recursos de ligação de dados básicos do WPF. Para obter mais informações detalhadas sobre o que, de dezembro de 2007 MSDN Magazine , de John Papa consulte artigo “ de vinculação de dados no WPF ”.

Visão geral sobre validação de dados

Quase sempre você inserir ou modificar dados em um aplicativo, você precisa garantir que os dados são válidos antes de chegar muito longe da fonte dessas alterações — nesse caso, o usuário. Além disso, você precisa dar aos usuários uma indicação clara quando os dados inseridos são inválidos e espera-se também dar-lhes alguma indicação de como corrigi-lo. Essas coisas são bastante fácil de fazer com o WPF, desde que você conhece a capacidade de usar e quando.

Quando você usa a vinculação de dados no WPF para apresentar dados de negócios, você geralmente usa um objeto de vinculação para fornecer um canal de dados entre uma única propriedade em um controle de destino e uma propriedade de objeto de fonte de dados. Para validação a ser relevantes, que normalmente está fazendo a ligação de TwoWay dados — o que significa que, juntamente com os dados que fluem da propriedade de origem para a propriedade de destino para exibição, os dados editados também fluem de destino para origem conforme mostrado no do Figura 1.

Figure 1 Data Flow in TwoWay Data Binding
Figura 1 de fluxo de dados em dados de TwoWay ligação

Existem três mecanismos para determinar se os dados inseridos por meio de um controle vinculado a dados são válidos. Estes são resumidas no do Figura 2.

De mecanismos de validação de ligação, a Figura 2

Mecanismo de validação Descrição
Exceções Definindo a propriedade ValidatesOnExceptions em um objeto de vinculação, se uma exceção é gerada no processo de tentativa de definir o valor modificado na propriedade de objeto de origem, um erro de validação será definido para essa ligação.
ValidationRules A classe Binding possui uma propriedade para fornecer uma coleção de instâncias de classes derivadas de ValidationRule. Esses ValidationRules precisará substituir um método validar que será chamado por ligação sempre que os dados no controle de limite forem alterados. Se o método Validate retorna um objeto ValidationResult inválido, um erro de validação é definido para essa ligação.
IDataErrorInfo Implementando a interface IDataErrorInfo no objeto de fonte de dados vinculado e definindo a propriedade ValidatesOnDataErrors em um objeto de vinculação, a Binding fará com que chamadas à API IDataErrorInfo expostas a partir do objeto de fonte de dados vinculado. Se as seqüências de caracteres não-nulos ou vazios não são retornadas dessas chamadas de propriedade, um erro de validação é definido para essa ligação.

Quando um usuário insere ou modifica dados na ligação de TwoWay dados, um fluxo de trabalho ativa:

  • Dados são inseridos ou modificados pelo usuário através de pressionamentos de teclas, mouse, toque ou caneta interação com o elemento, resultando em uma alteração de uma propriedade no elemento.
  • Dados são convertidos para o tipo de propriedade da fonte de dados, se necessário.
  • O valor da propriedade fonte está definido.
  • O Binding.SourceUpdated anexado evento é acionado.
  • Exceções são detectadas por ligação se gerada por um setter na propriedade da fonte de dados e podem ser usadas para indicar um erro de validação.
  • Propriedades de IDataErrorInfo são chamadas no objeto de fonte de dados, se implementada.
  • Indicações de erro de validação são apresentadas ao usuário e o evento Validation.Error anexado é acionado.

Como você pode ver, há vários pontos durante o processo em que poderá gerar os erros de validação, dependendo de qual mecanismo escolhido. Não é exibido na lista é onde a ValidationRules acionado. Que é porque eles podem ser disparado em vários pontos durante o processo, dependendo do valor definido para a propriedade ValidationStep na regra de validação, inclusive antes da conversão de tipo, após a conversão, depois que a propriedade é atualizada ou quando o valor alterado é confirmado (se o objeto de dados implementa IEditableObject). O valor padrão é RawProposedValue, o que acontece antes da conversão de tipo. O ponto quando os dados são convertidos no tipo de propriedade do controle de destino para o tipo de propriedade do objeto de fonte de dados geralmente implicitamente acontece sem tocar em qualquer do seu código, por exemplo, para uma entrada numérica em um TextBox. Esse processo de conversão de tipo pode lançar exceções, que devem ser usadas para indicar um erro de validação para o usuário.

Se ainda não é possível gravar o valor de propriedade do objeto de origem, claramente é uma entrada inválida. Se você optar por ligar ValidationRules, eles são invocados no ponto do processo indicada pela propriedade ValidationStep e eles podem retornar erros de validação com base em qualquer lógica é incorporada em-los ou chamada a partir deles. Se o setter de propriedade do objeto de origem lança uma exceção, que quase sempre deve ser tratada como um erro de validação, assim como acontece com o caso de conversão de tipo.

Finalmente, se você implementar IDataErrorInfo, a propriedade do indexador que você adicionar ao seu objeto de fonte de dados para essa interface será chamada para a propriedade sendo definida para verificar se há um erro de validação com base na seqüência de caracteres retornada da interface. Abordarei cada um desses mecanismos mais detalhadamente um pouco mais tarde.

Quando você deseja que a validação deve ocorrer é outra decisão, que você terá que fazer. Validação ocorre quando a ligação grava os dados à propriedade de objeto de origem subjacentes. Quando faz a validação de local é especificado pela propriedade UpdateSourceTrigger de Binding, é definida como PropertyChanged para a maioria das propriedades. Algumas propriedades, como, por exemplo, TextBox.Text, altere o valor para FocusChange, o que significa que a validação ocorre quando o foco deixa o controle que está sendo usado para editar os dados. O valor também pode ser definido como Explicit, o que significa que validação deve ser explicitamente chamado na ligação. O BindingGroup que abordarei posteriormente neste artigo usa o modo Explicit.

Em cenários de validação, particularmente com TextBoxes, normalmente você deseja fornecer um feedback imediato bastante para o usuário. Para dar suporte a isso, você deve definir a propriedade UpdateSourceTrigger na ligação para PropertyChanged:

Text="{Binding Path=Activity.Description, UpdateSourceTrigger=PropertyChanged}

Acontece que para muitos cenários reais de validação, será necessário utilizar mais de um desses mecanismos. Cada um tem suas vantagens e desvantagens, com base no tipo de erro de validação que você está preocupado com e onde a lógica de validação pode residir.

Cenário de validação de negócios

Para tornar isso mais concreto, let’s percorrer um cenário de edição com um contexto de negócios semi-real e você verá como cada um desses mecanismos pode entram em cena. Esse cenário e as regras de validação são baseadas em um aplicativo real que escrevi para um cliente em que um simples formulário necessário o uso do mecanismo de quase todas validação devido a regras de negócios suporte para validação. O aplicativo mais simples usado neste artigo, eu vai usam cada um dos mecanismos para demonstrar seu uso, mesmo que elas não são todos necessários explicitamente.

Let’s suponha que você precisa escrever um aplicativo compatível com os técnicos de campo que realizar chamadas de suporte a clientes domésticos (acha o cara de cabo, mas que também tenta venda serviços e recursos adicionais). Para cada atividade, que o técnico executa no campo, ele precisa inserir um relatório de atividades que informa o que ele fez e relaciona-se a várias partes de dados. O modelo de objeto é mostrado no do Figura 3.

Figure 3 Object Model for the Sample Application
Do modelo de objeto do aplicativo de exemplo, a Figura 3

A principal parte dos dados que os usuários preencherem é um objeto de atividades, incluindo um título, o ActivityDate, um ActivityType (uma seleção de lista suspensa de tipos de atividades predefinidas) e uma descrição. Eles também precisam se relacionam com suas atividades uma de três possibilidades. Eles precisam selecionar o cliente a atividade foi realizada para de uma lista de clientes atribuídos a eles ou um objetivo da empresa que a atividade foi relacionada a partir de uma lista de objetivos da empresa, ou eles podem digitar manualmente um motivo, se nem um cliente a um objetivo aplicam-se para esta atividade.

Estas são as regras de validação que o aplicativo precisa para impor a:

  • Título e descrição são campos necessários.
  • O ActivityDate deve ser não anterior de sete dias antes da data atual e não mais tarde do que sete dias no futuro.
  • Se ActivityType de instalação estiver marcada, o campo de inventário é necessário e deve indicar os pedaços de equipamento de caminhão do técnico, que foram feitos. Os itens de estoque devem ser inseridas como uma lista separada por vírgulas com uma estrutura de número esperado de modelo para os itens de entrada.
  • Pelo menos um cliente, objetivo ou razão deve ser fornecido.

Isso podem parecer requisitos bastante simples, mas os últimos dois em particular não são tão simples de endereço porque indicam cross-acoplamento entre propriedades. O aplicativo em execução com alguns dados inválidos, indicada pela caixa vermelha — é mostrada no do Figura 4.

Figure 4 A Dialog Showing ToolTips and Invalid Data
Figura 4 de de uma caixa de diálogo mostrando as dicas de ferramentas e dados inválidos

Exceção de validação

A forma mais simples de validação é ter uma exceção que é disparada no processo de configuração da propriedade de destino tratada como um erro de validação. A exceção que pode resultar do processo de conversão de tipo antes que a ligação já define a propriedade de destino; pode resultar em uma throw explícita de uma exceção na propriedade setter; ou pode resultar de uma chamada check-out para um objeto comercial do setter onde a exceção é lançada mais para baixo na pilha.

Para usar esse mecanismo, você basta definir a propriedade ValidatesOnExceptions como true no seu objeto de vinculação de:

Text="{Binding Path=Activity.Title, ValidatesOnExceptions=True}"

Quando uma exceção for gerada durante a tentativa de definir a propriedade do objeto de origem (Activity.Title neste caso), um erro de validação será definido no controle. A indicação de erro de validação padrão é uma borda vermelha em torno do controle, como mostrado no do Figura 5.

Figure 5 A Validation Error
De um erro de validação, a Figura 5

Como exceções podem ocorrer no processo de conversão de tipo, é uma boa idéia definir essa propriedade em ligações de entrada sempre que houver qualquer possibilidade do tipo de conversão falhar, mesmo se a propriedade fazendo simplesmente define o valor em uma variável de membro com nenhuma chance de uma exceção.

Por exemplo, suponha que você tivesse de usar um TextBox como o controle de entrada para uma propriedade DateTime. Se um usuário insere uma seqüência de caracteres que não pode ser convertida, ValidatesOnExceptions é a única maneira de que sua ligação pode indicar um erro, porque a propriedade do objeto de origem nunca será chamada.

Se você precisa fazer algo específico no modo de exibição quando não há dados inválidos, tais como desativar um comando, pode interceptar o evento anexado Validation.Error no controle. Além disso, você precisará definir a propriedade NotifyOnValidationError como true na ligação.

<TextBox Name="ageTextBox" 
  Text ="{Binding Path=Age, 
    ValidatesOnExceptions=True, 
    NotifyOnValidationError=True}" 
    Validation.Error="OnValidationError".../>

Validação de regra de validação

Em alguns cenários, talvez queira associar a validação no nível da interface do usuário e precisar de mais complicada lógica para determinar se a entrada é válida. Aplicativo de exemplo, considere a regra de validação para o campo de inventário. Se os dados são inseridos, ele precisa ser uma lista separada por vírgulas de números de modelo que seguem um padrão específico. Uma regra de validação pode acomodar facilmente isso porque ela depende totalmente o valor que está sendo definido. A regra de validação pode usar uma chamada string.Split para ativar a entrada em uma matriz de cadeia de caracteres e usar uma expressão regular para verificar se as partes individuais estão em conformidade com um determinado padrão. Para fazer isso, você pode definir uma regra de validação, conforme mostrado no do Figura 6.

Figura 6 de de regra de validação para validar uma matriz de cadeia de caracteres

public class InventoryValidationRule : ValidationRule {

  public override ValidationResult Validate(
    object value, CultureInfo cultureInfo) {

    if (InventoryPattern == null)
      return ValidationResult.ValidResult;

    if (!(value is string))
      return new ValidationResult(false, 
     "Inventory should be a comma separated list of model numbers as a string");

    string[] pieces = value.ToString().Split(‘,’);
    Regex m_RegEx = new Regex(InventoryPattern);

    foreach (string item in pieces) {
      Match match = m_RegEx.Match(item);
      if (match == null || match == Match.Empty)
        return new ValidationResult(
          false, "Invalid input format");
    }

    return ValidationResult.ValidResult;
  }

  public string InventoryPattern { get; set; }
}

É possível definir propriedades expostas em uma regra de validação do XAML no ponto de uso, permitindo que eles sejam um pouco mais flexível. Essa regra de validação ignora os valores que não podem ser convertidos em uma matriz de cadeia de caracteres. Mas quando a regra pode executar o string.Split, em seguida, usa um RegEx para validar cada seqüência de caracteres da lista separados por vírgulas é compatível com o padrão definido por meio da propriedade InventoryPattern.

Quando você retornar uma ValidationResult com o sinalizador válido definido como false, a mensagem de erro que você fornece pode ser usada na interface do usuário para apresentar o erro para o usuário, como mostraremos posteriormente. Uma desvantagem ValidationRules é que você precisa de um elemento de ligação expandido em XAML para associá-lo, conforme mostrado no código a seguir:

<TextBox Name="inventoryTextBox"...>
  <TextBox.Text>
    <Binding Path="Activity.Inventory" 
             ValidatesOnExceptions="True" 
             UpdateSourceTrigger="PropertyChanged" 
             ValidatesOnDataErrors="True">
      <Binding.ValidationRules>
        <local:InventoryValidationRule 
          InventoryPattern="^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$"/>
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

Neste exemplo, minha ligação ainda gerará erros de validação se ocorrer uma exceção devido à propriedade ValidatesOnExceptions sendo definida como true e também suporte à validação de IDataErrorInfo com base em ValidatesOnDataErrors sendo definido como true, o que falarei sobre o próximo.

Se você tiver vários ValidationRules conectados à mesma propriedade, essas regras podem ter valores diferentes para a propriedade ValidationStep ou elas podem ter o mesmo valor. As regras dentro do mesmo ValidationStep são avaliadas na ordem da declaração. Regras de ValidationSteps anteriores, obviamente, executar antes de os ValidationSteps posterior. O que talvez não seja óbvio é que se uma regra de validação retornará um erro, nenhuma das regras subseqüentes serão avaliadas. Assim, o primeiro erro de validação será a única pessoa indicada quando os erros resultam da ValidationRules.

Validação de IDataErrorInfo

A interface IDataErrorInfo requer o implementador de expor uma propriedade e um indexador:

public interface IDataErrorInfo {
  string Error { get; }
  string this[string propertyName] { get; }
}

A propriedade Error é usada para indicar um erro para o objeto como um todo e o indexador é usado para indicar erros no nível da propriedade individual. Ambos funcionam da mesma: retornar uma seqüência de caracteres nulo ou vazio não indica um erro de validação. Além disso, você retornará a seqüência de caracteres pode ser usada para exibir o erro para o usuário, como mostraremos posteriormente.

Quando você estiver trabalhando com controles individuais, ligados a propriedades individuais em um objeto de fonte de dados, a parte mais importante da interface é o indexador. A propriedade Error é usada somente em cenários como, por exemplo, quando o objeto é exibido em um DataGrid ou em um BindingGroup. A propriedade Error é usada para indicar um erro no nível de linha, ao passo que o indexador é usado para indicar um erro no nível da célula.

A implementação de IDataErrorInfo possui uma desvantagem grande: a implementação do indexador geralmente resulta em uma instrução switch caso grande, com um caso de cada nome de propriedade no objeto, e você tem a opção e a correspondência com base em seqüências de caracteres e seqüências de caracteres de retorno para indicar um erro. Além disso, a implementação de IDataErrorInfo não é chamada até que o valor da propriedade já foi definido no objeto. Se outros objetos tenham se inscrito INotifyPropertyChanged.PropertyChanged em seu objeto, eles irão já foram notificados da alteração e poderiam ter começou a trabalhar com base nos dados de sua implementação IDataErrorInfo é declarar inválido. Se o que poderia ser um problema para o seu aplicativo, será necessário lançar exceções de setters da propriedade quando você estiver insatisfeito com o valor que está sendo definido.

A boa IDataErrorInfo é que ele torna fácil a propriedades do endereço combinado de cruz. Por exemplo, bem como usar a regra de validação para validar o formato de entrada do campo de inventário, lembre-se o requisito de que o campo de estoque deve ser preenchido quando o ActivityType é a instalação. A regra de validação propriamente dito não tem acesso para as outras propriedades no objeto vinculado a dados. Ele apenas obtém passado um valor que está sendo definido para a propriedade que Binding é enganchada até. Para o endereço desse requisito, quando a propriedade ActivityType é definida, você precisa para fazer com que a validação ocorre sobre a propriedade de inventário e retornar um resultado inválido quando ActivityType é definido como Install se o valor de estoque estiver vazio.

Para fazer isso, você precisa IDataErrorInfo para que você pode inspecionar propriedades ActivityType e inventário ao avaliar o estoque, como mostrado aqui:

public string this[string propertyName] {
  get { return IsValid(propertyName); }
}

private string IsValid(string propertyName) {
  switch (propertyName) {
    ...
    case "Inventory":
      if (ActivityType != null && 
        ActivityType.Name == "Install" &&  
        string.IsNullOrWhiteSpace(Inventory))
        return "Inventory expended must be entered for installs";
      break;
}

Além disso, você precisará obter o inventário de vinculação para chamar a validação quando a propriedade ActivityType muda. Normalmente, uma Binding somente consultará a implementação de IDataErrorInfo ou chama ValidationRules se essa propriedade é alterado na interface do usuário. Nesse caso, eu quero acionar a reavaliação da validação de ligação, mesmo que a propriedade de inventário não mudou, mas tem o ActivityType relacionado.

Há duas maneiras de obter o inventário de vinculação para atualizar quando a propriedade ActivityType muda. A maneira de primeira e mais simples é publicar o evento PropertyChanged de estoque quando você define o ActivityType:

ActivityType _ActivityType;
public ActivityType ActivityType {
  get { return _ActivityType; }
  set { 
    if (value != _ActivityType) {
      _ActivityType = value;
      PropertyChanged(this, 
        new PropertyChangedEventArgs("ActivityType"));
      PropertyChanged(this, 
        new PropertyChangedEventArgs("Inventory"));
    }
  }
}

Isso faz com que a vinculação atualizar e reavaliar a validação dessa ligação.

A segunda maneira é capturar o evento anexado Binding.SourceUpdated na caixa de combinação de ActivityType ou um de seus elementos pai e acionar uma atualização de ligação do manipulador de code-behind para que o evento:

<ComboBox Name="activityTypeIdComboBox" 
  Binding.SourceUpdated="OnPropertySet"...

private void OnPropetySet(object sender, 
  DataTransferEventArgs e) {

  if (activityTypeIdComboBox == e.TargetObject) {
    inventoryTextBox.GetBindingExpression(
      TextBox.TextProperty).UpdateSource();
  }
}

Chamando UpdateSource em uma ligação através de programação provoca a gravar o valor atual no elemento acoplado destino na propriedade origem, disparando a cadeia de validação, como se o usuário tivesse acabou de editar o controle.

Usando o BindingGroup combinado de entre propriedades

O recurso BindingGroup foi adicionado no Microsoft .NET Framework 3. 5 SP1. Um BindingGroup foi especificamente projetada para permitir que você avalie a validação em um grupo de ligações ao mesmo tempo. Por exemplo, você poderia permitir que um usuário preenche um formulário inteiro e aguarde até que ela pressionado a enviar ou salvar o botão para avaliar as regras de validação para o formulário e apresentar os erros de validação ao mesmo tempo. No exemplo de aplicativo, tive a necessidade de que pelo menos um atendimento ao cliente, objetivo ou razão tinha que ser fornecido. Um BindingGroup pode ser usada para avaliar um subconjunto de um formulário também.

Para usar um BindingGroup, você precisa de um conjunto de controles com ligações normais neles que compartilham um elemento predecessor comum. No aplicativo de exemplo, a caixa de combinação de atendimento ao cliente, objetivo ComboBox e TextBox de razão de ativos dentro do mesmo Grid para o layout. BindingGroup é uma propriedade no FrameworkElement. Ele tem uma propriedade de coleção ValidationRules que podem ser preenchidos com um ou mais objetos de regra de validação. O XAML a seguir mostra a conexão de BindingGroup para o aplicativo de exemplo:

<Grid>...
<Grid.BindingGroup>
  <BindingGroup>
    <BindingGroup.ValidationRules>
      <local:CustomerObjectiveOrReasonValidationRule 
        ValidationStep="UpdatedValue" 
        ValidatesOnTargetUpdated="True"/>
    </BindingGroup.ValidationRules>
  </BindingGroup>
</Grid.BindingGroup>
</Grid>

Neste exemplo, adicionei uma instância de CustomerObjectiveOrReasonValidationRule à coleção. A propriedade ValidationStep permite que você tenha algum controle sobre o valor é passado para a regra. UpdatedValue significa que usar o valor que foi escrito para o objeto de fonte de dados depois que ele foi escrito. Você também pode escolher os valores para ValidationStep permitem que você usam a entrada bruta do que o usuário, o valor após a aplicação de conversão de tipo e valor ou o valor “ confirmado ”, o que significa que a implementação da interface IEditableObject para alterações transacionais em Propriedades de seu objeto.

O sinalizador ValidatesOnTargetUpdated faz com que a regra a ser avaliada cada vez que a propriedade target é definida por meio de ligações. Isso inclui quando ele é definido inicialmente, para que você tenha indicações de erro de validação de imediato se os dados iniciais são inválidos, assim como toda vez que o usuário altera os valores nos controles que fazem parte de o BindingGroup.

Uma regra de validação é conectada a um BindingGroup trabalha um pouco diferente de uma regra de validação conectado a uma ligação única. A Figura 7 mostra a regra de validação conectada a BindingGroup mostrado no exemplo de código anterior.

A Figura 7 de de regra de validação para um BindingGroup

public class CustomerObjectiveOrReasonValidationRule : 
  ValidationRule {

  public override ValidationResult Validate(
    object value, CultureInfo cultureInfo) {

    BindingGroup bindingGroup = value as BindingGroup;
    if (bindingGroup == null) 
      return new ValidationResult(false, 
        "CustomerObjectiveOrReasonValidationRule should only be used with a BindingGroup");

    if (bindingGroup.Items.Count == 1) {
      object item = bindingGroup.Items[0];
      ActivityEditorViewModel viewModel = 
        item as ActivityEditorViewModel;
      if (viewModel != null && viewModel.Activity != null && 
        !viewModel.Activity.CustomerObjectiveOrReasonEntered())
        return new ValidationResult(false, 
          "You must enter one of Customer, Objective, or Reason to a valid entry");
    }
    return ValidationResult.ValidResult;
  }
}

Uma regra de validação enganchada até uma ligação única, o valor é passado é o único valor da propriedade fonte de dados que é definido como o path de Binding. No caso de um BindingGroup, o valor é passado para a regra de validação é o BindingGroup propriamente dito. Ele contém uma coleção de itens é preenchida com o DataContext do elemento contido, nesse caso, a grade.

Para o aplicativo de exemplo, estou usando o padrão de MVVM, então o DataContext da exibição é ViewModel em si. A coleção de itens contém uma única referência para o ViewModel. De ViewModel, posso obter para a propriedade de atividade. Nesse caso, a classe Activity possui o método de validação que determina se pelo menos um cliente, o objetivo ou razão foi inserido para que eu Don precise duplicar a lógica da regra de validação.

Como com outras ValidationRules abordado anteriormente, se você estiver satisfeito com os valores dos dados transmitidos no, você pode retornar um ValidationResult.ValidResult. Se você estiver insatisfeito, você pode construir um novo ValidationResult com um sinalizador falso de válido e uma mensagem de seqüência de caracteres indicando o problema, que pode ser usado para fins de exibição.

Definindo o sinalizador ValidatesOnTargetUpdated não é suficiente para obter a ValidationRules para acionar automaticamente, no entanto. O BindingGroup foi desenvolvido com base no conceito de disparo explicitamente a validação para todo um grupo de controles, geralmente por algo como enviar ou salvar o pressionamento de botão em um formulário. Em alguns cenários, os usuários Don querem ser incomodados com indicações de erro de validação até que eles consideram o processo de edição completos, para que o BindingGroup destina-se com essa abordagem em mente.

O aplicativo de exemplo, quero fornecer feedback imediato de erro de validação para o usuário sempre que ele for alterado de alguma coisa no formulário. Para fazer com um BindingGroup que que capturar o evento de alteração apropriada nos controles de entrada individuais que fazem parte do grupo e que o evento o manipulador de eventos disparar a avaliação da BindingGroup. No aplicativo de exemplo, isso significa que o evento ComboBox.SelectionChanged sobre os dois ComboBoxes e o evento TextBox.TextChanged na TextBox de gancho. Aqueles que todos podem apontar para um método de manipulação de único no code-behind:

private void OnCommitBindingGroup(
  object sender, EventArgs e) {

  CrossCoupledPropsGrid.BindingGroup.CommitEdit();
}

Observe que, para a exibição de validação, o padrão de borda vermelha será exibida no FrameworkElement que o BindingGroup reside, como, por exemplo, a grade de exemplo de aplicativo, como em do Figura 4. Você também pode alterar onde a indicação de validação é exibida usando o Validation.ValidationAdornerSite e Validation.ValidationAdornerSiteFor anexado propriedades. Por padrão, os controles individuais também exibirá as bordas vermelhas seus erros de validação individuais. No aplicativo de exemplo, eu desativar as bordas, definindo o ErrorTemplate para nulo por meio de estilos.

Com BindingGroup no .NET Framework 3. 5 SP1, você poderá encontrar problemas com a exibição correta de validação de erros no formulário inicial carregar, mesmo que você defina a propriedade ValidatesOnTargetUpdated sobre a regra de validação. Para solucionar esse problema que encontrei para que isso era “ jiggle ” uma das propriedades acopladas a BindingGroup. No aplicativo de exemplo, você poderia adicionar e remover um espaço no final de qualquer texto que é inicialmente apresentado na TextBox no evento Loaded do modo de exibição da seguinte forma:

string originalText = m_ProductTextBox.Text;
m_ProductTextBox.Text += " ";
m_ProductTextBox.Text = originalText;

Isso faz com que a BindingGroup ValidationRules seja acionado uma vez que uma das propriedades de vinculação contidas tiver sido alterada, fazendo com que a validação de cada ligação deve ser chamado. Esse comportamento foi corrigido no .NET Framework 4. 0, portanto, deve haver sem a necessidade para a solução alternativa obter a exibição inicial de erros de validação — apenas definir a propriedade ValidatesOnTargetUpdated para true nas regras de validação.

Exibição de erros de validação

Como mencionado anteriormente, a forma padrão que WPF exibe erros de validação é desenhar uma borda vermelha em torno do controle. Geralmente você desejará personalizar esta opção para exibir erros de alguma outra maneira. Além disso, a mensagem de erro associada com o erro de validação não será exibida por padrão. Uma exigência comum é exibir a mensagem de erro em uma dica de ferramenta somente quando o erro de validação. Personalizar as exibições de erro de validação é razoavelmente fácil por meio de uma combinação de estilos e um conjunto de propriedades anexadas associado com a validação.

Para adicionar uma dica de ferramenta que exibe o erro de texto é trivial. Basta definir um estilo que se aplica ao controle de entrada que define a propriedade ToolTip do controle como o texto do erro de validação sempre que houver um erro de validação. Para suportar isso, há duas propriedades anexadas, que você precisará empregar: Validation.HasError e Validation.Errors. Um estilo de direcionamento do tipo TextBox que define a dica de ferramenta é mostrado aqui:

<Style TargetType="TextBox">
  <Style.Triggers>
    <Trigger Property="Validation.HasError" 
             Value="True">
      <Setter Property="ToolTip">
        <Setter.Value>
          <Binding 
            Path="(Validation.Errors).CurrentItem.ErrorContent"
            RelativeSource="{x:Static RelativeSource.Self}" />
        </Setter.Value>
      </Setter>
    </Trigger>
  </Style.Triggers>
</Style>

Você pode ver que o estilo contém apenas um disparador de propriedade para a propriedade Validation.HasError anexado. A propriedade HasError será definida como verdadeiro quando sua propriedade de objeto de origem de atualizações de uma ligação e os mecanismos de validação de geram um erro. Que poderia vir de uma exceção, chamada de regra de validação ou IDataErrorInfo. O estilo ', em seguida, usa a propriedade Validation.Errors anexado, o que irá conter uma coleção de seqüências de caracteres de erro se houver um erro de validação. Você pode usar a propriedade CurrentItem nesse tipo de coleção para capturar apenas a primeira seqüência de caracteres na coleção. Ou você poderia criar algo que os dados é vinculado à coleção e exibe a propriedade ErrorContent para cada item em um controle orientado a lista.

Para alterar a exibição de erro de validação padrão para um controle para algo diferente de uma borda vermelha, você precisará definir a propriedade de Validation.ErrorTemplate anexado como um novo modelo no controle que deseja personalizar. No exemplo de aplicativo, em vez de exibir uma borda vermelha, um pequeno círculo de gradiente de vermelho é exibido à direita de cada controle com um erro. Para isso, você pode definir um modelo de controle que será usado como o ErrorTemplate.

<ControlTemplate x:Key="InputErrorTemplate">
  <DockPanel>
    <Ellipse DockPanel.Dock="Right" Margin="2,0" 
             ToolTip="Contains invalid data"
             Width="10" Height="10">
      <Ellipse.Fill>
        <LinearGradientBrush>
          <GradientStop Color="#11FF1111" Offset="0" />
          <GradientStop Color="#FFFF0000" Offset="1" />
        </LinearGradientBrush>
      </Ellipse.Fill>
    </Ellipse>
    <AdornedElementPlaceholder />
  </DockPanel>
</ControlTemplate>

Para conectar a esse modelo de controle a um controle, basta definir a propriedade Validation.ErrorTemplate para o controle, que pode ser feito novamente por meio de um estilo:

<Style TargetType="TextBox">
  <Setter Property="Validation.ErrorTemplate" 
    Value="{StaticResource InputErrorTemplate}" />
  ...
</Style>

Quebra automática para cima

Neste artigo, mostrei como você pode utilizar os mecanismos de três validação do WPF vinculação de dados para atender a um número de cenários de validação de dados de negócios. Você viu como usar exceções, ValidationRules e a interface IDataErrorInfo para validação de única propriedade de endereço, assim como propriedades cujas regras de validação dependem dos valores atuais das outras propriedades do controle. Além disso, você viu como usar BindingGroups para avaliar várias ligações ao mesmo tempo e como personalizar a exibição de erros, além de padrões do WPF.

O aplicativo de exemplo deste artigo tem o conjunto completo de validação que satisfaz as regras de negócios descrito em um aplicativo simples que usa MVVM para ligar o modo de exibição dos dados com suporte para ele.

Brian Noyes   é arquiteto-chefe de arquitetura na IDesign (idesign.net), um diretor regional da Microsoft e MVP da Microsoft. Noyes é um autor e palestrante assíduo em Microsoft Tech · Ed, DevConnections, DevTeach e outras conferências em todo o mundo. Contate-o pelo seu blog em briannoyes.net de.

Graças ao especialista técnico seguir para revisar este artigo:   Sam Bent