Partilhar via


Visão geral da vinculação de dados (WPF .NET)

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 diferentes tipos de fontes de dados na forma de objetos .NET e XML. Qualquer ContentControl, como Button, e qualquer ItemsControl, como ListBox e ListView, têm funcionalidade integrada para permitir um estilo flexível de itens de dados únicos ou coleções de itens de dados. Visualizações de ordenação, filtro e agregação podem ser geradas com base nos dados.

A ligação de dados no WPF tem várias vantagens em relação aos modelos tradicionais, incluindo o suporte inerente das propriedades para ligação de dados, a representação flexível dos dados na interface do utilizador e a separação clara da lógica empresarial 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 diferentes tipos 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.

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 de destino de vinculaçã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ê vinculasse o conteúdo de um TextBox à propriedade Employee.Name, configuraria sua associação como a tabela a seguir:

    Cenário Valor
    Público-alvo TextBox
    Propriedade de destino Text
    Objeto de origem Employee
    Caminho do valor do objeto de origem Name
  • 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. Todos os tipos UIElement derivam de DependencyObject.

  • As fontes de vinculação não estão restritas a objetos .NET personalizados.

    Embora não seja mostrado na figura, deve-se notar que o objeto de origem de ligação não está restrito a ser um objeto .NET personalizado. A vinculação de dados WPF oferece suporte a dados na forma de objetos .NET, XML e até mesmo objetos de elemento XAML. 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 apresentar alguns dados XML subjacentes num ListBox usando a ligação de dados, estarás a vincular 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.

Contexto dos dados

Quando a associação de dados é declarada em elementos XAML, eles resolvem a vinculação de dados examinando sua propriedade DataContext imediata. O contexto de dados é normalmente o de objeto de origem de vinculação para o caminho avaliação do do valor de origem de vinculação. Você pode substituir este comportamento nas ligações e definir um valor específico de objeto de origem de ligações . Se a propriedade DataContext para o objeto que hospeda a associação não estiver definida, a propriedade DataContext do elemento pai será verificada, e assim por diante, até a raiz da árvore de objetos XAML. Em resumo, o contexto de dados usado para resolver a vinculação é herdado do pai, a menos que explicitamente definido no objeto.

As ligações podem ser configuradas para resolver com um objeto específico, em vez de usar o contexto de dados para resolução de vinculação. A especificação direta de um objeto de origem é usada quando, por exemplo, você vincula a cor de primeiro plano de um objeto à cor de plano de fundo de outro objeto. O contexto de dados não é necessário, pois a associação é resolvida entre esses dois objetos. Inversamente, as associações que não estão vinculadas a objetos de origem específicos usam resolução de contexto de dados.

Quando a propriedade DataContext é alterada, todas as associações que podem ser afetadas pelo contexto de dados são reavaliadas.

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 ligação de dados

  • OneWay vinculação faz com que as alterações na propriedade de origem atualizem automaticamente a propriedade de destino. No entanto, as alterações na propriedade de destino não são propagadas de volta para a propriedade de origem. Esse tipo de associação é apropriado se o controle que está sendo vinculado for implicitamente somente 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.

  • A associação TwoWay provoca alterações na propriedade "source" ou na propriedade "target", atualizando 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 associação, mas algumas propriedades de dependência (normalmente propriedades de controles editáveis pelo usuário, como TextBox.Text e CheckBox.IsChecked padrão para TwoWay vinculação.

    Uma maneira programática de determinar se uma propriedade de dependência está, por padrão, ligada de forma unidirecional ou bidirecional é obter os metadados da propriedade com DependencyProperty.GetMetadata. O tipo de retorno desse método é PropertyMetadata, que não contém metadados sobre vinculação. No entanto, se esse tipo puder ser convertido para o FrameworkPropertyMetadataderivado, o valor booleano da propriedade FrameworkPropertyMetadata.BindsTwoWayByDefault poderá ser verificado. O exemplo de código a seguir demonstra como obter os metadados para a propriedade TextBox.Text:

    public static void PrintMetadata()
    {
        // Get the metadata for the property
        PropertyMetadata metadata = TextBox.TextProperty.GetMetadata(typeof(TextBox));
    
        // Check if metadata type is FrameworkPropertyMetadata
        if (metadata is FrameworkPropertyMetadata frameworkMetadata)
        {
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:");
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}");
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}");
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}");
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}");
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}");
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}");
        }
    
        /*  Displays:
         *  
         *  TextBox.Text property metadata:
         *    BindsTwoWayByDefault: True
         *    IsDataBindingAllowed: True
         *          AffectsArrange: False
         *          AffectsMeasure: False
         *           AffectsRender: False
         *                Inherits: False
        */
    }
    
    Public Shared Sub PrintMetadata()
    
        Dim metadata As PropertyMetadata = TextBox.TextProperty.GetMetadata(GetType(TextBox))
        Dim frameworkMetadata As FrameworkPropertyMetadata = TryCast(metadata, FrameworkPropertyMetadata)
    
        If frameworkMetadata IsNot Nothing Then
    
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:")
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}")
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}")
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}")
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}")
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}")
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}")
    
            '  Displays:
            '
            '  TextBox.Text property metadata:
            '    BindsTwoWayByDefault: True
            '    IsDataBindingAllowed: True
            '          AffectsArrange: False
            '          AffectsMeasure: False
            '           AffectsRender: False
            '                Inherits: False
        End If
    
    
    End Sub
    
  • OneWayToSource é o inverso da ligaçã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.

  • Não está ilustrado na figura a ligação OneTime, que faz com que a propriedade de origem inicie a propriedade de destino, mas as alterações subsequentes não são propagadas. 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. Esse tipo de associação é apropriado se uma captura do estado atual for adequada 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 com antecedência. 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 às ligações de OneWay e TwoWay), a fonte deve implementar um mecanismo adequado de notificação de alteração de propriedade, como o INotifyPropertyChanged. Consulte Como: Implementar notificação de alteração de propriedade (.NET Framework) para obter um exemplo de uma implementação de 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 nas setas para a direita na figura seguinte ilustram o papel da propriedade Binding.UpdateSourceTrigger.

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

