Compartilhar via


Revisão de Associação de Dados

Windows Presentation Foundation (WPF) data binding provides a simple and consistent way for applications to present and interact with data. Elements can be bound to data from a variety of data sources in the form of common language runtime (CLR) objects and XML. ContentControls, como Button e ItemsControls, como ListBox e ListView possui a funcionalidade interna para ativar estilos flexível de itens de dados únicos ou coleções de itens de dados. Sort, filter, and group views can be generated on top of the data.

The data binding functionality in WPF has several advantages over traditional models, including a broad range of properties that inherently support data binding, flexible UI representation of data, and clean separation of business logic from UI.

This topic first discusses concepts fundamental to WPF data binding and then goes into the usage of the Binding class and other features of data binding.

Este tópico contém as seguintes seções.

  • What Is Data Binding?
  • Basic Data Binding Concepts
  • Creating a Binding
  • Data Conversion
  • Binding to Collections
  • Data Templating
  • Data Validation
  • Debugging Mechanism
  • Tópicos relacionados

What Is Data Binding?

Data binding is the process that establishes a connection between the application UI and business logic. If the binding has the correct settings and the data provides the proper notifications, then, when the data changes its value, the elements that are bound to the data reflect changes automatically. Data binding can also mean that if an outer representation of the data in an element changes, then the underlying data can be automatically updated to reflect the change. For example, if the user edits the value in a TextBox element, the underlying data value is automatically updated to reflect that change.

A typical use of data binding is to place server or local configuration data into forms or other UI controls. In WPF, this concept is expanded to include the binding of a broad range of properties to a variety of data sources. In WPF, dependency properties of elements can be bound to CLR objects (including ADO.NET objects or objects associated with Web Services and Web properties) and XML data.

Para obter um exemplo de ligação de dados, veja o seguinte aplicativo UI partir do Demonstração de ligação de dados:

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

The above is the UI of an application that displays a list of auction items. The application demonstrates the following features of data binding:

  • The content of the ListBox is bound to a collection of AuctionItem objects. An AuctionItem object has properties such as Description, StartPrice, StartDate, Category, SpecialFeatures, etc.

  • The data (AuctionItem objects) displayed in the ListBox is templated so that the description and the current price are shown for each item. This is done using a DataTemplate. In addition, the appearance of each item depends on the SpecialFeatures value of the AuctionItem being displayed. If the SpecialFeatures value of the AuctionItem is Color, the item has a blue border. If the value is Highlight, the item has an orange border and a star. The Data Templating section provides information about data templating.

  • The user can group, filter, or sort the data using the CheckBoxes provided. In the image above, the "Group by category" and "Sort by category and date" CheckBoxes are selected. You may have noticed that the data is grouped based on the category of the product, and the category name is in alphabetical order. It is difficult to notice from the image but the items are also sorted by the start date within each category. This is done using a collection view. The Binding to Collections section discusses collection views.

  • When the user selects an item, the ContentControl displays the details of the selected item. This is called the Master-Detail scenario. The Master-Detail Scenario section provides information about this type of binding scenario.

  • The type of the StartDate property is DateTime, which returns a date that includes the time to the millisecond. In this application, a custom converter has been used so that a shorter date string is displayed. The Data Conversion section provides information about converters.

When the user clicks the Add Product button, the following form comes up:

Página Add Product Listing

The user can edit the fields in the form, preview the product listing using the short preview and the more detailed preview panes, and then click submit to add the new product listing. Any existing grouping, filtering and sorting functionalities will apply to the new entry. In this particular case, the item entered in the above image will be displayed as the second item within the Computer category.

Not shown in this image is the validation logic provided in the Start Date TextBox. If the user enters an invalid date (invalid formatting or a past date), the user will be notified with a ToolTip and a red exclamation point next to the TextBox. The Data Validation section discusses how to create validation logic.

Before going into the different features of data binding outlined above, we will first discuss in the next section the fundamental concepts that are critical to understanding WPF data binding.

Basic Data Binding Concepts

Esta seção contém as seguintes subseções.

  • Direction of the Data Flow
  • What Triggers Source Updates

Regardless of what element you are binding and the nature of your data source, each binding always follows the model illustrated by the following figure:

Diagrama de associação de dados básica

