Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
A associação de dados no WPF (Windows Presentation Foundation) fornece uma maneira simples e consistente para os aplicativos apresentarem e interagirem com os dados. Os elementos podem ser associados a dados de diferentes tipos de fontes de dados na forma de objetos .NET e XML. Qualquer ContentControl um, como Button e qualquer ItemsControloutro , como ListBox e ListView, tem funcionalidade interna para habilitar o estilo flexível de itens de dados únicos ou coleções de itens de dados. As exibições de classificação, filtro e grupo podem ser geradas sobre os dados.
A associação de dados no WPF tem várias vantagens em relação aos modelos tradicionais, incluindo suporte inerente à associação de dados por uma ampla gama de propriedades, representação flexível de dados da interface do usuário e separação limpa da lógica de negócios da interface do usuário.
Este artigo primeiro discute os conceitos fundamentais para a associação de dados do WPF e, em seguida, aborda o uso da Binding classe e outros recursos da associação de dados.
O que é a associaçã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 exibidos. 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 associados aos dados refletirão as alterações automaticamente. A associaçã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 TextBox
elemento, o valor de dados subjacente será atualizado automaticamente para refletir essa alteração.
Um uso típico da associação de dados é colocar dados de configuração do servidor ou local em formulários ou outros controles de interface do usuário. No WPF, esse conceito é expandido para incluir a associação de uma ampla gama de propriedades a diferentes tipos de fontes de dados. No WPF, as propriedades de dependência de elementos podem ser associadas 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 associação de dados
Independentemente do elemento que você está associando e da natureza da fonte de dados, cada associação sempre segue o modelo ilustrado pela figura a seguir.
Como a figura mostra, a associação de dados é essencialmente a ponte entre seu destino de associação e sua fonte de associação. A figura demonstra os seguintes conceitos fundamentais de associação de dados do WPF:
Normalmente, cada associação tem quatro componentes:
- Um objeto de destino de associação.
- Uma propriedade de destino.
- Uma origem de associação.
- Um caminho para o valor na fonte de associação a ser usada.
Por exemplo, se você associar o conteúdo de um
TextBox
àEmployee.Name
propriedade, configurará sua associação como a tabela a seguir:Configurações Valor Meta 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 UIElement propriedades são propriedades de dependência e a maioria das propriedades de dependência, exceto as somente leitura, dão suporte à associação de dados por padrão. Somente os tipos derivados podem definir propriedades de DependencyObject dependência. Todos os UIElement tipos derivam de
DependencyObject
.As fontes de associação não são restritas a objetos .NET personalizados.
Embora não seja mostrado na figura, deve-se observar que o objeto de origem de associação não está restrito a ser um objeto .NET personalizado. A associação de dados do WPF dá suporte a dados na forma de objetos .NET, XML e até objetos de elemento XAML. Para fornecer alguns exemplos, sua fonte de associação pode ser um UIElementobjeto de lista, um objeto ADO.NET ou serviços Web ou um XmlNode que contenha seus dados XML. Para obter mais informações, consulte a visão geral das fontes de vinculação.
É importante lembrar que, quando você está estabelecendo uma vinculação, está vinculando um alvo de vinculação a uma fonte de vinculação. Por exemplo, se você estiver exibindo alguns dados XML subjacentes em uma ListBox associação de dados usando, você estará associando os ListBox
dados XML.
Para estabelecer uma associação, use o Binding objeto. O restante deste artigo discute muitos dos conceitos associados e algumas das propriedades e o uso do Binding
objeto.
Contexto de dados
Quando a associação de dados é declarada em elementos XAML, eles resolvem a associação de dados examinando sua propriedade imediata DataContext . O contexto de dados normalmente é o objeto de origem de associação para a avaliação do caminho do valor de origem de associação . Você pode substituir esse comportamento na associação e definir um valor de objeto de origem de associação específico. Se a DataContext
propriedade do objeto que hospeda a associação não estiver definida, a propriedade do DataContext
elemento pai será verificada e assim por diante, até a raiz da árvore de objetos XAML. Em suma, o contexto de dados usado para resolver a associação é herdado do pai, a menos que explicitamente definido no objeto.
As associações podem ser configuradas para serem resolvidas com um objeto específico, em vez de usar o contexto de dados para resolução de associação. Especificar um objeto de origem diretamente é usado quando, por exemplo, você associa a cor de primeiro plano de um objeto à cor da tela 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 associadas a objetos de origem específicos usam a resolução de contexto de dados.
Quando a DataContext
propriedade é 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 de associação para a origem da associação (por exemplo, o valor de origem muda quando um usuário edita o valor de um TextBox
) e/ou da fonte de associação para o destino de associação (por exemplo, seu TextBox
conteúdo é atualizado com alterações na origem da associação) se a fonte de 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:
OneWay A associaçã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. Esse tipo de associação é apropriado se o controle associado for implicitamente somente leitura. Por exemplo, você pode se vincular a uma origem, como uma cotação de ações, ou talvez a propriedade alvo não possua interface de controle fornecida para realizar 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 OneWay modo de associação evitará a sobrecarga do modo de TwoWay associação.
TwoWay A associação faz com que as alterações na propriedade de origem ou na propriedade de destino atualizem 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 usa associação OneWay por padrão, mas algumas propriedades de dependência (normalmente propriedades de controles editáveis pelo usuário, como o TextBox.Text e CheckBox.IsChecked) usam associação TwoWay por padrão.
Uma maneira programática de determinar se uma propriedade de dependência é associada de forma unidirecional ou bidirecional por padrão é obter os metadados da propriedade com DependencyProperty.GetMetadata. O tipo de retorno desse método é PropertyMetadata, que não contém metadados sobre associação. No entanto, se esse tipo puder ser convertido no derivado FrameworkPropertyMetadata, o valor booliano da FrameworkPropertyMetadata.BindsTwoWayByDefault propriedade poderá ser verificado. O exemplo de código a seguir demonstra como obter os metadados da TextBox.Text propriedade:
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 OneWay associação; atualiza a propriedade de origem quando a propriedade de destino é alterada. Um exemplo de cenário é se você precisar apenas reavaliar o valor de origem da interface usuário.
Não ilustrado na figura está a vinculação OneTime, que faz com que a propriedade de origem inicialize a de destino, mas não propaga as alterações subsequentes. Caso o contexto de dados mude ou o objeto dentro desse contexto seja modificado, a alteração não se refletirá na propriedade alvo. Esse tipo de vinculação é adequado se for apropriado ter um instantâneo do estado atual ou se os dados forem realmente estáticos. Esse tipo de associação também será ú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. Esse modo é essencialmente uma forma mais simples de OneWay associação que fornece melhor desempenho nos casos em que o valor de origem não é alterado.
Para detectar alterações de origem (aplicáveis às associações OneWay e TwoWay), a origem deve implementar um mecanismo de notificação de alteração de propriedade adequado, como INotifyPropertyChanged. Veja como implementar a notificação de alteração de propriedade (.NET Framework) para obter um exemplo de implementação INotifyPropertyChanged .
A Binding.Mode propriedade fornece mais informações sobre modos de associação e um exemplo de como especificar a direção de uma associação.
O que provoca atualizações na fonte
Associações que são TwoWay ou OneWayToSource escutam alterações na propriedade de destino e as propagam de volta para a origem, processo conhecido como atualização da origem. Por exemplo, você pode editar o texto de um TextBox para alterar o valor de origem subjacente.
No entanto, o 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 Binding.UpdateSourceTrigger propriedade determina o que dispara a atualização da origem. Os pontos das setas para a direita na figura a seguir ilustram o papel da propriedade Binding.UpdateSourceTrigger.
Se o valor de UpdateSourceTrigger
for UpdateSourceTrigger.PropertyChanged, então o valor apontado pela seta para a direita de TwoWay ou as associações de OneWayToSource será atualizado assim que a propriedade de destino for alterada. No entanto, se o valor UpdateSourceTrigger
for igual a LostFocus, esse valor será atualizado apenas por um novo valor quando a propriedade alvo perder o foco.
Semelhante à Mode propriedade, propriedades de dependência diferentes têm valores padrão UpdateSourceTrigger 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 boas 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 fazer backspace e corrigir erros de digitação antes de se comprometer com o novo valor. Por exemplo, a TextBox.Text
propriedade usa como padrão o UpdateSourceTrigger
valor 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 TextBox.Text
propriedade é alterada. Consulte a página de propriedades UpdateSourceTrigger para obter informações sobre como localizar 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 de 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 associado à lógica de validação (consulte a Validação de Dados abaixo). |
PropertyChanged |
À medida que você digita no TextBox. | Controles de caixa de texto 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 do TextBox atualiza a origem (.NET Framework).
Exemplo de associação de dados
Para um exemplo de associação de dados, dê uma olhada na interface do usuário do seguinte aplicativo de demonstração de associação de dados, que exibe uma lista de itens de leilão.
O aplicativo demonstra os seguintes recursos de associação de dados:
O conteúdo do ListBox está associado a uma coleção de objetos AuctionItem . Um objeto AuctionItem tem propriedades como Description, StartPrice, StartDate, Category e 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 SpecialFeatures do AuctionItem que está sendo exibido. Se o valor SpecialFeatures do AuctionItem for Color, o item terá uma borda azul. Se o valor for Realce, o item terá uma borda laranja e uma estrela. A seção Data Templating fornece informações sobre a modelagem de dados.O usuário pode agrupar, filtrar ou classificar os dados usando os
CheckBoxes
fornecidos. Na imagem acima, o Grupo por categoria e Classificar por categoria e dataCheckBoxes
sã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 observar da imagem, mas os itens também são classificados pela data de início dentro de cada categoria. A classificação é feita usando uma exibição de coleção. A seção Associação a coleções trata de visões de coleção.Quando o usuário seleciona um item, exibe ContentControl os detalhes do item selecionado. Essa experiência é chamada cenário mestre-detalhe. 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 Conversão de dados fornece informações sobre conversores.
Quando o usuário seleciona o botão Adicionar Produto , o formulário a seguir aparece.
O usuário pode editar os campos no formulário, visualizar a listagem do produto usando os painéis de visualização curtos ou detalhados e selecionar Submit
para adicionar a nova listagem de produtos. Todas as configurações de agrupamento, filtragem e classificação existentes serão aplicadas à nova entrada. Nesse caso específico, o item inserido na imagem acima será exibido como o segundo item dentro da categoria Computador .
A lógica de validação fornecida na Data de Início nã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 ponto de exclamação vermelho e um ponto de exclamação ao lado do TextBox. A seção Validação de Dados discute como criar lógica de validação.
Antes de entrar nos diferentes recursos de associação de dados descritos acima, primeiro discutiremos os conceitos fundamentais que são fundamentais para entender a associação de dados do WPF.
Criar uma associação
Para reafirmar alguns dos conceitos discutidos nas seções anteriores, você estabelece uma associação usando o Binding objeto e cada associação geralmente tem quatro componentes: um destino de associação, uma propriedade de destino, uma origem de associação e um caminho para o valor de origem a ser usado. Esta seção discute como configurar uma vinculação.
As fontes de associação estão ligadas ao DataContext ativo do elemento. Os elementos herdam DataContext
automaticamente se não tiverem explicitamente definido um.
Considere o exemplo a seguir, no qual o objeto de origem de associação é uma classe chamada MyData que é definida no namespace SDKSample . Para fins de demonstração, MyData tem uma propriedade de cadeia de caracteres chamada ColorName cujo valor é definido como "Vermelho". Assim, este exemplo gera um botão com um plano de 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 associação e exemplos de como configurar uma associação no código, consulte a visão geral das declarações de associação.
Se aplicarmos este exemplo ao nosso diagrama básico, a figura resultante será semelhante à seguinte. Essa figura descreve uma OneWay vinculação porque a propriedade Background suporta a vinculação OneWay por padrão.
Você pode se perguntar por que essa associação funciona mesmo que a propriedade ColorName seja do tipo cadeia de caracteres enquanto a Background propriedade for 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 associação
Observe que, no exemplo anterior, a fonte de associação é especificada definindo a propriedade DockPanel.DataContext . O Button então herda o DataContext valor do DockPanelelemento pai. Para reiterar, o objeto de origem de associação é um dos quatro componentes necessários de uma associação. Portanto, sem que o objeto de origem de associação fosse especificado, a associação não faria nada.
Há várias maneiras de especificar o objeto de origem de associação. Usar a DataContext propriedade em um elemento pai é útil quando você está associando várias propriedades à mesma origem. No entanto, às vezes, pode ser mais apropriado especificar a origem da associação em declarações de associação individuais. Para o exemplo anterior, em vez de usar a DataContext propriedade, você pode especificar a origem da associação definindo a Binding.Source propriedade diretamente na declaração de associaçã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 DataContext propriedade em um elemento diretamente, herdar o DataContext valor de um ancestral (como o botão no primeiro exemplo) e especificar explicitamente a origem da associação definindo a Binding.Source propriedade na associação (como o botão do último exemplo), você também pode usar a Binding.ElementName propriedade ou a Binding.RelativeSource propriedade para especificar a origem da associação. A ElementName propriedade é útil quando você está associando a outros elementos em seu aplicativo, como quando você está usando um controle deslizante para ajustar a largura de um botão. A RelativeSource propriedade é útil quando a associação é especificada em um ControlTemplate ou um Style. Para obter mais informações, consulte a visão geral das fontes de vinculação.
Especificando o caminho para o valor
Se a origem da associação for um objeto, você usará a Binding.Path propriedade para especificar o valor a ser usado para sua associação. Se você estiver associando dados XML, use a propriedade Binding.XPath para especificar o valor. Em alguns casos, pode ser aplicável usar a Path propriedade 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), deverá usar a Path propriedade além da XPath propriedade.
Para obter mais informações, consulte as propriedades Path e XPath.
Embora tenhamos enfatizado que o Path valor a ser usado é um dos quatro componentes necessários de uma associação, nos cenários em que você deseja vincular a um objeto inteiro, o valor a ser usado seria o mesmo que o objeto de origem de associaçã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 associação vazia: {Binding}. Nesse caso, o ListBox herda o DataContext de um elemento pai DockPanel (não mostrado neste exemplo). Quando o caminho não é especificado, o padrão é associar ao objeto inteiro. Em outras palavras, neste exemplo, o caminho foi deixado de fora porque estamos associando a ItemsSource propriedade ao objeto inteiro. (Consulte a seção Vinculação a coleções para uma discussão detalhada.)
Além de associar a uma coleção, esse cenário também é útil quando você deseja associar 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 associar à cadeia de caracteres em si. Outro cenário comum é quando você deseja associar um elemento a um objeto com várias propriedades.
Você pode precisar aplicar lógica personalizada para que os dados sejam significativos para a propriedade vinculada de destino. A lógica personalizada pode estar na forma de um conversor personalizado se a conversão de tipo padrão não existir. Consulte a conversão de dados para obter informações sobre conversores.
Binding e BindingExpression
Antes de entrar em outros recursos e usos da associação de dados, é útil introduzir a BindingExpression classe. Como você viu em seções anteriores, a Binding classe é a classe de alto nível para a declaração de uma associação; ela fornece muitas propriedades que permitem especificar as características de uma associação. Uma classe relacionada é BindingExpressiono 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 associação. A 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
está uma instância da MyData
classe, myBinding
é o objeto de origem Binding e MyData
é uma classe definida que contém uma propriedade de cadeia de caracteres chamada ColorName
. Este exemplo associa 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 associar o conteúdo do texto de uma caixa de seleção ao ColorName. Nesse cenário, haverá duas instâncias de BindingExpression compartilhando o objeto myBinding.
Um BindingExpression objeto é retornado chamando GetBindingExpression um objeto associado a dados. Os artigos a seguir demonstram alguns dos usos da BindingExpression classe:
- Obter o objeto de associação de uma propriedade de destino associada (.NET Framework)
- Controlar quando o texto da caixa de texto atualiza a origem (.NET Framework)
Conversão de dados
Na seção Criar uma associação , o botão é vermelho porque sua Background propriedade está associada a uma propriedade de cadeia de caracteres com o valor "Red". Esse valor de cadeia de caracteres funciona porque um conversor de tipo está presente no Brush tipo para converter o valor da cadeia de caracteres em um Brush.
A adição dessas informações à figura na seção Criar uma associação é semelhante a esta.
No entanto, e se, em vez de ter uma propriedade de cadeia de caracteres de tipo, seu objeto de origem de associação tiver uma propriedade Color do tipo Color? Nesse caso, para que a associação funcione, você precisaria primeiro transformar o valor da propriedade Color em algo que a Background propriedade aceita. Você precisaria criar um conversor personalizado implementando a IValueConverter interface, 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 de conversão padrão, e nosso diagrama tem esta aparência.
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á associado. Esse comportamento dependerá de quais conversores de tipo estão disponíveis no destino. Em caso de dúvida, crie seu próprio conversor.
Veja a seguir 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, talvez você queira implementar um conversor de moeda ou um conversor de data/hora do 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 se destinam 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 nesta instância convertendo a associação de uma propriedade que pode não parecer apropriada, como associar um campo de texto à propriedade Background de uma célula de tabela.
Mais de um controle ou várias propriedades de controles estão associados aos mesmos dados. Nesse caso, a associação primária pode apenas exibir o texto, enquanto outras associações lidam com problemas de exibição específicos, mas ainda usam a mesma associação que as informações de origem.
Uma propriedade de destino tem uma coleção de vínculos denominada MultiBinding. Para MultiBinding, você usa um IMultiValueConverter personalizado para produzir um valor final a partir dos valores das vinculações. Por exemplo, a cor pode ser computada a partir de valores vermelhos, azuis e verdes, que podem ser valores dos mesmos objetos de origem de associação ou diferentes. Consulte MultiBinding para exemplos e informações.
Vinculação a coleções
Um objeto de origem de associaçã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 associação a objetos únicos. No entanto, a associação a uma coleta de dados é um cenário comum. Por exemplo, um cenário comum é usar um ItemsControl tal como um ListBox, ListViewou TreeView exibir uma coleção de dados, como no aplicativo mostrado na seção O que é associação de dados .
Felizmente, nosso diagrama básico ainda se aplica. Se você estiver associando um ItemsControl a uma coleção, o diagrama terá esta aparência.
Conforme mostrado neste diagrama, para associar um objeto ItemsControl a um objeto de coleção, a propriedade ItemsControl.ItemsSource é a ser usada. Você pode pensar ItemsSource
como o conteúdo do ItemsControl. A associação é OneWay porque a propriedade ItemsSource
dá suporte à associação OneWay
por padrão.
Como implementar coleções
Você pode enumerar em 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 ObservableCollection<T> classe, que é uma implementação interna de uma coleção de dados que expõe a INotifyCollectionChanged interface. Para dar suporte total à transferência de valores de dados de objetos de origem para destinos, cada objeto em sua coleção que dá suporte a propriedades associáveis também deve implementar a INotifyPropertyChanged interface. Para obter mais informações, consulte a visão geral das 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.
Exibições de coleções
Uma vez que seu ItemsControl está associado a uma coleta de dados, talvez você queira classificar, filtrar ou agrupar os dados. Para fazer isso, use exibições de coleção, que são classes que implementam a ICollectionView interface.
O que são exibições de coleção?
Uma exibição de coleção é uma camada sobre uma coleção de origem vinculada que permite navegar e exibir a coleção de origem com base em consultas de ordenação, filtragem e agrupamento, sem a necessidade de alterar a própria coleção de origem subjacente. Uma visualizaçã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 visualizações.
Como as exibições não alteram as coleções de origem subjacentes, cada coleção de origem pode ter várias exibições associadas a ela. Por exemplo, você pode ter uma coleção de objetos Task . Com o uso de visões, você pode apresentar esses mesmos dados de maneiras diferentes. Por exemplo, no lado esquerdo da página, talvez você queira mostrar tarefas classificadas por prioridade e, no lado direito, agrupadas por área.
Como criar uma visão
Uma maneira de criar e usar uma visão é instanciar o objeto de visão diretamente e usá-lo como a fonte de ligação. Por exemplo, considere o aplicativo de demonstração de associação de dados mostrado na seção O que é associação de dados. O aplicativo é implementado de forma que o ListBox se vincule a uma visualização da coleção de dados, em vez de se vincular diretamente à coleção de dados. O exemplo a seguir é extraído do aplicativo de demonstração de associação de dados. A CollectionViewSource classe é o proxy XAML de uma classe que herda de CollectionView. Neste exemplo específico, a visão Source está associada à coleção AuctionItems (do tipo ObservableCollection<T>) do objeto atual do aplicativo.
<Window.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"
x:Key="listingDataView" />
</Window.Resources>
O recurso listingDataView serve então como a fonte de vínculo 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 exibição para a mesma coleção, você pode criar outra CollectionViewSource instância e dar a ela um nome diferente x:Key
.
A tabela a seguir mostra quais tipos de visualização de dados são criados como a visualizaçã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 exibição de coleção | Anotações |
---|---|---|
IEnumerable | Um tipo interno baseado em CollectionView | Não é possível agrupar itens. |
IList | ListCollectionView | O mais rápido. |
IBindingList | BindingListCollectionView |
Usando uma exibição padrão
Especificar uma exibição de coleção como uma origem de associação é uma maneira de criar e usar uma 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 associação. Se você associar diretamente a uma coleção, o WPF se liga à 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 um modo de exibição padrão por um controle ou código associado (como classificação ou alteração no ponteiro do item atual, discutido posteriormente) é refletida em todas as outras associações para a mesma coleção.
Para obter a exibição padrão, use o GetDefaultView método. Para obter um exemplo, consulte Obter a exibição padrão de uma coleção de dados (.NET Framework).
Visualizações de coleção com ADO.NET DataTables
Para melhorar o desempenho, as exibições de coleção para objetos ADO.NET DataTable ou DataView delegam a tarefa de classificação e filtragem para o DataView que faz com que a classificação e a filtragem sejam compartilhadas em todas as vistas de coleção da fonte de dados. Para permitir que cada exibição de coleção classifique e filtre de forma independente, inicialize cada exibição de coleção com seu próprio DataView objeto.
Classificação
Conforme mencionado antes, as exibições podem aplicar uma ordem de classificação a uma coleção. Como eles existem na coleção subjacente, seus dados podem ou não ter uma ordem relevante e inerente. A exibição sobre a coleção permite que você imponha uma ordem ou altere a ordem padrão, com base nos critérios de comparação fornecidos. Como é uma exibição baseada no cliente dos dados, um cenário comum é que o usuário pode querer classificar colunas de dados tabulares de acordo com o valor ao qual a coluna corresponde. Usando exibições, essa classificação controlada pelo usuário pode ser aplicada novamente sem fazer nenhuma alteração na coleção subjacente ou sequer precisar consultar novamente o conteúdo da coleção. Para obter um exemplo, consulte Classificar uma coluna GridView quando um cabeçalho for clicado (.NET Framework).
O exemplo a seguir mostra a lógica de classificação de "Classificar por categoria e data" CheckBox da interface do usuário do aplicativo na seção O que é associaçã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 exibições também podem aplicar um filtro a uma coleção, de modo que a exibição mostre apenas um determinado subconjunto da coleção completa. Você pode filtrar com base em uma condição dos dados. Por exemplo, como é feito pelo aplicativo na seção O que é associação de dados , "Mostrar apenas barganhas" CheckBox contém lógica para filtrar itens que custam US$ 25 ou mais. O código a seguir é executado para definir ShowOnlyBargainsFilter como o Filter manipulador de eventos quando selecionado CheckBox .
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 CollectionView classes diretamente em vez de CollectionViewSource, você usaria a propriedade Filter para especificar um retorno de chamada. Para obter um exemplo, consulte Filtrar Dados em uma Exibição (.NET Framework).
Agrupamento
Exceto pela classe interna que exibe uma IEnumerable coleção, todas as exibições de coleção dão suporte ao agrupamento, o que permite ao usuário particionar a coleção na exibição de coleção em grupos lógicos. Os grupos podem ser explícitos, em que o usuário fornece uma lista de grupos ou implícita, em que os grupos são gerados dinamicamente dependendo dos dados.
O exemplo a seguir mostra a lógica de "Agrupar por categoria" CheckBox.
// 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 de Grupo em um ListView que implementa um GridView (.NET Framework).
Ponteiros de itens atuais
As exibições também dão suporte à noção de um item atual. Você pode navegar pelos objetos em um modo de 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 na coleção. Para obter um exemplo, consulte Navegar pelos objetos em um CollectionView de dados (.NET Framework).
Como o WPF se associa a uma coleção apenas usando uma exibição (uma exibição especificada ou a exibição padrão da coleção), todas as associações às coleções têm um ponteiro para o item atual. Ao associar a uma visualização, o caractere de barra ("/") no valor Path
indica o item atual da visualização. No exemplo a seguir, o contexto de dados é uma visualização de coleção. A primeira linha se vincula à coleção. A segunda linha vincula-se ao item atual na 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 de propriedade também pode ser empilhada para percorrer uma hierarquia de coleções. O exemplo a seguir associa-se ao item atual de uma coleção nomeada 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 o modo de exibição de coleção agora é reestruturado 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 preservará 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 associação mestre-detalhe
A noção de um item atual é útil não apenas para navegação de itens em uma coleção, mas também para o cenário de vinculação mestre-detalhe. Considere a interface do usuário do aplicativo na seção O que é associação de dados novamente. Nesse aplicativo, a seleção dentro do ListBox determina o conteúdo mostrado no ContentControl. Para colocá-lo de outra forma, quando um ListBox item é selecionado, mostra ContentControl os detalhes do item selecionado.
Você pode implementar o cenário mestre-detalhe simplesmente vinculando dois ou mais controles à mesma visão. O exemplo a seguir do Data binding demo mostra a marcação que você vê na UI do aplicativo na seção ListBox.
<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 associados à mesma origem, o recurso estático listingDataView (consulte a definição desse recurso na seção Como criar uma exibição). Essa associação funciona porque quando um objeto (ContentControl nesse caso) está ligado a uma exibição de coleção, ele se associa automaticamente ao CurrentItem da exibição. Os CollectionViewSource objetos sincronizam automaticamente a moeda e a seleção. Se o controle de lista não estiver associado a um CollectionViewSource objeto como neste exemplo, você precisará definir sua IsSynchronizedWithCurrentItem propriedade para true
que isso funcione.
Para obter outros exemplos, consulte Associar a uma coleção e exibir informações com base na seleção (.NET Framework) e usar o padrão de detalhes mestre com dados hierárquicos (.NET Framework).
Talvez você tenha notado que o exemplo acima usa um modelo. Na verdade, os dados não seriam exibidos da maneira desejada sem o uso de modelos (aquele explicitamente usado pelo ContentControl e aquele usado implicitamente pelo ListBox). Agora, recorremos à modelagem de dados na próxima seção.
Modelagem de dados
Sem o uso de modelos de dados, a interface do usuário do aplicativo na seção Exemplo de associação de dados seria semelhante à seguinte:
Conforme mostrado no exemplo na seção anterior, tanto o ListBox controle quanto o ContentControl são vinculados ao objeto de coleção inteiro (ou mais especificamente, à visão sobre o objeto de coleção) de AuctionItems. Sem instruções específicas de como exibir a coleção de dados, o ListBox mostra a representação em cadeia de caracteres de cada objeto na coleção subjacente, e o ContentControl mostra a representação em cadeia de caracteres do objeto ao qual está vinculado.
Para resolver esse problema, o aplicativo define DataTemplates. Conforme mostrado no exemplo na seção anterior, o ContentControl modelo de dados detailsProductListingTemplate é usado explicitamente. O ListBox controle usa implicitamente o modelo de dados a seguir 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 desses dois DataTemplates, a interface do usuário resultante é a mostrada na seção O que é associação de dados . Como você pode ver nessa captura de tela, além de permitir que você coloque dados em seus controles, o DataTemplates permite que você defina visuais atraentes para seus dados. Por exemplo, DataTriggers são usados no DataTemplate acima para que AuctionItems com o valor SpecialFeatures de Destaque sejam exibidos com uma borda laranja e uma estrela.
Para obter mais informações sobre modelos de dados, consulte a visão geral de modelagem de dados (.NET Framework).
Validação de dados
A maioria dos aplicativos que tomam 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 no tipo, intervalo, formato ou outros requisitos específicos do aplicativo. Esta seção discute como a validação de dados funciona no WPF.
Associando regras de validação a uma vinculação
O modelo de associação de dados do WPF permite que você associe ValidationRules ao seu Binding objeto. Por exemplo, o exemplo a seguir associa uma TextBox propriedade nomeada StartPrice
e adiciona um ExceptionValidationRule objeto à Binding.ValidationRules propriedade.
<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 ValidationRule objeto verifica se o valor de uma propriedade é válido. O WPF tem dois tipos de objetos internos ValidationRule :
Verifica ExceptionValidationRule se há exceções geradas durante a atualização da propriedade de origem da vinculação. No exemplo anterior,
StartPrice
é de tipo inteiro. Quando o usuário insere um valor que não pode ser convertido em um inteiro, uma exceção é gerada, fazendo com que a associação seja marcada como inválida. Uma sintaxe alternativa para definir ExceptionValidationRule explicitamente é definir a propriedade ValidatesOnExceptions comotrue
em seu objeto Binding ou MultiBinding.Um DataErrorValidationRule objeto verifica se há erros gerados por objetos que implementam a IDataErrorInfo interface. Para obter mais informações sobre como usar essa regra de validação, consulte DataErrorValidationRule. Uma sintaxe alternativa para definir DataErrorValidationRule explicitamente é definir a propriedade ValidatesOnDataErrors como
true
em seu objeto Binding ou MultiBinding.
Você também pode criar sua própria regra de validação derivando da ValidationRule classe e implementando o Validate método. O exemplo a seguir mostra a regra usada pela seção Adicionar Lista de Produtos "Data de Início" TextBox da seção O que é associaçã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 StartDateEntryFormTextBox usa este 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 de UpdateSourceTrigger é PropertyChanged, o sistema de ligação atualiza o valor original a cada pressionamento de tecla, o que significa que ele também verifica todas as regras em cada coleção de ValidationRules a cada pressionamento de tecla. Discutiremos isso ainda mais na seção Processo de Validação.
Fornecendo comentários visuais
Se o usuário inserir um valor inválido, talvez você queira fornecer alguns comentários sobre o erro na interface do usuário do aplicativo. Uma maneira de fornecer esse feedback é definir a propriedade anexada Validation.ErrorTemplate para um ControlTemplate personalizado. Conforme mostrado na subseção anterior, o StartDateEntryFormTextBox usa um ErrorTemplatechamado 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 AdornedElementPlaceholder elemento 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 StartPriceEntryFormTextBoxusam 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 Validation.HasError anexada é true
quando uma ou mais das associações nas propriedades do elemento associado 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 personalizado ErrorTemplate e o ToolTip, o StartDateEntryFormTextBox fica assim quando há um erro de validação.
Se o seu Binding tiver regras de validação associadas, mas você não especificar um ErrorTemplate no controle vinculado, um ErrorTemplate padrão será usado para notificar os usuários quando houver um erro de validação. O padrão ErrorTemplate é um modelo de controle que define uma borda vermelha na camada de adornos. Com o padrão ErrorTemplate e ToolTip, a interface do usuário do StartPriceEntryFormTextBox fica assim quando há um erro de validação.
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 alvo é transferido para a propriedade de origem da ligação. Essa transferência ocorre nas associações TwoWay e OneWayToSource. Para reiterar, o que causa uma atualização de origem depende do valor da UpdateSourceTrigger propriedade, conforme descrito na seção What triggers source updates .
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 esse processo, o processo será interrompido:
O mecanismo de associação verifica se há algum objeto personalizado ValidationRule cujo ValidationStep esteja configurado para RawProposedValue nesse Binding. Nesse caso, ele chama o método Validate em cada ValidationRule até que um deles encontre um erro ou que todos passem.
O mecanismo de associação, em seguida, chama o conversor, se houver um.
Se o conversor for bem-sucedido, o mecanismo de associação verifica se há objetos personalizados ValidationRule definidos cujo ValidationStep está configurado como ConvertedProposedValue para aquele Binding; nesse caso, ele chama o método Validate em cada ValidationRule que tem ValidationStep configurado como ConvertedProposedValue, até que um deles encontre um erro ou até que todos passem.
O mecanismo de associação define a propriedade de origem.
O mecanismo de associação verifica se há objetos personalizados ValidationRule definidos cujo ValidationStep está configurado para UpdatedValue nesse Binding, caso em que ele chama o método Validate em cada ValidationRule cujo ValidationStep está 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 seu ValidationStep definido como padrão, UpdatedValue, o DataErrorValidationRule é verificado neste ponto. Neste ponto, qualquer associação que tenha o ValidatesOnDataErrors configurado para
true
é verificada.O mecanismo de associação verifica se há objetos personalizados ValidationRule definidos cujo ValidationStep está configurado para CommittedValue nesse Binding, caso em que ele chama o método Validate em cada ValidationRule cujo ValidationStep está definido 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 associação criará um ValidationError objeto e o Validation.Errors adicionará à coleção do elemento associado. Antes que o mecanismo de associação execute os ValidationRule objetos em qualquer etapa, ele remove qualquer ValidationError que tenha sido adicionado à propriedade anexada Validation.Errors do elemento associado durante essa etapa. Por exemplo, se um ValidationRule cujo valor de ValidationStep está definido como UpdatedValue falhar, na próxima vez que o processo de validação ocorrer, o mecanismo de associação removerá esse ValidationError imediatamente antes de chamar qualquer ValidationRule que tenha ValidationStep definido como UpdatedValue.
Quando Validation.Errors não estiver vazio, a Validation.HasError propriedade anexada do elemento será definida como true
. Além disso, se a propriedade NotifyOnValidationError do Binding for configurada como true
, então o mecanismo de associação levanta 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 vinculada Validation.Errors.
Se a associação tiver um ExceptionValidationRule associado a ele ou se a ValidatesOnExceptions propriedade estiver definida true
e uma exceção for gerada quando o mecanismo de associação definir a origem, o mecanismo de associação verificará se há um UpdateSourceExceptionFilter. Você pode usar o UpdateSourceExceptionFilter callback para fornecer um handler personalizado para o tratamento de exceções. Se um UpdateSourceExceptionFilter não for especificado no Binding, o mecanismo de associação criará um ValidationError com a exceção e o adicionará à coleção de Validation.Errors do elemento associado.
Mecanismo de debug
Você pode definir a propriedade PresentationTraceSources.TraceLevel anexada em um objeto relacionado à associação para receber informações sobre o status de uma associação específica.
Consulte também
.NET Desktop feedback