Se o valor de UpdateSourceTrigger for UpdateSourceTrigger.PropertyChanged, então o valor apontado pela ligação direita de TwoWay ou pelas ligações de 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, o que faz com que o valor da propriedade de origem seja alterado instantaneamente quando o valor da propriedade de destino é alterado. Alterações instantâneas são ótimas para CheckBox 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. Por exemplo, a propriedade TextBox.Text assume como padrão o valor UpdateSourceTrigger de LostFocus, o que faz com que o valor de origem seja alterado somente quando o elemento de controle perde o foco, não quando a propriedade TextBox.Text é alterada. 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 (consulte Validação de Dados abaixo).
PropertyChanged À medida que você digita no TextBox. Controles TextBox em uma janela de sala de chat.
Explicit Quando o aplicativo chama UpdateSource. Controles TextBox em um formulário editável (atualiza os valores de origem somente quando o usuário pressiona o botão enviar).

Para obter um exemplo, consulte Como: Controlar quando o texto TextBox atualiza a origem (.NET Framework).

Exemplo de vinculação de dados

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, Categorye SpecialFeatures.

  • 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 das SpecialFeatures do AuctionItem que está a ser exibido. 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 sobre Vinculação a coleções discute vistas de coleção.

  • Quando o usuário seleciona um item, o ContentControl exibe os detalhes do item selecionado. Essa experiência é chamada de cenário Master-detail. A seção Cenário mestre-detalhe fornece informações sobre esse tipo de associaçã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 de Adição de 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.

Nesta imagem, não é mostrada a lógica de validação fornecida no Data de InícioTextBox. 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.

Criar uma ligaçã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 associação.

As fontes de vinculação estão vinculadas ao DataContext ativo do elemento. Os elementos herdam automaticamente seus DataContext se não tiverem definido explicitamente um.

Considere o exemplo a seguir, no qual o objeto de origem de ligaçã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 suporta, por padrão, uma 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. Essa associação usa a conversão de tipo padrão, 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. Assim, 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 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 Visão geral de fontes de vinculaçã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 um 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 discussão 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, você pode simplesmente querer vincular à própria cadeia de caracteres. 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 da 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 num objeto ligado a dados. Os seguintes artigos demonstram alguns dos usos da classe BindingExpression:

Conversão de dados

Na seção Criar uma vinculação, o botão é vermelho porque sua propriedade Background está vinculada a uma propriedade 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 estas informações à figura na seção Criar uma ligação tem esta aparência.

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 vinculaçõ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, tal como no aplicativo mostrado na secção "O que é vinculaçã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 que deve 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 sobre qualquer coleção que implementa 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 acima de uma coleção de origem de vinculação que permite navegar e exibir a coleção de origem com base em consultas de ordenação, filtro e agrupamento, sem necessidade de alterar a própria coleção de origem 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, você pode 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 coleção de dados em vez da coleção 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, podes 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 exibição são criados como o modo de exibição de coleção padrão ou por CollectionViewSource com base no tipo de coleção de origem.