As illustrated by the above figure, data binding is essentially the bridge between your binding target and your binding source. The figure demonstrates the following fundamental WPF data binding concepts:

  • Normalmente, cada ligação tem esses quatro componentes: um destino de vinculação objeto, uma propriedade de destino, um origem da ligaçãoe um caminho para o valor na a origem de ligação para usar. For example, if you want to bind the content of a TextBox to the Name property of an Employee object, your target object is the TextBox, the target property is the Text property, the value to use is Name, and the source object is the Employee object.

  • The target property must be a dependency property. Most UIElement properties are dependency properties and most dependency properties, except read-only ones, support data binding by default. (Only DependencyObject types can define dependency properties and all UIElements derive from DependencyObject.)

  • Embora não especificado na figura, deve-se observar que o a origem de ligação o objeto não é restrito para sendo um personalizado CLR objeto. WPFligação de dados oferece suporte a dados na forma de CLR objetos e XML. To provide some examples, your binding source may be a UIElement, any list object, a CLR object that is associated with ADO.NET data or Web Services, or an XmlNode that contains your XML data. For more information, see Visão geral sobre associação de fontes.

Enquanto você lê outros software development kit (SDK) tópicos, é importante lembrar que quando você estiver estabelecendo uma ligação, você estiver vinculando um destino de vinculação para um a origem de ligação. For example, if you are displaying some underlying XML data in a ListBox using data binding, you are binding your ListBox to the XML data.

To establish a binding, you use the Binding object. The rest of this topic discusses many of the concepts associated with and some of the properties and usage of the Binding object.

Direction of the Data Flow

As mentioned previously and as indicated by the arrow in the figure above, the data flow of a binding can go from the binding target to the binding source (for example, the source value changes when a user edits the value of a TextBox) and/or from the binding source to the binding target (for example, your TextBox content gets updated with changes in the binding source) if the binding source provides the proper notifications.

You may want your application to enable users to change the data and propagate it back to the source object. Or you may not want to enable users to update the source data. You can control this by setting the Mode property of your Binding object. The following figure illustrates the different types of data flow:

Fluxo de dados de associação de dados

  • OneWay binding causes changes to the source property to automatically update the target property, but changes to the target property are not propagated back to the source property. This type of binding is appropriate if the control being bound is implicitly read-only. For instance, you may bind to a source such as a stock ticker or perhaps your target property has no control interface provided for making changes, such as a data-bound background color of a table. If there is no need to monitor the changes of the target property, using the OneWay binding mode avoids the overhead of the TwoWay binding mode.

  • TwoWay binding causes changes to either the source property or the target property to automatically update the other. This type of binding is appropriate for editable forms or other fully-interactive UI scenarios. Most properties default to OneWay binding, but some dependency properties (typically properties of user-editable controls such as the Text property of TextBox and the IsChecked property of CheckBox) default to TwoWay binding. A programmatic way to determine whether a dependency property binds one-way or two-way by default is to get the property metadata of the property using GetMetadata and then check the Boolean value of the BindsTwoWayByDefault property.

  • OneWayToSource is the reverse of OneWay binding; it updates the source property when the target property changes. One example scenario is if you only need to re-evaluate the source value from the UI.

  • Not illustrated in the figure is OneTime binding, which causes the source property to initialize the target property, but subsequent changes do not propagate. Isso significa que se o contexto de dados passa por uma alteração ou o objeto em que as alterações de contexto de dados, em seguida, a alteração não será refletida na propriedade de destino. This type of binding is appropriate if you are using data where either a snapshot of the current state is appropriate to use or the data is truly static. This type of binding is also useful if you want to initialize your target property with some value from a source property and the data context is not known in advance. This is essentially a simpler form of OneWay binding that provides better performance in cases where the source value does not change.

Note that to detect source changes (applicable to OneWay and TwoWay bindings), the source must implement a suitable property change notification mechanism such as INotifyPropertyChanged. See Como: Implement Property Change Notification for an example of an INotifyPropertyChanged implementation.

The Mode property page provides more information about binding modes and an example of how to specify the direction of a binding.

What Triggers Source Updates

Bindings that are TwoWay or OneWayToSource listen for changes in the target property and propagate them back to the source. This is known as updating the source. For example, you may edit the text of a TextBox to change the underlying source value. As described in the last section, the direction of the data flow is determined by the value of the Mode property of the binding.

However, does your source value get updated while you are editing the text or after you finish editing the text and point your mouse away from the TextBox? The UpdateSourceTrigger property of the binding determines what triggers the update of the source. The dots of the right arrows in the following figure illustrate the role of the UpdateSourceTrigger property:

Diagrama de UpdateSourceTrigger

If the UpdateSourceTrigger value is PropertyChanged, then the value pointed to by the right arrow of TwoWay or the OneWayToSource bindings gets updated as soon as the target property changes. However, if the UpdateSourceTrigger value is LostFocus, then that value only gets updated with the new value when the target property loses focus.

