Partilhar via


Visão geral da vinculação de dados no WPF

A associação de dados no Windows Presentation Foundation (WPF) fornece uma maneira simples e consistente para os aplicativos apresentarem e interagirem com dados. Os elementos podem ser vinculados a dados de uma variedade de fontes de dados na forma de objetos .NET e XML. Quaisquer ContentControl, como Button, e quaisquer ItemsControl, como ListBox e ListView, têm funcionalidade integrada para permitir o estilo flexível de itens de dados únicos ou coleções de itens de dados. As visualizações de ordenação, filtro e agrupamento podem ser geradas a partir dos dados.

A funcionalidade de vinculação de dados no WPF apresenta várias vantagens em relação aos modelos tradicionais, incluindo suporte inerente para vinculação de dados abrangendo uma ampla gama de propriedades, representação flexível de dados na interface do utilizador e separação limpa da lógica de negócios e da interface do utilizador.

Este artigo primeiro discute conceitos fundamentais para a vinculação de dados do WPF e, em seguida, aborda o uso da classe Binding e outros recursos da vinculação de dados.

O que é a vinculação de dados?

A associação de dados é o processo que estabelece uma conexão entre a interface do usuário do aplicativo e os dados que ela exibe. Se a associação tiver as configurações corretas e os dados fornecerem as notificações adequadas, quando os dados alterarem seu valor, os elementos vinculados aos dados refletirão as alterações automaticamente. A vinculação de dados também pode significar que, se uma representação externa dos dados em um elemento for alterada, os dados subjacentes poderão ser atualizados automaticamente para refletir a alteração. Por exemplo, se o usuário editar o valor em um elemento TextBox, o valor de dados subjacente será atualizado automaticamente para refletir essa alteração.

Um uso típico de vinculação de dados é colocar dados de configuração local ou de servidor em formulários ou outros controles de interface do usuário. No WPF, esse conceito é expandido para incluir a vinculação de uma ampla gama de propriedades a uma variedade de fontes de dados. No WPF, as propriedades de dependência dos elementos podem ser vinculadas a objetos .NET (incluindo objetos ADO.NET ou objetos associados a serviços Web e propriedades da Web) e dados XML.

Para obter um exemplo de vinculação de dados, dê uma olhada na seguinte interface do usuário do aplicativo de demonstração de vinculação de dados Data Binding Demo, que exibe uma lista de itens de leilão.

Captura de tela de exemplo de vinculação de dados

O aplicativo demonstra os seguintes recursos de vinculação de dados:

  • O conteúdo da ListBox está vinculado a uma coleção de objetos AuctionItem. Um objeto AuctionItem tem propriedades como Description, StartPrice, StartDate, Category, SpecialFeaturese assim por diante.

  • Os dados (objetos AuctionItem) exibidos no ListBox são modelados para que a descrição e o preço atual sejam mostrados para cada item. O modelo é criado usando um DataTemplate. Além disso, a aparência de cada item depende do valor de SpecialFeatures que o AuctionItem está a exibir. Se o valor SpecialFeatures do AuctionItem for Color, o item tem uma borda azul. Se o valor for Realce, o item tem uma borda laranja e uma estrela. A seção Data Templating fornece informações sobre modelos de dados.

  • O usuário pode agrupar, filtrar ou classificar os dados usando o CheckBoxes fornecido. Na imagem acima, Agrupar por categoria e Classificar por categoria e dataCheckBoxes estão selecionados. Você deve ter notado que os dados são agrupados com base na categoria do produto e o nome da categoria está em ordem alfabética. É difícil notar na imagem, mas os itens também são classificados pela data de início dentro de cada categoria. A classificação é feita usando uma vista de coleção . A seção Vinculação a coleções discute exibições de coleções.

  • Quando o usuário seleciona um item, o ContentControl exibe os detalhes do item selecionado. Esta experiência é conhecida como o cenário Master-detail . A seção Cenário Mestre-detale fornece informações sobre esse tipo de vinculação.

  • O tipo da propriedade StartDate é DateTime, que retorna uma data que inclui a hora para o milissegundo. Neste aplicativo, um conversor personalizado foi usado para que uma cadeia de caracteres de data mais curta seja exibida. A seção de conversão de dados fornece informações sobre conversores.

Quando o utilizador seleciona o botão Adicionar Produto, surge o seguinte formulário.

Página para Adicionar Listagem de Produtos

O usuário pode editar os campos no formulário, visualizar a lista de produtos usando os painéis de visualização curtos ou detalhados e selecionar Submit para adicionar a nova listagem de produtos. Todas as configurações existentes de agrupamento, filtragem e classificação serão aplicadas à nova entrada. Neste caso em particular, o item inserido na imagem acima será exibido como o segundo item dentro da categoria Computador.

A lógica de validação fornecida no Data de InícioTextBoxnão é mostrada nesta imagem. Se o usuário inserir uma data inválida (formatação inválida ou uma data passada), o usuário será notificado com um ToolTip e um ponto de exclamação vermelho ao lado do TextBox. A seção de Validação de Dados discute como criar lógica de validação.

Antes de entrar nos diferentes recursos de vinculação de dados descritos acima, discutiremos primeiro os conceitos fundamentais que são críticos para entender a vinculação de dados do WPF.

Conceitos básicos de vinculação de dados

Independentemente do elemento que você está vinculando e da natureza da fonte de dados, cada associação sempre segue o modelo ilustrado pela figura a seguir.

Diagrama que mostra o modelo básico de vinculação de dados.