Tipo de coleção de origem Tipo de vista 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 um modo de exibição de coleção como uma fonte de vinculação é uma maneira de criar e usar um modo de exibição 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, veja A visualização padrão de uma coleção de dados (.NET Framework).

Visõ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 Ordenar uma coluna do GridView ao clicar no cabeçalho (.NET Framework).

O exemplo a seguir mostra a lógica de classificação do CheckBox "Classificar por categoria e data" da interface do usuário do aplicativo na seçã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 visualizações também podem aplicar um filtro a uma coleção, para que a visualização mostre apenas um determinado subconjunto da coleção completa. Você pode filtrar uma condição nos dados. Por exemplo, como é feito pela aplicação na seção O que é ligação de dados, a opção CheckBox "Mostrar apenas pechinchas" contém uma lógica para filtrar itens que custam 25 dólares 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 (.NET Framework).

Agrupamento

Com exceção da classe interna que vê uma coleção de IEnumerable, todas as vistas de coleção suportam o agrupamento de , permitindo ao utilizador dividir a coleção na vista de coleção em grupos lógicos. 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 obter outro exemplo de agrupamento, consulte Agrupar itens num ListView que implementa um GridView (.NET Framework).

Ponteiros de itens atuais

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 naquele local específico da coleção. Para obter um exemplo, consulte Navegar pelos objetos em um CollectionView de dados (.NET Framework).

Como o WPF se liga a uma coleção apenas utilizando uma vista (uma vista especificada por si, ou a vista padrão da coleção), todas as associações a coleções têm um ponteiro de 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 vincula-se à 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 podem ser combinadas 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 atual do item no último item selecionado, mas a vista da coleção agora é reestruturada em torno dele. (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 ligação mestre-detalhe

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. Reveja a interface do aplicativo na seção O que é vinculação de dados novamente. Nesse aplicativo, a seleção dentro do ListBox determina o conteúdo mostrado 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 interface do utilizador da aplicação na secção O que é 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 (.NET Framework) e Usar o padrão mestre-detalhe com dados hierárquicos (.NET Framework).

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.

Modelação de dados

Sem o uso de modelos de dados, nossa interface do usuário do aplicativo na seção Exemplo de de 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 a utilização destes dois DataTemplates, a UI resultante é aquela 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 valor de SpecialFeatures de HighLight sejam exibidos com uma borda laranja e uma estrela.

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

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 usada pelo Adicionar Listagem de Produtos "Data de Início" TextBox da seção O que é Ligaçã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 (Validation.Errors).CurrentItem.ErrorContent, RelativeSource={RelativeSource Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

Com o ErrorTemplate personalizado e o ToolTip, o formulário StartDateEntryForm TextBox se apresenta da seguinte forma quando há um erro de validação.

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

Se o Binding tiver regras de validação associadas, mas você não especificar um ErrorTemplate no controle acoplado, uma ErrorTemplate padrão será usada para notificar os usuários 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 de validação de vinculação de dados de preço para

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 em TwoWay e OneWayToSource vinculações. Para esclarecer, 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á algum objeto de ValidationRule personalizado definido cuja ValidationStep esteja configurada como RawProposedValue para esse Binding, caso em que chama o método Validate em cada ValidationRule até que um deles encontre um erro ou que todos passem.

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

  3. Se o conversor for bem-sucedido, o mecanismo de vinculação verificará se há algum objeto de ValidationRule personalizado definido cujo ValidationStep esteja definido como ConvertedProposedValue para esse Binding, caso em que ele chama o método Validate em cada ValidationRule que tem ValidationStep definido para ConvertedProposedValue até que um deles incorra em um erro ou até que todos estes passem.

  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 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 passem. 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 vinculação verifica se há algum objeto ValidationRule personalizado definido, cujo ValidationStep esteja configurado como CommittedValue para esse Binding. Nesse caso, ele chama o método Validate em cada ValidationRule que tem ValidationStep configurado para CommittedValue até que um deles 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 que o mecanismo de vinculação execute os objetos ValidationRule em qualquer etapa específica, ele remove qualquer ValidationError que foi adicionado à propriedade Validation.Errors anexada do elemento acoplado 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.

Observe 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 anexada.

Se a associaçã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 mecanismo de vinculação definir a origem, o mecanismo de vinculação verificará se há um UpdateSourceExceptionFilter. Você pode usar o callback UpdateSourceExceptionFilter para fornecer um manipulador personalizado para lidar com as exceções. Se um UpdateSourceExceptionFilter não for especificado no Binding, o mecanismo de vinculação criará um ValidationError com a exceção e o adicionará à 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