Similar to the Mode property, different dependency properties have different default UpdateSourceTrigger values. The default value for most dependency properties is PropertyChanged, while the Text property has a default value of LostFocus. This means that source updates usually happen whenever the target property changes, which is fine for CheckBoxes and other simple controls. However, for text fields, updating after every keystroke can diminish performance and it denies the user the usual opportunity to backspace and fix typing errors before committing to the new value. That is why the Text property has a default value of LostFocus instead of PropertyChanged.

See the UpdateSourceTrigger property page for information about how to find the default UpdateSourceTrigger value of a dependency property.

The following table provides an example scenario for each UpdateSourceTrigger value using the TextBox as an example:

UpdateSourceTrigger value

When the Source Value Gets Updated

Example Scenario for TextBox

LostFocus (padrão para TextBox.Text)

When the TextBox control loses focus

A TextBox that is associated with validation logic (see Data Validation section)

PropertyChanged

As you type into the TextBox

TextBox controls in a chat room window

Explicit

When the application calls UpdateSource

TextBox controls in an editable form (updates the source values only when the user clicks the submit button)

For an example, see Como: Control When the TextBox Text Updates the Source.

Creating a Binding

Esta seção contém as seguintes subseções.

  • Specifying the Binding Source
  • Specifying the Path to the Value
  • Binding and BindingExpression

Para recapitulate alguns dos conceitos discutidos nas seções anteriores, estabelecer uma ligação usando o Binding objeto e cada ligação geralmente tem quatro componentes: destino de vinculação, a propriedade de destino, origem da ligação e um caminho para o valor de origem para usar. This section discusses how to set up a binding.

Consider the following example, in which the binding source object is a class named MyData that is defined in the SDKSample namespace. For demonstration purposes, MyData class has a string property named ColorName, of which the value is set to "Red". Thus, this example generates a button with a red background.

<DockPanel
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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>

For more details on the binding declaration syntax and for examples of how to set up a binding in code, see Resumo de Declaração de Associações.

If we apply this example to our basic diagram, the resulting figure looks like the following. This is a OneWay binding because the Background property supports OneWay binding by default.

Diagrama de associação de dados

You may wonder why this works even though the ColorName property is of type string while the Background property is of type Brush. This is default type conversion at work and is discussed in the Data Conversion section.

Specifying the Binding Source

Notice that in the previous example, the binding source is specified by setting the DataContext property on the DockPanel element. The Button then inherits the DataContext value from the DockPanel, which is its parent element. To reiterate, the binding source object is one of the four necessary components of a binding. Therefore, without the binding source object being specified, the binding would do nothing.

There are several ways to specify the binding source object. Using the DataContext property on a parent element is useful when you are binding multiple properties to the same source. However, sometimes it may be more appropriate to specify the binding source on individual binding declarations. For the previous example, instead of using the DataContext property, you can specify the binding source by setting the Source property directly on the binding declaration of the button, as in the following example:

<DockPanel.Resources>
  <c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<Button Width="150" Height="30"
        Background="{Binding Source={StaticResource myDataSource},
                             Path=ColorName}">I am bound to be RED!</Button>

Other than setting the DataContext property on an element directly, inheriting the DataContext value from an ancestor (such as the button in the first example), and explicitly specifying the binding source by setting the Source property on the Binding (such as the button the last example), you can also use the ElementName property or the RelativeSource property to specify the binding source. The ElementName property is useful when you are binding to other elements in your application, such as when you are using a slider to adjust the width of a button. The RelativeSource property is useful when the binding is specified in a ControlTemplate or a Style. For more information, see Como: Especificar a Fonte de Associação.

Specifying the Path to the Value

If your binding source is an object, you use the Path property to specify the value to use for your binding. If you are binding to XML data, you use the XPath property to specify the value. In some cases, it may be applicable to use the Path property even when your data is XML. Por exemplo, se você deseja acessar a propriedade Name de um XmlNode retornado (como resultado de uma consulta XPath), você deve usar o Path propriedade além da XPath propriedade.

For syntax information and examples, see the Path and XPath property pages.

Note that although we have emphasized that the Path to the value to use is one of the four necessary components of a binding, in the scenarios which you want to bind to an entire object, the value to use would be the same as the binding source object. In those cases, it is applicable to not specify a Path. Consider the following example:

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

O exemplo acima usa a sintaxe de ligação vazia: {Binding}. In this case, the ListBox inherits the DataContext from a parent DockPanel element (not shown in this example). When the path is not specified, the default is to bind to the entire object. In other words, in this example, the path has been left out because we are binding the ItemsSource property to the entire object. (See the Binding to Collections section for an in-depth discussion.)