Como mostra a figura, a vinculação de dados é essencialmente a ponte entre o destino da vinculação e a origem da vinculação. A figura demonstra os seguintes conceitos fundamentais de vinculação de dados do WPF:

  • Normalmente, cada ligação tem quatro componentes:

    • Um objeto alvo de associação.
    • Uma propriedade de destino.
    • Uma fonte vinculativa.
    • Um caminho para o valor na fonte de vinculação a ser usado.

    Por exemplo, se você quiser vincular o conteúdo de um TextBox à propriedade Employee.Name, seu objeto de destino será o TextBox, a propriedade de destino será a propriedade Text, o valor a ser usado será Namee o objeto de origem será o objeto Employee.

  • A propriedade de destino deve ser uma propriedade de dependência. A maioria das propriedades UIElement são propriedades de dependência, e a maioria das propriedades de dependência, exceto as somente leitura, oferece suporte à vinculação de dados por padrão. (Somente tipos derivados de DependencyObject podem definir propriedades de dependência; e todos os tipos UIElement derivam de DependencyObject.)

  • Embora não seja mostrado na figura, deve-se observar que o objeto de origem de vinculação não se restringe a ser um objeto .NET personalizado. A vinculação de dados WPF oferece suporte a dados na forma de objetos .NET e XML. Para fornecer alguns exemplos, sua fonte de vinculação pode ser um UIElement, qualquer objeto de lista, um objeto ADO.NET ou Web Services ou um XmlNode que contém seus dados XML. Para obter mais informações, consulte Visão geral de fontes de vinculação.

É importante lembrar que, ao estabelecer uma ligação, você está vinculando um destino de vinculação a uma fonte de vinculação. Por exemplo, se estiveres a exibir alguns dados XML subjacentes num ListBox usando a ligação de dados, estarás a ligar o teu ListBox aos dados XML.

Para estabelecer uma ligação, use o objeto Binding. O restante deste artigo discute muitos dos conceitos associados e algumas das propriedades e uso do objeto Binding.

Direção do fluxo de dados

Conforme indicado pela seta na figura anterior, o fluxo de dados de uma associação pode ir do destino da vinculação para a fonte da vinculação (por exemplo, o valor da fonte muda quando um usuário edita o valor de uma TextBox) e/ou da fonte da vinculação para o destino da vinculação (por exemplo, o conteúdo da TextBox é atualizado com alterações na fonte da vinculação) se a fonte da associação fornecer as notificações adequadas.

Talvez você queira que seu aplicativo permita que os usuários alterem os dados e os propaguem de volta para o objeto de origem. Ou talvez você não queira permitir que os usuários atualizem os dados de origem. Você pode controlar o fluxo de dados definindo o Binding.Mode.

Esta figura ilustra os diferentes tipos de fluxo de dados:

Fluxo de dados de vinculação de dados

  • OneWay vinculação faz com que as alterações na propriedade de origem atualizem automaticamente a propriedade de destino, mas as alterações na propriedade de destino não são propagadas de volta para a propriedade de origem. Este tipo de associação é apropriado se o controlo que está a ser vinculado for implicitamente só de leitura. Por exemplo, você pode vincular a uma fonte, como um ticker de ações, ou talvez sua propriedade de destino não tenha nenhuma interface de controle fornecida para fazer alterações, como uma cor de plano de fundo vinculada a dados de uma tabela. Se não houver necessidade de monitorar as alterações da propriedade de destino, usar o modo de vinculação OneWay evitará a sobrecarga do modo de vinculação TwoWay.

  • TwoWay associação faz alterações na propriedade source ou na propriedade target para atualizar automaticamente a outra. Esse tipo de associação é apropriado para formulários editáveis ou outros cenários de interface do usuário totalmente interativos. A maioria das propriedades tem como padrão OneWay vinculação, mas algumas propriedades de dependência (normalmente propriedades de controles editáveis pelo usuário, como o TextBox.Text e CheckBox.IsChecked) usam como padrão TwoWay vinculação. Uma maneira programática de determinar se uma propriedade de dependência se liga de forma unidirecional ou bidirecional por padrão é obter os metadados da propriedade usando DependencyProperty.GetMetadata e, em seguida, verificar o valor booleano da propriedade FrameworkPropertyMetadata.BindsTwoWayByDefault.

  • OneWayToSource é o inverso da vinculação de OneWay; atualiza a propriedade de origem quando a propriedade de destino é alterada. Um cenário de exemplo é se você só precisa reavaliar o valor de origem da interface do usuário.

  • A ligação OneTime não está ilustrada na figura, que faz com que a propriedade origem inicialize a propriedade alvo, mas não propaga alterações subsequentes. Se o contexto de dados for alterado ou o objeto no contexto de dados for alterado, a alteração não é refletida na propriedade de destino. Este tipo de associação é adequado quando é apropriado capturar um instantâneo do estado atual ou se os dados forem realmente estáticos. Esse tipo de associação também é útil se você quiser inicializar sua propriedade de destino com algum valor de uma propriedade de origem e o contexto de dados não for conhecido antecipadamente. Este modo é essencialmente uma forma mais simples de ligação de OneWay que fornece um melhor desempenho nos casos em que o valor de origem não é alterado.

Para detetar alterações na origem (aplicáveis a ligações de OneWay e TwoWay), a origem deve implementar um mecanismo de notificação de alterações de propriedade adequado, como INotifyPropertyChanged. Consulte Como implementar a notificação de alteração de propriedade para ver um exemplo de uma implementação INotifyPropertyChanged.

A propriedade Binding.Mode fornece mais informações sobre modos de vinculação e um exemplo de como especificar a direção de uma ligação.

O que aciona as atualizações de origem

As ligações que são TwoWay ou OneWayToSource escutam as alterações na propriedade de destino e as propagam de volta para a origem, conhecida como atualização da fonte. Por exemplo, você pode editar o texto de um TextBox para alterar o valor de origem subjacente.

No entanto, seu valor de origem é atualizado enquanto você está editando o texto ou depois de terminar de editar o texto e o controle perde o foco? A propriedade Binding.UpdateSourceTrigger determina o que aciona a atualização da fonte. Os pontos das setas à direita na figura a seguir ilustram o papel da propriedade Binding.UpdateSourceTrigger.

Diagrama que mostra a função da propriedade UpdateSourceTrigger.

Se o valor UpdateSourceTrigger for UpdateSourceTrigger.PropertyChanged, o valor indicado pela seta direita de TwoWay ou as ligações OneWayToSource será atualizado assim que a propriedade alvo for alterada. No entanto, se o valor UpdateSourceTrigger for LostFocus, esse valor só será atualizado com o novo valor quando a propriedade de destino perder o foco.

Semelhante à propriedade Mode, diferentes propriedades de dependência têm valores de UpdateSourceTrigger padrão diferentes. O valor padrão para a maioria das propriedades de dependência é PropertyChanged, enquanto a propriedade TextBox.Text tem um valor padrão de LostFocus. PropertyChanged significa que as atualizações de origem geralmente acontecem sempre que a propriedade de destino é alterada. Alterações instantâneas são ótimas para CheckBoxes e outros controles simples. No entanto, para campos de texto, a atualização após cada pressionamento de tecla pode diminuir o desempenho e nega ao usuário a oportunidade habitual de backspace e corrigir erros de digitação antes de se comprometer com o novo valor.

Consulte a página de propriedades UpdateSourceTrigger para obter informações sobre como encontrar o valor padrão de uma propriedade de dependência.

A tabela a seguir fornece um cenário de exemplo para cada valor de UpdateSourceTrigger usando o TextBox como exemplo.

Valor UpdateSourceTrigger Quando o valor de origem é atualizado Cenário de exemplo para TextBox
LostFocus (padrão para TextBox.Text) Quando o controle TextBox perde o foco. Um TextBox que está associado à lógica de validação (veja Validação de Dados abaixo).
PropertyChanged À medida que você digita no TextBox. Controlos de caixas de texto numa janela de sala de conversação.
Explicit Quando o aplicativo chama UpdateSource. Controles TextBox em um formulário editável (atualiza os valores de origem somente quando o usuário clica no botão enviar).

Para obter um exemplo, consulte Como controlar quando o texto da TextBox atualiza a origem.

Criando uma vinculação

Para reafirmar alguns dos conceitos discutidos nas seções anteriores, você estabelece uma associação usando o objeto Binding, e cada associação geralmente tem quatro componentes: um destino de ligação, uma propriedade de destino, uma fonte de vinculação e um caminho para o valor de origem a ser usado. Esta seção discute como configurar uma vinculação.

Considere o exemplo a seguir, no qual o objeto de origem de vinculação é uma classe chamada MyData, definida no namespace SDKSample. Para fins de demonstração, MyData tem uma propriedade string chamada ColorName cujo valor é definido como "Red". Assim, este exemplo gera um botão com um fundo vermelho.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <DockPanel.DataContext>
        <Binding Source="{StaticResource myDataSource}"/>
    </DockPanel.DataContext>
    <Button Background="{Binding Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Para obter mais informações sobre a sintaxe da declaração de vinculação e exemplos de como configurar uma vinculação no código, consulte Visão geral das declarações vinculativas .

Se aplicarmos este exemplo ao nosso diagrama básico, a figura resultante será semelhante à seguinte. Esta figura descreve uma ligação OneWay porque a propriedade Background, por defeito, suporta a ligação OneWay.

Diagrama que mostra a propriedade Background de vinculação de dados.

Você pode se perguntar por que essa associação funciona mesmo que a propriedade ColorName seja do tipo string enquanto a propriedade Background seja do tipo Brush. Esta vinculação utiliza a conversão padrão de tipo, que é discutida na seção conversão de dados.

Especificando a origem da vinculação

Observe que, no exemplo anterior, a fonte de vinculação é especificada definindo a propriedade DockPanel.DataContext. Em seguida, o Button herda o valor DataContext do DockPanel, que é seu elemento pai. Para reiterar, o objeto de origem de ligação é um dos quatro componentes necessários de uma ligação. Portanto, sem que o objeto de origem da vinculação fosse especificado, a vinculação não faria nada.

Há várias maneiras de especificar o objeto de origem da ligação. Usar a propriedade DataContext em um elemento pai é útil quando você está vinculando várias propriedades à mesma fonte. No entanto, por vezes, pode ser mais adequado especificar a fonte vinculativa em declarações vinculativas individuais. Para o exemplo anterior, em vez de usar a propriedade DataContext, você pode especificar a fonte de ligação definindo a propriedade Binding.Source diretamente na declaração de vinculação do botão, como no exemplo a seguir.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <Button Background="{Binding Source={StaticResource myDataSource}, Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Além de definir a propriedade DataContext em um elemento diretamente, herdar o valor DataContext de um ancestral (como o button no primeiro exemplo) e especificar explicitamente a fonte de vinculação definindo a propriedade Binding.Source na vinculação (como o botão do último exemplo), você também pode usar a propriedade Binding.ElementName ou a propriedade Binding.RelativeSource para especificar a fonte de ligação. A propriedade ElementName é útil quando você está vinculando a outros elementos em seu aplicativo, como quando você está usando um controle deslizante para ajustar a largura de um botão. A propriedade RelativeSource é útil quando a associação é especificada em um ControlTemplate ou um Style. Para obter mais informações, consulte Como especificar a fonte de associação.

Especificando o caminho para o valor

Se a origem da vinculação for um objeto, use a propriedade Binding.Path para especificar o valor a ser usado para a vinculação. Se você estiver vinculando a dados XML, use a propriedade Binding.XPath para especificar o valor. Em alguns casos, pode ser aplicável usar a propriedade Path mesmo quando seus dados são XML. Por exemplo, se você quiser acessar a propriedade Name de um XmlNode retornado (como resultado de uma consulta XPath), você deve usar a propriedade Path além da propriedade XPath.

Para obter mais informações, consulte as propriedades Path e XPath.

Embora tenhamos enfatizado que o Path ao valor a ser usado é um dos quatro componentes necessários de uma ligação, nos cenários que você deseja vincular a um objeto inteiro, o valor a ser usado seria o mesmo que o objeto de origem da ligação. Nesses casos, é aplicável não especificar uma Path. Considere o exemplo a seguir.

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

O exemplo acima usa a sintaxe de vinculação vazia: {Binding}. Nesse caso, o ListBox herda o DataContext de um elemento DockPanel pai (não mostrado neste exemplo). Quando o caminho não é especificado, o padrão é vincular ao objeto inteiro. Em outras palavras, neste exemplo, o caminho foi deixado de fora porque estamos vinculando a propriedade ItemsSource ao objeto inteiro. (Consulte a seção Vinculação a coleções para obter uma análise aprofundada.)