Other than binding to a collection, this scenario is also useful when you want to bind to an entire object instead of just a single property of an object. For example, if your source object is of type string and you simply want to bind to the string itself. Another common scenario is when you want to bind an element to an object with several properties.

Note that you may need to apply custom logic so that the data is meaningful to your bound target property. The custom logic may be in the form of a custom converter (if default type conversion does not exist). See Data Conversion for information about converters.

Binding and BindingExpression

Before getting into other features and usages of data binding, it would be useful to introduce the BindingExpression class. As you have seen in previous sections, the Binding class is the high-level class for the declaration of a binding; the Binding class provides many properties that allow you to specify the characteristics of a binding. A related class, BindingExpression, is the underlying object that maintains the connection between the source and the target. A binding contains all the information that can be shared across several binding expressions. A BindingExpression is an instance expression that cannot be shared and contains all the instance information of the Binding.

For example, consider the following, where myDataObject is an instance of MyData class, myBinding is the source Binding object, and MyData class is a defined class that contains a string property named MyDataProperty. This example binds the text content of mytext, an instance of TextBlock, to MyDataProperty.

Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
//make a new source
  MyData myDataObject = new MyData(DateTime.Now);      
  Binding myBinding = new Binding("MyDataProperty");
  myBinding.Source = myDataObject;
  myText.SetBinding(TextBlock.TextProperty, myBinding);

You can use the same myBinding object to create other bindings. For example, you may use myBinding object to bind the text content of a check box to MyDataProperty. In that scenario, there will be two instances of BindingExpression sharing the myBinding object.

A BindingExpression object can be obtained through the return value of calling GetBindingExpression on a data-bound object. The following topics demonstrate some of the usages of the BindingExpression class:

Data Conversion

In the previous example, the button is red because its Background property is bound to a string property with the value "Red". This works because a type converter is present on the Brush type to convert the string value to a Brush.

To add this information to the figure in the Creating a Binding section, the diagram looks like the following:

Diagrama de associação de dados

However, what if instead of having a property of type string your binding source object has a Color property of type Color? In that case, in order for the binding to work you would need to first turn the Color property value into something that the Background property accepts. You would need to create a custom converter by implementing the IValueConverter interface, as in the following example:

    <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
[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;
    }
}

The IValueConverter reference page provides more information.

Now the custom converter is used instead of default conversion, and our diagram looks like this:

Diagrama de associação de dados

To reiterate, default conversions may be available because of type converters that are present in the type being bound to. This behavior will depend on which type converters are available in the target. If in doubt, create your own converter.

Following are some typical scenarios where it makes sense to implement a data converter:

  • Your data should be displayed differently, depending on culture. For instance, you might want to implement a currency converter or a calendar date/time converter based on the values or standards used in a particular culture.

  • The data being used is not necessarily intended to change the text value of a property, but is instead intended to change some other value, such as the source for an image, or the color or style of the display text. Converters can be used in this instance by converting the binding of a property that might not seem to be appropriate, such as binding a text field to the Background property of a table cell.

  • More than one control or to multiple properties of controls are bound to the same data. In this case, the primary binding might just display the text, whereas other bindings handle specific display issues but still use the same binding as source information.

  • So far we have not yet discussed MultiBinding, where a target property has a collection of bindings. In the case of a MultiBinding, you use a custom IMultiValueConverter to produce a final value from the values of the bindings. For example, color may be computed from red, blue, and green values, which can be values from the same or different binding source objects. See the MultiBinding class page for examples and information.

Binding to Collections

Esta seção contém as seguintes subseções.

  • How to Implement Collections
  • Collection Views

A binding source object can be treated either as a single object of which the properties contain data or as a data collection of polymorphic objects that are often grouped together (such as the result of a query to a database). So far we've only discussed binding to single objects, however, binding to a data collection is a common scenario. For example, a common scenario is to use an ItemsControl such as a ListBox, ListView, or TreeView to display a data collection, such as in the application shown in the What Is Data Binding? section.

Fortunately, our basic diagram still applies. If you are binding an ItemsControl to a collection, the diagram looks like this:

Diagrama de ItemsControl de associação de dados

As shown in this diagram, to bind an ItemsControl to a collection object, ItemsSource property is the property to use. You can think of ItemsSource property as the content of the ItemsControl. Note that the binding is OneWay because the ItemsSource property supports OneWay binding by default.

How to Implement Collections

You can enumerate over any collection that implements the IEnumerable interface. However, to set up dynamic bindings so that insertions or deletions in the collection update the UI automatically, the collection must implement the INotifyCollectionChanged interface. This interface exposes an event that should be raised whenever the underlying collection changes.

WPF provides the ObservableCollection<T> class, which is a built-in implementation of a data collection that exposes the INotifyCollectionChanged interface. Note that to fully support transferring data values from source objects to targets, each object in your collection that supports bindable properties must also implement the INotifyPropertyChanged interface. For more information, see Visão geral sobre associação de fontes.

Before implementing your own collection, consider using ObservableCollection<T> or one of the existing collection classes, such as List<T>, Collection<T>, and BindingList<T>, among many others. If you have an advanced scenario and want to implement your own collection, consider using IList, which provides a non-generic collection of objects that can be individually accessed by index and thus the best performance.

Collection Views

Once your ItemsControl is bound to a data collection, you may want to sort, filter, or group the data. To do that, you use collection views, which are classes that implement the ICollectionView interface.

Esta seção contém as seguintes subseções.

  • What Are Collection Views?
  • How to Create a View
  • Sorting
  • Filtering
  • Grouping
  • Ponteiros do Item atual
  • Master-Detail Binding Scenario

What Are Collection Views?

Um modo de exibição da coleção é uma camada na parte superior de uma coleção de origem de ligação que permite que você navegue e exibir a coleção de origem com base na classificação, filtrar e agrupar consultas, sem alterar a coleção de origem subjacentes próprio. Um modo de exibição de coleção também mantém um ponteiro para o item atual na coleção. If the source collection implements the INotifyCollectionChanged interface, the changes raised by the CollectionChanged event are propagated to the views.

Because views do not change the underlying source collections, each source collection can have multiple views associated with it. For example, you may have a collection of Task objects. With the use of views, you can display that same data in different ways. For example, on the left side of your page you may want to show tasks sorted by priority, and on the right side, grouped by area.

How to Create a View

One way to create and use a view is to instantiate the view object directly and then use it as the binding source. Por exemplo, considere a A demonstração de ligação de dados aplicativo mostrado o o que é a ligação de dados? seção. The application is implemented such that the ListBox binds to a view over the data collection instead of the data collection directly. O exemplo a seguir é extraído da A demonstração de ligação de dados aplicativo. O CollectionViewSource classe é o Extensible Application Markup Language (XAML) o proxy de uma classe que herda de CollectionView. In this particular example, the Source of the view is bound to the AuctionItems collection (of type ObservableCollection<T>) of the current application object.

<Window.Resources>


...


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


...


</Window.Resources>

The resource listingDataView then serves as the binding source for elements in the application, such as the ListBox:

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


...


</ListBox>

To create another view for the same collection, you can create another CollectionViewSource instance and give it a different x:Key name.

A tabela a seguir mostra quais tipos de dados do modo de exibição são criados como a 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 exibição de coleção

Notes

IEnumerable

Um tipo interno, com base emCollectionView

Não é possível agrupar itens.

IList

ListCollectionView

Mais rápido.

IBindingList

BindingListCollectionView

Usando um modo de exibição padrão

A especificação de 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. WPF também cria um modo de exibição de coleção padrão para cada coleção usado como uma fonte de ligação. Se você vincular diretamente a uma coleção, WPF vincula seu modo de exibição padrão. Observe que esse modo de exibição padrão é compartilhado por todas as ligações à mesma coleção, para que uma alteração feita em um modo de exibição padrão por um controle acoplado ou código (como a classificação ou uma alteração do ponteiro atual do item, discutido posteriormente) é refletido em todas as demais ligações à mesma coleção.

To get the default view, you use the GetDefaultView method. For an example, see Como: Obter o Modo de Exibição Padrão de uma Coleção de Dados.

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

Para melhorar o desempenho, exibições de coleção do ADO.NET DataTable ou DataView objetos delegar a classificação e filtragem para o DataView. Isso causa a classificação e filtragem a ser compartilhado entre todos os modos de exibição de coleção da fonte de dados. Para ativar cada modo de exibição de coleção classificar e filtrar independentemente, inicializar cada modo de exibição da coleção com seu próprio DataView objeto.

Sorting

As mentioned before, views can apply a sort order to a collection. As it exists in the underlying collection, your data may or may not have a relevant, inherent order. The view over the collection allows you to impose an order, or change the default order, based on comparison criteria that you supply. Because it is a client-based view of the data, a common scenario is that the user might want to sort columns of tabular data per the value that the column corresponds to. Using views, this user-driven sort can be applied, again without making any changes to the underlying collection or even having to requery for the collection content. For an example, see Como: Sort a GridView Column When a Header Is Clicked.

The following example shows the sorting logic of the "Sort by category and date" CheckBox of the application UI in the What Is Data Binding? section:

private void AddSorting(object sender, RoutedEventArgs args)
{
    // This sorts the items first by Category and within each Category,
    // by StartDate. Notice that because Category is an enumeration,
    // the order of the items is the same as in the enumeration declaration
    listingDataView.SortDescriptions.Add(
        new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(
        new SortDescription("StartDate", ListSortDirection.Ascending));
}

Filtering

Views can also apply a filter to a collection. This means that although an item might exist in the collection, this particular view is intended to show only a certain subset of the full collection. You might filter on a condition in the data. For instance, as is done by the application in the What Is Data Binding? section, the "Show only bargains" CheckBox contains logic to filter out items that cost $25 or more. The following code is executed to set ShowOnlyBargainsFilter as the Filter event handler when that CheckBox is selected:

listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);

The ShowOnlyBargainsFilter event handler has the following implementation:

private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
    AuctionItem product = e.Item as AuctionItem;
    if (product != null)
    {
        // Filter out products with price 25 or above
        if (product.CurrentPrice < 25)
        {
            e.Accepted = true;
        }
        else
        {
            e.Accepted = false;
        }
    }
}

If you are using one of the CollectionView classes directly instead of CollectionViewSource, you would use the Filter property to specify a callback. For an example, see Como: Filtrar dados em um modo de exibição.

Grouping

Exceto para a classe interna que exibe um IEnumerable suportam a todas as exibições de coleção coleção, a funcionalidade de agrupamento, que permite que o usuário particionar a coleção no modo de exibição de coleção em grupos lógicos. The groups can be explicit, where the user supplies a list of groups, or implicit, where the groups are generated dynamically depending on the data.

The following example shows the logic of the "Group by category" CheckBox:

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

For another grouping example, see Como: Group Items in a ListView That Implements a GridView.

Ponteiros do Item atual

Views also support the notion of a current item. You can navigate through the objects in a collection view. Enquanto você navega, você está movendo um ponteiro de item que lhe permite recuperar o objeto que existe nesse local específico na coleção. For an example, see Como: Navigate Through the Objects in a Data CollectionView.

Porque o WPF se vincula a uma coleção usando somente um modo de exibição (um modo de exibição que você especificar, ou modo de exibição da coleção padrão), todas as ligações para as coleções têm um ponteiro atual do item. Ao ligar a um modo de exibição, a barra ("/") de caracteres em um Path valor designa o item atual da exibição. No exemplo a seguir, o contexto de dados é um modo de exibição de coleção. A primeira linha vincula-se à coleção. A segunda linha é vinculado ao item atual na coleção. A terceira linha vincula a Description a propriedade do item atual na coleção.

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

A sintaxe de barra e a propriedade também pode ser empilhada para atravessar uma hierarquia de coleções. O exemplo a seguir vincula ao item atual de uma coleção denominada Offices, que é uma propriedade do item atual da coleção de origem.

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

O ponteiro atual do item pode ser afetado por qualquer classificação ou filtragem é aplicada à coleção. Classificação preserva o ponteiro atual do item no último item selecionado, mas o modo de exibição de coleção agora é reestruturar ao redor dele. (Talvez o item selecionado foi no início da lista antes, mas agora o item selecionado pode estar em algum lugar no meio). Filtragem preserva o item selecionado se a seleção permanece no modo de exibição após a filtragem. Caso contrário, o ponteiro atual do item é definido para o primeiro item da exibição de coleção filtrada.

Master-Detail Binding Scenario

The notion of a current item is useful not only for navigation of items in a collection, but also for the master-detail binding scenario. Consider the application UI in the What Is Data Binding? section again. Nesse aplicativo, a seleção dentro do ListBox determina o conteúdo mostrado na ContentControl. Para colocá-lo de outra forma, quando um ListBox item é selecionado, o ContentControl mostra os detalhes do item selecionado.

You can implement the master-detail scenario simply by having two or more controls bound to the same view. O exemplo a seguir do A demonstração de ligação de dados mostra a marcação do ListBox e o ContentControl você pode ver no aplicativo UI na o que é a ligação de dados? seção:

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


...


</ListBox>


...


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

Notice that both of the controls are bound to the same source, the listingDataView static resource (see the definition of this resource in the How to Create a View section). This works because when a singleton object (the ContentControl in this case) is bound to a collection view, it automatically binds to the CurrentItem of the view. Note that CollectionViewSource objects automatically synchronize currency and selection. If your list control is not bound to a CollectionViewSource object as in this example, then you would need to set its IsSynchronizedWithCurrentItem property to true for this to work.

For other examples, see Como: Bind to a Collection and Display Information Based on Selection and Como: Use the Master-Detail Pattern with Hierarchical Data.