Além da associação a uma coleção, esse cenário também é útil quando você deseja vincular a um objeto inteiro em vez de apenas uma única propriedade de um objeto. Por exemplo, se o objeto de origem for do tipo String, poderá simplesmente querer vincular à própria string. Outro cenário comum é quando você deseja vincular um elemento a um objeto com várias propriedades.

Talvez seja necessário aplicar lógica personalizada para que os dados sejam significativos para sua propriedade de destino vinculada. A lógica personalizada pode estar na forma de um conversor personalizado se a conversão de tipo padrão não existir. Consulte conversão de dados para obter informações sobre conversores.

Binding e BindingExpression

Antes de entrar em outros recursos e usos de vinculação de dados, é útil introduzir a classe BindingExpression. Como você viu nas seções anteriores, a classe Binding é a classe de alto nível para a declaração de uma vinculação; Ele fornece muitas propriedades que permitem especificar as características de uma ligação. Uma classe relacionada, BindingExpression, é o objeto subjacente que mantém a conexão entre a origem e o destino. Uma associação contém todas as informações que podem ser compartilhadas entre várias expressões de vinculação. Um BindingExpression é uma expressão de instância que não pode ser compartilhada e contém todas as informações de instância do Binding.

Considere o exemplo a seguir, onde myDataObject é uma instância da classe MyData, myBinding é a origem Binding objeto e MyData é uma classe definida que contém uma propriedade string chamada ColorName. Este exemplo vincula o conteúdo de texto de myText, uma instância de TextBlock, a ColorName.

// Make a new source
var myDataObject = new MyData();
var myBinding = new Binding("ColorName")
{
    Source = myDataObject
};

// Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding);
' Make a New source
Dim myDataObject As New MyData
Dim myBinding As New Binding("ColorName")
myBinding.Source = myDataObject

' Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding)

Você pode usar o mesmo objeto myBinding para criar outras associações. Por exemplo, você pode usar o objeto myBinding para vincular o conteúdo de texto de uma caixa de seleção a ColorName. Nesse cenário, haverá duas instâncias de BindingExpression compartilhando o objeto myBinding.

Um objeto BindingExpression é devolvido ao chamar GetBindingExpression em um objeto vinculado a dados. Os seguintes artigos demonstram alguns dos usos da classe BindingExpression:

Conversão de dados

Na seção Criando uma ligação, o botão é vermelho porque a sua propriedade Background está vinculada a uma propriedade do tipo string com o valor "Vermelho". Esse valor de cadeia de caracteres funciona porque um conversor de tipo está presente no tipo Brush para converter o valor da cadeia de caracteres em um Brush.

Adicionar esta informação à figura na seção Criando uma Ligação fica assim.

Diagrama que mostra a propriedade Default de vinculação de dados.

No entanto, e se, em vez de ter uma propriedade do tipo string, seu objeto de origem de vinculação tiver uma propriedade Color do tipo Color? Nesse caso, para que a vinculação funcione, você precisaria primeiro transformar o valor da propriedade Color em algo que a propriedade Background aceita. Você precisaria criar um conversor personalizado implementando a interface IValueConverter, como no exemplo a seguir.