You may have noticed that the above example uses a template. In fact, the data would not be displayed the way we wish without the use of templates (the one explicitly used by the ContentControl and the one implicitly used by the ListBox). We now turn to data templating in the next section.

Data Templating

Without the use of data templates, our application UI in the What Is Data Binding? section would look like the following:

Demonstração de associação de dados sem modelos de dados

Conforme mostrado no exemplo na seção anterior, tanto o ListBox controle e o ContentControl são vinculados a objeto da coleção inteira (ou mais especificamente, o modo de exibição sobre o objeto da coleção) de AuctionItems. Sem instruções específicas sobre como exibir a coleta de dados, o ListBox está exibindo uma representação de seqüência de caracteres de cada objeto da coleção subjacente e o ContentControl está exibindo uma representação de seqüência de caracteres do objeto está vinculado a.

Para resolver o problema, o aplicativo define DataTemplates. Conforme mostrado no exemplo na seção anterior, o ContentControl usa explicitamente o detailsProductListingTemplate DataTemplate. The ListBox control implicitly uses the following DataTemplate when displaying the AuctionItem objects in the collection:

<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>

With the use of those two DataTemplates, the resulting UI is the one shown in the What Is Data Binding? section. As you can see from that screenshot, in addition to letting you place data in your controls, DataTemplates allow you to define compelling visuals for your data. For example, DataTriggers are used in the above DataTemplate so that AuctionItems with SpecialFeatures value of HighLight would be displayed with an orange border and a star.

For more information about data templates, see the Visão geral sobre Templating de dados.

Data Validation

Esta seção contém as seguintes subseções.

  • Associating Validation Rules with a Binding
  • Providing Visual Feedback
  • Validation Process

Most applications that take user input need to have validation logic to ensure that the user has entered the expected information. The validation checks can be based on type, range, format, or other application-specific requirements. This section discusses how data validation works in the WPF.

Associating Validation Rules with a Binding

The WPF data binding model allows you to associate ValidationRules with your Binding object. Por exemplo, o exemplo a seguir vincula um TextBox para uma propriedade chamada StartPrice e adiciona um ExceptionValidationRule o objeto para o Binding.ValidationRules propriedade.

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

A ValidationRule objeto verifica se o valor de uma propriedade é válido. WPFtem os seguintes dois tipos internos ValidationRule objetos:

You can also create your own validation rule by deriving from the ValidationRule class and implementing the Validate method. The following example shows the rule used by the Add Product Listing "Start Date" TextBox from the What Is Data Binding? section:

class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        DateTime date;
        try
        {
            date = DateTime.Parse(value.ToString());
        }
        catch (FormatException)
        {
            return new ValidationResult(false, "Value is not a valid date.");
        }
        if (DateTime.Now.Date > date)
        {
            return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            return ValidationResult.ValidResult;
        }
    }
}

The StartDateEntryForm TextBox uses this FutureDateRule, as shown in the following example:

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

Note that because the UpdateSourceTrigger value is PropertyChanged, the binding engine updates the source value on every keystroke, which means it also checks every rule in the ValidationRules collection on every keystroke. We discuss this further in the Validation Process section.

Providing Visual Feedback

If the user enters an invalid value, you may want to provide some feedback about the error on the application UI. Uma maneira de fornecer desses comentários são definir o Validation.ErrorTemplate anexado a propriedade para um personalizado ControlTemplate. Conforme mostrado na subseção anterior a StartDateEntryForm TextBox usa um ErrorTemplate chamado validationTemplate. The following example shows the definition of validationTemplate:

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

The AdornedElementPlaceholder element specifies where the control being adorned should be placed.

In addition, you may also use a ToolTip to display the error message. Both the StartDateEntryForm and the StartPriceEntryForm TextBoxes use the style textStyleTextBox, which creates a ToolTip that displays the error message. The following example shows the definition of textStyleTextBox. O anexado a propriedade Validation.HasError é true quando uma ou mais das ligações nas propriedades do elemento acoplado estão em erro.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
  <Setter Property="Foreground" Value="#333333" />
  <Setter Property="MaxLength" Value="40" />
  <Setter Property="Width" Value="392" />
  <Style.Triggers>
    <Trigger Property="Validation.HasError" Value="true">
      <Setter Property="ToolTip"
        Value="{Binding RelativeSource={RelativeSource Self},
                        Path=(Validation.Errors)[0].ErrorContent}"/>
    </Trigger>
  </Style.Triggers>
</Style>

With the custom ErrorTemplate and the ToolTip, the StartDateEntryForm TextBox looks like the following when there is a validation error:

Erro de validação de associação de dados