[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
<ValueConversion(GetType(Color), GetType(SolidColorBrush))>
Public Class ColorBrushConverter
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        Dim color As Color = CType(value, Color)
        Return New SolidColorBrush(color)
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Return Nothing
    End Function
End Class

Consulte IValueConverter para obter mais informações.

Agora o conversor personalizado é usado em vez da conversão padrão, e nosso diagrama tem esta aparência.

Diagrama que mostra o conversor personalizado de vinculação de dados.

Para reiterar, as conversões padrão podem estar disponíveis devido a conversores de tipo que estão presentes no tipo ao qual está vinculado. Esse comportamento dependerá de quais conversores de tipo estão disponíveis no destino. Em caso de dúvida, crie o seu próprio conversor.

A seguir estão alguns cenários típicos em que faz sentido implementar um conversor de dados:

  • Seus dados devem ser exibidos de forma diferente, dependendo da cultura. Por exemplo, você pode querer implementar um conversor de moeda ou um conversor de data/hora de calendário com base nas convenções usadas em uma cultura específica.

  • Os dados que estão sendo usados não se destinam necessariamente a alterar o valor de texto de uma propriedade, mas sim a alterar algum outro valor, como a origem de uma imagem ou a cor ou o estilo do texto de exibição. Os conversores podem ser usados neste caso convertendo a associação de uma propriedade que pode não parecer apropriada, como vincular um campo de texto à propriedade Background de uma célula da tabela.

  • Mais de um controle ou várias propriedades de controles estão vinculados aos mesmos dados. Nesse caso, a associação primária pode apenas exibir o texto, enquanto outras associações lidam com problemas específicos de exibição, mas ainda usam a mesma associação como informações de origem.

  • Uma propriedade de destino tem uma coleção de ligações, que é denominada MultiBinding. Para MultiBinding, você usa um IMultiValueConverter personalizado para produzir um valor final a partir dos valores das associações. Por exemplo, a cor pode ser calculada a partir de valores vermelhos, azuis e verdes, que podem ser valores do mesmo ou de objetos de origem de ligação diferentes. Consulte MultiBinding para obter exemplos e informações.

Vinculação a coleções

Um objeto de origem de ligação pode ser tratado como um único objeto cujas propriedades contêm dados ou como uma coleção de dados de objetos polimórficos que geralmente são agrupados (como o resultado de uma consulta a um banco de dados). Até agora, discutimos apenas a vinculação a objetos individuais. No entanto, a vinculação a uma coleta de dados é um cenário comum. Por exemplo, um cenário comum é usar um ItemsControl como um ListBox, ListViewou TreeView para exibir uma coleção de dados, como no aplicativo mostrado na seção O que é ligação de dados.

Felizmente, o nosso diagrama básico ainda se aplica. Se você estiver vinculando um ItemsControl a uma coleção, o diagrama terá esta aparência.

Diagrama que mostra o objeto ItemsControl de vinculação de dados.

Conforme mostrado neste diagrama, para vincular um ItemsControl a um objeto de coleção, a propriedade ItemsControl.ItemsSource é a propriedade a ser usada. Você pode pensar em ItemsSource como o conteúdo do ItemsControl. A associação é OneWay porque a propriedade ItemsSource suporta OneWay vinculação por padrão.

Como implementar coleções

Você pode enumerar qualquer coleção que implemente a interface IEnumerable. No entanto, para configurar associações dinâmicas para que inserções ou exclusões na coleção atualizem a interface do usuário automaticamente, a coleção deve implementar a interface INotifyCollectionChanged. Essa interface expõe um evento que deve ser gerado sempre que a coleção subjacente for alterada.

O WPF fornece a classe ObservableCollection<T>, que é uma implementação interna de uma coleção de dados que expõe a interface INotifyCollectionChanged. Para oferecer suporte total à transferência de valores de dados de objetos de origem para destinos, cada objeto em sua coleção que ofereça suporte a propriedades vinculáveis também deve implementar a interface INotifyPropertyChanged. Para obter mais informações, consulte Visão geral de fontes de vinculação.

Antes de implementar sua própria coleção, considere usar ObservableCollection<T> ou uma das classes de coleção existentes, como List<T>, Collection<T>e BindingList<T>, entre muitas outras. Se você tiver um cenário avançado e quiser implementar sua própria coleção, considere usar IList, que fornece uma coleção não genérica de objetos que podem ser acessados individualmente pelo índice e, portanto, fornece o melhor desempenho.

Vistas da coleção

Depois que o ItemsControl estiver vinculado a uma coleção de dados, convém classificar, filtrar ou agrupar os dados. Para fazer isso, utilizas vistas de coleção, que são classes que implementam a interface ICollectionView.

O que são visualizações de coleção?

Uma exibição de coleção é uma camada sobre uma coleção de código-fonte de vinculação que permite navegar e exibir a coleção de código-fonte com base em consultas de classificação, filtro e grupo, sem precisar alterar a própria coleção de código-fonte subjacente. Uma exibição de coleção também mantém um ponteiro para o item atual na coleção. Se a coleção de origem implementar a interface INotifyCollectionChanged, as alterações geradas pelo evento CollectionChanged serão propagadas para as exibições.

Como os modos de exibição não alteram as coleções de origem subjacentes, cada coleção de fontes pode ter vários modos de exibição associados a ela. Por exemplo, pode-se ter uma coleção de objetos Task. Com o uso de modos de exibição, você pode exibir esses mesmos dados de maneiras diferentes. Por exemplo, no lado esquerdo da sua página, você pode querer mostrar tarefas ordenadas por prioridade e, no lado direito, agrupadas por área.

Como criar uma vista

Uma maneira de criar e usar um modo de exibição é instanciar o objeto de exibição diretamente e, em seguida, usá-lo como a fonte de ligação. Por exemplo, considere o aplicativo de demonstração de vinculação de dados aplicativo de demonstração de vinculação de dados mostrado na seção O que é de vinculação de dados. A aplicação é implementada de forma que o ListBox se vincula a uma visualização sobre a coleta de dados em vez da própria coleta de dados diretamente. O exemplo a seguir é extraído do aplicativo de demonstração de vinculação de dados aplicativo de demonstração de vinculação de dados. A classe CollectionViewSource é o proxy XAML de uma classe que herda de CollectionView. Neste exemplo específico, a da exibição está vinculada à coleção AuctionItems (do tipo ) do objeto do aplicativo atual.

<Window.Resources>
    <CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />
</Window.Resources>

O recurso listingDataView serve como a fonte de associação para elementos no aplicativo, como o ListBox.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />

Para criar outra vista para a mesma coleção, pode criar outra instância de CollectionViewSource e atribuir-lhe um nome diferente de x:Key.

A tabela a seguir mostra quais tipos de dados de visualização são criados como a visualização padrão da coleção ou por CollectionViewSource com base no tipo de coleção de origem.

Tipo de coleção de origem Tipo de visualização de coleção Observações
IEnumerable Um tipo interno baseado em CollectionView Não é possível agrupar itens.
IList ListCollectionView Mais rápido.
IBindingList BindingListCollectionView

Usando um modo de exibição padrão

Especificar uma vista de coleção como uma fonte de associação é uma maneira de criar e usar uma vista de coleção. O WPF também cria uma exibição de coleção padrão para cada coleção usada como uma fonte de vinculação. Se você vincular diretamente a uma coleção, o WPF se vinculará à sua exibição padrão. Essa exibição padrão é compartilhada por todas as associações à mesma coleção, portanto, uma alteração feita em uma exibição padrão por um controle ou código acoplado (como classificação ou uma alteração no ponteiro do item atual, discutida posteriormente) é refletida em todas as outras associações para a mesma coleção.

Para obter o modo de exibição padrão, use o método GetDefaultView. Para obter um exemplo, consulte Obter o modo de exibição padrão de umde coleta de dados .

Visualizações de coleção com ADO.NET DataTables

Para melhorar o desempenho, as exibições de coleta para objetos ADO.NET DataTable ou DataView delegam a classificação e a filtragem ao DataView, o que faz com que a classificação e a filtragem sejam compartilhadas em todas as exibições de coleta da fonte de dados. Para permitir que cada exibição de coleção classifique e filtre independentemente, inicialize cada exibição de coleção com seu próprio objeto DataView.

Classificação

Como mencionado anteriormente, as exibições podem aplicar uma ordem de classificação a uma coleção. Como existe na recolha subjacente, os seus dados podem ou não ter uma ordem relevante e inerente. A visualização sobre a coleção permite impor uma ordem ou alterar a ordem padrão com base nos critérios de comparação fornecidos. Como é uma exibição baseada em cliente dos dados, um cenário comum é que o usuário pode querer classificar colunas de dados tabulares pelo valor ao qual a coluna corresponde. Usando modos de exibição, essa classificação orientada pelo usuário pode ser aplicada, novamente sem fazer alterações na coleção subjacente ou até mesmo ter que consultar novamente o conteúdo da coleção. Para obter um exemplo, consulte Classificar uma coluna do GridView ao clicar num cabeçalho.

O exemplo seguinte mostra a lógica de classificação do CheckBox "Classificar por categoria e data" da interface do utilizador da aplicação na secção O que é a vinculação de dados.

private void AddSortCheckBox_Checked(object sender, RoutedEventArgs e)
{
    // Sort the items first by Category and then by StartDate
    listingDataView.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(new SortDescription("StartDate", ListSortDirection.Ascending));
}
Private Sub AddSortCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    ' Sort the items first by Category And then by StartDate
    listingDataView.SortDescriptions.Add(New SortDescription("Category", ListSortDirection.Ascending))
    listingDataView.SortDescriptions.Add(New SortDescription("StartDate", ListSortDirection.Ascending))
End Sub

Filtragem

As vistas também podem aplicar um filtro a uma coleção, para que a vista mostre apenas um determinado subconjunto da coleção completa. Você pode filtrar uma condição nos dados. Por exemplo, como é feito pelo aplicativo na seção O que é vinculação de dados, a CheckBox "Mostrar apenas pechinchas" contém lógica para filtrar itens que custam US $ 25 ou mais. O código a seguir é executado para definir ShowOnlyBargainsFilter como o manipulador de eventos Filter quando esse CheckBox é selecionado.

private void AddFilteringCheckBox_Checked(object sender, RoutedEventArgs e)
{
    if (((CheckBox)sender).IsChecked == true)
        listingDataView.Filter += ListingDataView_Filter;
    else
        listingDataView.Filter -= ListingDataView_Filter;
}
Private Sub AddFilteringCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    Dim checkBox = DirectCast(sender, CheckBox)

    If checkBox.IsChecked = True Then
        AddHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    Else
        RemoveHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    End If
End Sub

O manipulador de eventos ShowOnlyBargainsFilter tem a seguinte implementação.

private void ListingDataView_Filter(object sender, FilterEventArgs e)
{
    // Start with everything excluded
    e.Accepted = false;

    // Only inlcude items with a price less than 25
    if (e.Item is AuctionItem product && product.CurrentPrice < 25)
        e.Accepted = true;
}
Private Sub ListingDataView_Filter(sender As Object, e As FilterEventArgs)

    ' Start with everything excluded
    e.Accepted = False

    Dim product As AuctionItem = TryCast(e.Item, AuctionItem)

    If product IsNot Nothing Then

        ' Only include products with prices lower than 25
        If product.CurrentPrice < 25 Then e.Accepted = True

    End If

End Sub

Se você estiver usando uma das classes CollectionView diretamente em vez de CollectionViewSource, você usaria a propriedade Filter para especificar um retorno de chamada. Para obter um exemplo, consulte Filtrar Dados numa Vista.

Agrupamento

Exceto pela classe interna que visualiza uma coleção de IEnumerable, todas as visualizações de coleção oferecem suporte ao agrupamento , permitindo que o utilizador divida a coleção em grupos lógicos na visualização de coleção. Os grupos podem ser explícitos, onde o usuário fornece uma lista de grupos, ou implícitos, onde os grupos são gerados dinamicamente dependendo dos dados.

O exemplo a seguir mostra a lógica do CheckBox"Grupo por categoria".

// This groups the items in the view by the property "Category"
var groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
' This groups the items in the view by the property "Category"
Dim groupDescription = New PropertyGroupDescription()
groupDescription.PropertyName = "Category"
listingDataView.GroupDescriptions.Add(groupDescription)

Para outro exemplo de agrupamento, consulte Itens em grupo num ListView que implementa um GridView.

Ponteiros atuais de item

As visualizações também apoiam a noção de um item atual. Você pode navegar pelos objetos em uma exibição de coleção. Ao navegar, você está movendo um ponteiro de item que permite recuperar o objeto que existe nesse local específico da coleção. Para obter um exemplo, consulte Navegar pelos objetos em umCollectionView de dados.

Como o WPF se liga a uma coleção apenas usando uma vista (uma vista especificada ou a vista padrão da coleção), todas as associações a coleções têm um apontador para o item atual. Ao vincular a um modo de exibição, o caractere de barra ("/") em um valor Path designa o item atual do modo de exibição. No exemplo a seguir, o contexto de dados é uma vista de coleção. A primeira linha liga-se à coleção. A segunda linha liga-se ao item atual da coleção. A terceira linha se liga à propriedade Description do item atual na coleção.

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />

A sintaxe de barra (/) e propriedade também pode ser utilizada em conjunto para atravessar uma hierarquia de coleções. O exemplo a seguir se liga ao item atual de uma coleção chamada Offices, que é uma propriedade do item atual da coleção de origem.

<Button Content="{Binding /Offices/}" />

O ponteiro do item atual pode ser afetado por qualquer classificação ou filtragem aplicada à coleção. A classificação preserva o ponteiro do item atual no último item selecionado, mas a visualização da coleção agora é reestruturada com base nele. (Talvez o item selecionado estivesse no início da lista antes, mas agora o item selecionado pode estar em algum lugar no meio.) A filtragem preserva o item selecionado se essa seleção permanecer em exibição após a filtragem. Caso contrário, o ponteiro do item atual será definido como o primeiro item da exibição de coleção filtrada.

Cenário de vinculação mestre-detallhe

A noção de um item atual é útil não apenas para a navegação de itens em uma coleção, mas também para o cenário de vinculação mestre-detalhe. Considere novamente a interface do usuário do aplicativo na seção O que é vinculação de dados. Nesse aplicativo, a seleção dentro do ListBox determina o conteúdo apresentado no ContentControl. Dito de outra forma, quando um item de ListBox é selecionado, o ContentControl mostra os detalhes do item selecionado.

Você pode implementar o cenário mestre-detalhe simplesmente tendo dois ou mais controles vinculados à mesma exibição. O exemplo a seguir do aplicativo de demonstração de vinculação de dados mostra a marcação do ListBox e do ContentControl que se vê na UI do aplicativo na secção O que é a vinculação de dados.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
                Content="{Binding Source={StaticResource listingDataView}}"
                ContentTemplate="{StaticResource detailsProductListingTemplate}" 
                Margin="9,0,0,0"/>

Observe que ambos os controles estão vinculados à mesma fonte, o listingDataView recurso estático (consulte a definição desse recurso na seção Como criar um modo de exibição). Essa associação funciona porque quando um objeto (o ContentControl neste caso) é vinculado a uma exibição de coleção, ele se liga automaticamente ao CurrentItem da exibição. Os objetos CollectionViewSource sincronizam automaticamente a moeda e a seleção. Se seu controle de lista não estiver vinculado a um objeto CollectionViewSource como neste exemplo, você precisará definir sua propriedade IsSynchronizedWithCurrentItem como true para que isso funcione.

Para outros exemplos, consulte Vincular a uma coleção e exibir informações com base na seleção e Usar o padrão mestre-detalhe com dados hierárquicos.

Você deve ter notado que o exemplo acima usa um modelo. Na verdade, os dados não seriam exibidos da maneira que desejamos sem o uso de modelos (o explicitamente usado pelo ContentControl e o implicitamente usado pelo ListBox). Passemos agora à criação de modelos de dados na próxima seção.

Modelagem de dados

Sem o uso de modelos de dados, nossa interface do usuário do aplicativo na seção O que é vinculação de dados teria a seguinte aparência.

demonstração de vinculação de dados sem modelos de dados

Como mostrado no exemplo na seção anterior, tanto o controle ListBox quanto o ContentControl estão vinculados a todo o objeto de coleção (ou, mais especificamente, à exibição sobre o objeto de coleção) de AuctionItems. Sem instruções específicas de como exibir a coleta de dados, o ListBox exibe a representação de cadeia de caracteres de cada objeto na coleção subjacente e o ContentControl exibe a representação de cadeia de caracteres do objeto ao qual está vinculado.

Para resolver esse problema, o aplicativo define DataTemplates. Como mostrado no exemplo na seção anterior, o ContentControl usa explicitamente o detailsProductListingTemplate modelo de dados. O controlo ListBox usa implicitamente o seguinte modelo de dados ao exibir os objetos AuctionItem na coleção.

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/>
                <ColumnDefinition Width="86"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2"
                       Text="{Binding Path=Description}"
                       Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType"
                           Text="{Binding Path=CurrentPrice}" 
                           Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
            <DataTrigger.Setters>
                <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
                <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
                <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
                <Setter Property="BorderThickness" Value="3" TargetName="border" />
                <Setter Property="Padding" Value="5" TargetName="border" />
            </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Com o uso destes dois DataTemplates, a interface do utilizador resultante é a mostrada na seção O que é ligação de dados. Como você pode ver nessa captura de tela, além de permitir que você coloque dados em seus controles, DataTemplates permite que você defina visuais atraentes para seus dados. Por exemplo, DataTriggers são usados na DataTemplate acima para que AuctionItems com um valor de SpecialFeatures de HighLight sejam exibidos com uma borda laranja e uma estrela.

Para obter mais informações sobre modelos de dados, consulte a Visão geral da modelagem de dados.

Validação de dados

A maioria dos aplicativos que recebem a entrada do usuário precisa ter lógica de validação para garantir que o usuário tenha inserido as informações esperadas. As verificações de validação podem ser baseadas em tipo, intervalo, formato ou outros requisitos específicos do aplicativo. Esta seção discute como a validação de dados funciona no WPF.

Associar regras de validação a uma vinculação

O modelo de vinculação de dados do WPF permite que você associe ValidationRules ao seu objeto Binding. Por exemplo, o exemplo a seguir vincula um TextBox a uma propriedade chamada StartPrice e adiciona um objeto ExceptionValidationRule à propriedade Binding.ValidationRules.

<TextBox Name="StartPriceEntryForm" Grid.Row="2"
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Um objeto ValidationRule verifica se o valor de uma propriedade é válido. WPF tem dois tipos de built-in ValidationRule objetos:

Você também pode criar sua própria regra de validação derivando da classe ValidationRule e implementando o método Validate. O exemplo a seguir mostra a regra utilizada na Adicionar listagem de produtos "Data de início" TextBox da seção O que é a vinculação de dados.

public class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        // Test if date is valid
        if (DateTime.TryParse(value.ToString(), out DateTime date))
        {
            // Date is not in the future, fail
            if (DateTime.Now > date)
                return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            // Date is not a valid date, fail
            return new ValidationResult(false, "Value is not a valid date.");
        }

        // Date is valid and in the future, pass
        return ValidationResult.ValidResult;
    }
}
Public Class FutureDateRule
    Inherits ValidationRule

    Public Overrides Function Validate(value As Object, cultureInfo As CultureInfo) As ValidationResult

        Dim inputDate As Date

        ' Test if date is valid
        If Date.TryParse(value.ToString, inputDate) Then

            ' Date is not in the future, fail
            If Date.Now > inputDate Then
                Return New ValidationResult(False, "Please enter a date in the future.")
            End If

        Else
            ' // Date Is Not a valid date, fail
            Return New ValidationResult(False, "Value is not a valid date.")
        End If

        ' Date is valid and in the future, pass
        Return ValidationResult.ValidResult

    End Function

End Class

O TextBox StartDateEntryForm usa esse FutureDateRule, conforme mostrado no exemplo a seguir.

<TextBox Name="StartDateEntryForm" Grid.Row="3"
         Validation.ErrorTemplate="{StaticResource validationTemplate}" 
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
                 Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Como o valor UpdateSourceTrigger é PropertyChanged, o mecanismo de vinculação atualiza o valor de origem em cada pressionamento de tecla, o que significa que ele também verifica todas as regras na coleção ValidationRules em cada pressionamento de teclas. Discutimos isso mais detalhadamente na seção Processo de validação.

Fornecer feedback visual

Se o usuário inserir um valor inválido, convém fornecer alguns comentários sobre o erro na interface do usuário do aplicativo. Uma maneira de fornecer esse feedback é definir a propriedade Validation.ErrorTemplate anexada como um ControlTemplatepersonalizado. Como mostrado na subseção anterior, o StartDateEntryFormTextBox usa um ErrorTemplate chamado validationTemplate. O exemplo a seguir mostra a definição de validationTemplate.

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

O elemento AdornedElementPlaceholder especifica onde o controle que está sendo adornado deve ser colocado.

Além disso, você também pode usar um ToolTip para exibir a mensagem de erro. Tanto o StartDateEntryForm quanto o StartPriceEntryFormTextBoxes usam o estilo textStyleTextBox, que cria um ToolTip que exibe a mensagem de erro. O exemplo a seguir mostra a definição de textStyleTextBox. A propriedade anexada Validation.HasError é true quando uma ou mais das ligações nas propriedades do elemento acoplado estão em erro.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
    <Setter Property="Foreground" Value="#333333" />
    <Setter Property="MaxLength" Value="40" />
    <Setter Property="Width" Value="392" />
    <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>