If your Binding has associated validation rules but you do not specify an ErrorTemplate on the bound control, a default ErrorTemplate will be used to notify users when there is a validation error. The default ErrorTemplate is a control template that defines a red border in the adorner layer. With the default ErrorTemplate and the ToolTip, the UI of the StartPriceEntryForm TextBox looks like the following when there is a validation error:

Erro de validação de associação de dados

For an example of how to provide logic to validate all controls in a dialog box, see the Custom Dialog Boxes section in the Dialog Boxes Overview.

Validation Process

Normalmente, a validação ocorre quando o valor de um destino é transferido para a propriedade de origem da ligação. Isso ocorre em TwoWay e OneWayToSource ligações. To reiterate, what causes a source update depends on the value of the UpdateSourceTrigger property, as described in the What Triggers Source Updates section.

A seguir descreve o validação processo. Observe que, se um erro de validação ou outro tipo de erro ocorrer a qualquer momento durante esse processo, o processo é interrompido.

  1. O mecanismo de ligação verifica se há qualquer personalizado ValidationRule objetos definidos cuja ValidationStep for definido como RawProposedValue para que Binding, caso em que ele chama o Validate método em cada ValidationRule até que um deles seja executado em um erro ou até que todos os pass.

  2. O mecanismo de ligação, em seguida, chama o conversor, se houver.

  3. Se o conversor for bem-sucedida, o mecanismo de ligação verifica se há qualquer personalizado ValidationRule objetos definidos cuja ValidationStep for definido como ConvertedProposedValue para que Binding, caso em que ele chama o Validate método em cada ValidationRule que tem ValidationStep definido como ConvertedProposedValue até que um deles seja executado em um erro ou até que todos os pass.

  4. O mecanismo de ligação define a propriedade de origem.

  5. O mecanismo de ligação verifica se há qualquer personalizado ValidationRule objetos definidos cuja ValidationStep for definido como UpdatedValue para que Binding, caso em que ele chama o Validate método em cada ValidationRule que tem ValidationStep definido como UpdatedValue até que um deles seja executado em um erro ou até que todos os pass. Se um DataErrorValidationRule está associada uma ligação e sua ValidationStep é definida como padrão, UpdatedValue, o DataErrorValidationRule é verificado nesse ponto. Também é o ponto quando as vinculações que têm o ValidatesOnDataErrors definido como true são verificadas.

  6. O mecanismo de ligação verifica se há qualquer personalizado ValidationRule objetos definidos cuja ValidationStep for definido como CommittedValue para que Binding, caso em que ele chama o Validate método em cada ValidationRule que tem ValidationStep definido como CommittedValue até que um deles seja executado em um erro ou até que todos os pass.

Se um ValidationRule não passa a qualquer momento durante todo esse processo, o mecanismo de ligação cria um ValidationError de objeto e adiciona-lo para o Validation.Errors coleção do elemento acoplado. Antes da ligação engine executa o ValidationRule objetos em qualquer etapa de determinado, ele remove qualquer ValidationError que foi adicionado para o Validation.Errors anexado a propriedade do elemento acoplado durante esta etapa. Por exemplo, se um ValidationRule cujo ValidationStep for definido como UpdatedValue falha, na próxima vez em que o processo de validação ocorre, o mecanismo de ligação remove que ValidationError imediatamente antes de chamar qualquer ValidationRule que tem ValidationStep definido como UpdatedValue.

Quando Validation.Errors não está vazio, o Validation.HasError anexado a propriedade do elemento é definido como true. Além disso, se o NotifyOnValidationError propriedade da Binding é definido como true, e em seguida, o mecanismo de ligação gera o Validation.Error anexado evento no elemento.

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

Se a ligação tem um ExceptionValidationRule associado a ele ou teve o ValidatesOnExceptions for definida como true e uma exceção é lançada quando o mecanismo de ligação define a origem, o mecanismo de ligação verifica se há um UpdateSourceExceptionFilter. You have the option to use the UpdateSourceExceptionFilter callback to provide a custom handler for handling exceptions. Se um UpdateSourceExceptionFilter não for especificado na Binding, o mecanismo de ligação cria um ValidationError com a exceção e adiciona-lo para o Validation.Errors coleção do elemento acoplado.

Debugging Mechanism

You can set the attached property PresentationTraceSources.TraceLevel on a binding-related object to receive information about the status of a specific binding.

Consulte também

Tarefas

Como: Bind to the Results of a LINQ Query

Como: Acoplar uma Fonte de Dados ADO.NET

Referência

DataErrorValidationRule

Conceitos

O que há de novo na versão 4 do WPF

Otimização de desempenho: Ligação de Dados

Outros recursos

Demonstração de ligação de dados

Data Binding How-to Topics