Com o ErrorTemplate personalizado e o ToolTip, o TextBox StartDateEntryForm se parece com o seguinte quando há um erro de validação.

Erro de validação de vinculação de dados

Se o Binding tiver regras de validação associadas, mas o utilizador não especificar um ErrorTemplate no controlo vinculado, uma ErrorTemplate padrão será usada para notificar os utilizadores quando houver um erro de validação. O ErrorTemplate padrão é um modelo de controle que define uma borda vermelha na camada adorner. Com o ErrorTemplate padrão e o ToolTip, a interface do usuário do StartPriceEntryFormTextBox se parece com o seguinte quando há um erro de validação.

Erro padrão de validação de vinculação de dados

Para obter um exemplo de como fornecer lógica para validar todos os controles em uma caixa de diálogo, consulte a seção Caixas de diálogo personalizadas na visão geral das caixas de diálogo .

Processo de validação

A validação geralmente ocorre quando o valor de um destino é transferido para a propriedade binding source. Esta transferência ocorre nos bindings TwoWay e OneWayToSource. Para reiterar, o que causa uma atualização de origem depende do valor da propriedade UpdateSourceTrigger, conforme descrito na seção O que aciona as atualizações de origem.

Os itens a seguir descrevem o processo de validação . Se ocorrer um erro de validação ou outro tipo de erro a qualquer momento durante este processo, o processo é interrompido:

  1. O mecanismo de vinculação verifica se há objetos personalizados de ValidationRule definidos cuja ValidationStep esteja definida como RawProposedValue para esse Binding, caso em que chama o método Validate em cada ValidationRule até que um deles encontre um erro ou todos passem.

  2. Em seguida, o mecanismo de vinculação chama o conversor, se existir.

  3. Se o conversor tiver sucesso, o motor de vinculação verifica se existem objetos de ValidationRule personalizados definidos cujo ValidationStep esteja definido como ConvertedProposedValue para esse Binding. Nesse caso, o método Validate é chamado em cada ValidationRule que tenha ValidationStep definido para ConvertedProposedValue, até que um deles encontre um erro ou até que todos sejam aprovados.

  4. O mecanismo de vinculação define a propriedade source.

  5. O mecanismo de vinculação verifica se há algum objeto de ValidationRule personalizado definido cujo ValidationStep esteja definido como UpdatedValue para esse Binding, caso em que ele chama o método Validate em cada ValidationRule que tem ValidationStep definido para UpdatedValue até que um deles encontre um erro ou até que todos sejam bem-sucedidos. Se um DataErrorValidationRule estiver associado a uma associação e sua ValidationStep estiver definida como padrão, UpdatedValue, o DataErrorValidationRule será verificado neste momento. Neste ponto, qualquer associação que tenha o ValidatesOnDataErrors definido como true é verificada.

  6. O motor de ligação verifica se existem objetos ValidationRule personalizados definidos cujo ValidationStep esteja como CommittedValue para aquele Binding, caso em que chama o método Validate em cada ValidationRule que tem ValidationStep definido para CommittedValue, até que um encontre um erro ou até que todos passem.

Se um ValidationRule não passar em nenhum momento durante esse processo, o mecanismo de vinculação criará um objeto ValidationError e o adicionará à coleção Validation.Errors do elemento vinculado. Antes de o mecanismo de vinculação executar os objetos ValidationRule em uma etapa específica, remove qualquer ValidationError que tenha sido adicionado à propriedade Validation.Errors anexada do elemento vinculado durante essa etapa. Por exemplo, se um ValidationRule cujo ValidationStep está definido como UpdatedValue falhou, na próxima vez que o processo de validação ocorrer, o mecanismo de vinculação removerá esse ValidationError imediatamente antes de chamar qualquer ValidationRule que tenha ValidationStep definido como UpdatedValue.

Quando Validation.Errors não está vazio, a propriedade Validation.HasError anexada do elemento é definida como true. Além disso, se a propriedade NotifyOnValidationError do Binding estiver definida como true, o mecanismo de vinculação acionará o evento Validation.Error anexado no elemento.

Note também que uma transferência de valor válida em qualquer direção (destino para origem ou origem para destino) limpa a propriedade Validation.Errors associada.

Se a vinculação tiver um ExceptionValidationRule associado a ela, ou se a propriedade ValidatesOnExceptions estiver definida como true e uma exceção for lançada quando o motor de vinculação definir a origem, o motor de vinculação verificará se há um UpdateSourceExceptionFilter. Você tem a opção de usar o retorno de chamada UpdateSourceExceptionFilter para fornecer um manipulador personalizado para lidar com exceções. Se um UpdateSourceExceptionFilter não for especificado no Binding, o mecanismo de ligação criará um ValidationError com a exceção e irá adicioná-lo à coleção Validation.Errors do elemento vinculado.

Mecanismo de depuração

Você pode definir a propriedade anexada PresentationTraceSources.TraceLevel em um objeto relacionado à associação para receber informações sobre o status de uma associação específica.

Ver também