Suggerisci traduzione
 
Altri utenti hanno suggerito:

progress indicator
Nessun altro suggerimento.
Valuta il contenuto e lascia un commento
MSDN
MSDN Library
.NET Framework 4
WPF
Dati
 Cenni preliminari sull'associazione...
Comprimi tutto/Espandi tutto Comprimi tutto
Visualizza contenuto:  affiancatoVisualizza contenuto: affiancato
.NET Framework 4 - Windows Presentation Foundation
Data Binding Overview

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 such as Button and ItemsControls such as ListBox and ListView have built-in functionality to enable flexible styling of single data items or collections of data items. 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.

This topic contains the following sections.

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.

For an example of data binding, take a look at the following application UI from the Data Binding Demo:

Data binding sample screen shot

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:

Add Product Listing page

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.

This section contains the following subsections.

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:

Basic data binding diagram

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:

  • Typically, each binding has these four components: a binding target object, a target property, a binding source, and a path to the value in the binding source to use. 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.)

  • Although not specified in the figure, it should be noted that the binding source object is not restricted to being a custom CLR object. WPF data binding supports data in the form of CLR objects and 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 Binding Sources Overview.

As you read through other software development kit (SDK) topics, it is important to remember that when you are establishing a binding, you are binding a binding target to a binding source. 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:

Data binding data flow
  • 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. This means that if the data context undergoes a change or the object in the data context changes, then the change is not reflected in the target property. 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 How to: 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:

UpdateSourceTrigger diagram

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 (default for 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 How to: Control When the TextBox Text Updates the Source.

This section contains the following subsections.

To recapitulate some of the concepts discussed in the previous sections, you establish a binding using the Binding object, and each binding usually has four components: binding target, target property, binding source, and a path to the source value to use. 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.

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

For more details on the binding declaration syntax and for examples of how to set up a binding in code, see Binding Declarations Overview.

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.

Data binding diagram

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:

XAML
<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 How to: Specify the Binding Source.

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. For example, if you want to access the Name property of a returned XmlNode (as a result of an XPath query), you should use the Path property in addition to the XPath property.

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:

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

The above example uses the empty binding syntax: {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.

Visual Basic
Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
C#
//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:

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:

Data binding diagram

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:

Visual Basic
    <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
C#
[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:

Data binding diagram

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.

This section contains the following subsections.

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:

Data binding ItemsControl diagram

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<(Of <(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 Binding Sources Overview.

Before implementing your own collection, consider using ObservableCollection<(Of <(T>)>) or one of the existing collection classes, such as List<(Of <(T>)>), Collection<(Of <(T>)>), and BindingList<(Of <(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.

This section contains the following subsections.

What Are Collection Views?

A collection view is a layer on top of a binding source collection that allows you to navigate and display the source collection based on sort, filter, and group queries, without having to change the underlying source collection itself. A collection view also maintains a pointer to the current item in the collection. 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. For example, consider the Data Binding Demo application shown in the What Is Data Binding? section. The application is implemented such that the ListBox binds to a view over the data collection instead of the data collection directly. The following example is extracted from the Data Binding Demo application. The CollectionViewSource class is the Extensible Application Markup Language (XAML) proxy of a class that inherits from CollectionView. In this particular example, the Source of the view is bound to the AuctionItems collection (of type ObservableCollection<(Of <(T>)>)) of the current application object.

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

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

The following table shows which view data types are created as the default collection view or by CollectionViewSource based on the source collection type.

Source collection type

Collection view type

Notes

IEnumerable

An internal type based on CollectionView

Cannot group items.

IList

ListCollectionView

Fastest.

IBindingList

BindingListCollectionView

Using a Default View

Specifying a collection view as a binding source is one way to create and use a collection view. WPF also creates a default collection view for every collection used as a binding source. If you bind directly to a collection, WPF binds to its default view. Note that this default view is shared by all bindings to the same collection, so a change made to a default view by one bound control or code (such as sorting or a change to the current item pointer, discussed later) is reflected in all other bindings to the same collection.

To get the default view, you use the GetDefaultView method. For an example, see How to: Get the Default View of a Data Collection.

Collection Views with ADO.NET DataTables

To improve performance, collection views for ADO.NET DataTable or DataView objects delegate sorting and filtering to the DataView. This causes sorting and filtering to be shared across all collection views of the data source. To enable each collection view to sort and filter independently, initialize each collection view with its own DataView object.

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 How to: 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:

C#
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:

C#
listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);

The ShowOnlyBargainsFilter event handler has the following implementation:

C#
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 How to: Filter Data in a View.

Grouping

Except for the internal class that views an IEnumerable collection, all collection views support the functionality of grouping, which allows the user to partition the collection in the collection view into logical groups. 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:

C#
// 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 How to: Group Items in a ListView That Implements a GridView.

Current Item Pointers

Views also support the notion of a current item. You can navigate through the objects in a collection view. As you navigate, you are moving an item pointer that allows you to retrieve the object that exists at that particular location in the collection. For an example, see How to: Navigate Through the Objects in a Data CollectionView.

Because WPF binds to a collection only by using a view (either a view you specify, or the collection's default view), all bindings to collections have a current item pointer. When binding to a view, the slash ("/") character in a Path value designates the current item of the view. In the following example, the data context is a collection view. The first line binds to the collection. The second line binds to the current item in the collection. The third line binds to the Description property of the current item in the collection.

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

The slash and property syntax can also be stacked to traverse a hierarchy of collections. The following example binds to the current item of a collection named Offices, which is a property of the current item of the source collection.

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

The current item pointer can be affected by any sorting or filtering that is applied to the collection. Sorting preserves the current item pointer on the last item selected, but the collection view is now restructured around it. (Perhaps the selected item was at the beginning of the list before, but now the selected item might be somewhere in the middle.) Filtering preserves the selected item if that selection remains in view after the filtering. Otherwise, the current item pointer is set to the first item of the filtered collection view.

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. In that application, the selection within the ListBox determines the content shown in the ContentControl. To put it in another way, when a ListBox item is selected, the ContentControl shows the details of the selected item.

You can implement the master-detail scenario simply by having two or more controls bound to the same view. The following example from the Data Binding Demo shows the markup of the ListBox and the ContentControl you see on the application UI in the What Is Data Binding? section:

XAML
<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 How to: Bind to a Collection and Display Information Based on Selection and How to: 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.

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

Data Binding Demo without Data Templates

As shown in the example in the previous section, both the ListBox control and the ContentControl are bound to the entire collection object (or more specifically, the view over the collection object) of AuctionItems. Without specific instructions of how to display the data collection, the ListBox is displaying a string representation of each object in the underlying collection and the ContentControl is displaying a string representation of the object it is bound to.

To solve that problem, the application defines DataTemplates. As shown in the example in the previous section, the ContentControl explicitly uses the detailsProductListingTemplate DataTemplate. The ListBox control implicitly uses the following DataTemplate when displaying the AuctionItem objects in the collection:

XAML
<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 Data Templating Overview.

This section contains the following subsections.

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. For example, the following example binds a TextBox to a property named StartPrice and adds a ExceptionValidationRule object to the Binding..::.ValidationRules property.

XAML
<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 object checks whether the value of a property is valid. WPF has the following two types of built-in ValidationRule objects:

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:

C#
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:

XAML
<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. One way to provide such feedback is to set the Validation..::.ErrorTemplate attached property to a custom ControlTemplate. As shown in the previous subsection, the StartDateEntryForm TextBox uses an ErrorTemplate called validationTemplate. The following example shows the definition of validationTemplate:

XAML
<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. The attached property Validation..::.HasError is true when one or more of the bindings on the properties of the bound element are in error.

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

Data binding validation error

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:

Data binding validation error

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

Validation usually occurs when the value of a target is transferred to the binding source property. This occurs on TwoWay and OneWayToSource bindings. To reiterate, what causes a source update depends on the value of the UpdateSourceTrigger property, as described in the What Triggers Source Updates section.

The following describes the validation process. Note that if a validation error or other type of error occurs at any time during this process, the process is halted.

  1. The binding engine checks if there are any custom ValidationRule objects defined whose ValidationStep is set to RawProposedValue for that Binding, in which case it calls the Validate method on each ValidationRule until one of them runs into an error or until all of them pass.

  2. The binding engine then calls the converter, if one exists.

  3. If the converter succeeds, the binding engine checks if there are any custom ValidationRule objects defined whose ValidationStep is set to ConvertedProposedValue for that Binding, in which case it calls the Validate method on each ValidationRule that has ValidationStep set to ConvertedProposedValue until one of them runs into an error or until all of them pass.

  4. The binding engine sets the source property.

  5. The binding engine checks if there are any custom ValidationRule objects defined whose ValidationStep is set to UpdatedValue for that Binding, in which case it calls the Validate method on each ValidationRule that has ValidationStep set to UpdatedValue until one of them runs into an error or until all of them pass. If a DataErrorValidationRule is associated with a binding and its ValidationStep is set to the default, UpdatedValue, the DataErrorValidationRule is checked at this point. This is also the point when bindings that have the ValidatesOnDataErrors set to true are checked.

  6. The binding engine checks if there are any custom ValidationRule objects defined whose ValidationStep is set to CommittedValue for that Binding, in which case it calls the Validate method on each ValidationRule that has ValidationStep set to CommittedValue until one of them runs into an error or until all of them pass.

If a ValidationRule does not pass at any time throughout this process, the binding engine creates a ValidationError object and adds it to the Validation..::.Errors collection of the bound element. Before the binding engine runs the ValidationRule objects at any given step, it removes any ValidationError that was added to the Validation..::.Errors attached property of the bound element during that step. For example, if a ValidationRule whose ValidationStep is set to UpdatedValue failed, the next time the validation process occurs, the binding engine removes that ValidationError immediately before it calls any ValidationRule that has ValidationStep set to UpdatedValue.

When Validation..::.Errors is not empty, the Validation..::.HasError attached property of the element is set to true. Also, if the NotifyOnValidationError property of the Binding is set to true, then the binding engine raises the Validation..::.Error attached event on the element.

Also note that a valid value transfer in either direction (target to source or source to target) clears the Validation..::.Errors attached property.

If the binding either has an ExceptionValidationRule associated with it, or had the ValidatesOnExceptions property is set to true and an exception is thrown when the binding engine sets the source, the binding engine checks to see if there is a UpdateSourceExceptionFilter. You have the option to use the UpdateSourceExceptionFilter callback to provide a custom handler for handling exceptions. If an UpdateSourceExceptionFilter is not specified on the Binding, the binding engine creates a ValidationError with the exception and adds it to the Validation..::.Errors collection of the bound element.

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

.NET Framework 4 - Windows Presentation Foundation
Cenni preliminari sull'associazione dati

L'associazione dati Windows Presentation Foundation (WPF) rappresenta per le applicazioni un modo semplice e coerente di presentare e interagire con i dati. È possibile associare gli elementi a numerose origini dati sotto forma di oggetti common language runtime (CLR) e XML. Gli oggetti ContentControlcome ad esempio Button e ItemsControl come ad esempio ListBox e ListView possiedono funzionalità incorporate che consentono un'applicazione di stili flessibile a singoli elementi di dati o a insiemi di elementi di dati. In cima ai dati è possibile generare visualizzazioni di ordinamento, filtraggio e raggruppamento.

La funzionalità dell'associazione dati in WPF presenta molti vantaggi rispetto ai modelli tradizionali, tra cui un'ampia gamma di proprietà che supportano implicitamente l'associazione dati, una rappresentazione dei dati mediante UI flessibile e una netta separazione tra regola business e UI.

In questo argomento vengono innanzitutto trattati i concetti fondamentali dell'associazione dati WPF, per poi analizzare l'utilizzo della classe Binding e altre funzionalità dell'associazione dati.

Nel presente argomento sono contenute le seguenti sezioni.

L'associazione dati è il processo mediante il quale viene stabilita una connessione tra l'UI dell'applicazione e la regola business. Se le impostazioni dell'associazione sono corrette e i dati forniscono le notifiche appropriate, quando il valore dei dati viene modificato, gli elementi associati ai dati riflettono automaticamente le modifiche apportate. Associazione dati significa anche che, se una rappresentazione esterna dei dati in un elemento viene modificata, i dati sottostanti possono essere automaticamente aggiornati per riflettere la modifica. Ad esempio, se l'utente modifica il valore in un elemento TextBox, il valore dei dati sottostanti viene automaticamente aggiornato per riflettere tale modifica.

Un utilizzo tipico dell'associazione dati consiste nell'inserimento dei dati di configurazione locali o del server in moduli o in altri controlli dell'UI. In WPF questo concetto viene ampliato per includere l'associazione di una vasta gamma di proprietà a varie origini dati. In WPF le proprietà di dipendenza degli elementi possono essere associate a oggetti CLR, inclusi oggetti ADO.NET o oggetti associati a servizi Web e proprietà Web, e a dati XML.

Per un esempio di associazione dati, osservare l'UI dell'applicazione seguente disponibile in Demo di associazione dati (la pagina potrebbe essere in inglese):

Schermata dell'esempio Data Binding

Si tratta dell'UI di un'applicazione in cui viene visualizzato un elenco di articoli venduti all'asta. Nell'applicazione vengono illustrate le seguenti funzionalità dell'associazione dati:

  • Il contenuto di ListBox è associato a un insieme di oggetti AuctionItem. Un oggetto AuctionItem possiede diverse proprietà, tra cui Description, StartPrice, StartDate, Category, SpecialFeatures e così via.

  • I dati, ossia gli oggetti AuctionItem, visualizzati in ListBox sono basati su modelli, per cui per ogni articolo vengono visualizzati la descrizione e il prezzo corrente. A tal proposito viene utilizzato DataTemplate. In aggiunta, l'aspetto di ogni articolo dipende dal valore di SpecialFeatures dell'oggetto AuctionItem visualizzato. Se il valore di SpecialFeatures in AuctionItem è Color, l'articolo avrà un bordo blu. Se il valore è Highlight, l'articolo avrà un bordo arancione e una stella. Nella sezione Modelli di dati vengono fornite informazioni sull'applicazione dei modelli di dati.

  • L'utente può raggruppare, filtrare o ordinare i dati utilizzando gli oggetti CheckBox forniti. Nella precedente immagine sono stati selezionati gli oggetti CheckBox "Group by category" e "Sort by category and date". I dati sono raggruppati in base alla categoria del prodotto e i nomi delle categorie sono riportati in ordine alfabetico. Benché non risulti evidente dall'immagine, gli elementi sono anche ordinati in base alla data di inizio all'interno di ogni categoria. A tal proposito viene utilizzata una visualizzazione di insieme. Nella sezione Associazione agli insiemi vengono trattate le visualizzazioni di insieme.

  • Quando l'utente seleziona un articolo, ContentControl visualizza i dettagli dell'articolo selezionato. Si parla in tal caso di scenario Master-Details. Nella sezione Scenario Master-Details vengono fornite informazioni su questo tipo di scenario di associazione.

  • Il tipo della proprietà StartDate è DateTime, il quale restituisce una data che include l'ora fino al millisecondo. In questa applicazione è stato utilizzato un convertitore personalizzato in modo tale da visualizzare una stringa della data più corta. Nella sezione Conversione dei dati vengono fornite informazioni sui convertitori.

Quando l'utente fa clic sul pulsante Add Product, viene visualizzato il seguente modulo:

Pagina Add Product Listing

L'utente può modificare i campi nel modulo, visualizzare in anteprima il prodotto utilizzando i riquadri di anteprima breve e anteprima dettagliata, quindi fare clic su submit per aggiungere il nuovo prodotto. Le funzionalità di raggruppamento, filtraggio e ordinamento esistenti verranno applicate alla nuova voce. In questo caso particolare, l'articolo immesso nell'immagine precedente verrà visualizzato come secondo articolo della categoria Computer.

Nell'immagine non viene illustrata la logica di convalida fornita nell'oggetto TextBox Start Date. Immettendo una data non valida, ovvero una data con formato non valido o una data appartenente al passato, l'utente riceverà una notifica mediante ToolTip e un punto esclamativo rosso verrà visualizzato accanto a TextBox. Nella sezione Convalida dei dati viene illustrato come creare la logica di convalida.

Prima di illustrare le diverse funzionalità dell'associazione dati delineate finora, nella sezione che segue verranno trattati i concetti fondamentali necessari a comprendere l'associazione dati WPF.

Nella presente sezione sono contenute le seguenti sottosezioni.

Indipendentemente dall'elemento associato e dalla natura dell'origine dati, ogni associazione segue sempre il modello illustrato nella figura seguente:

Diagramma di associazione dati di base

Come illustrato nella figura, l'associazione dati funge essenzialmente da ponte tra la destinazione dell'associazione e l'origine dell'associazione. Nella figura vengono illustrati i seguenti concetti fondamentali relativi all'associazione dati WPF:

  • In genere, ogni associazione possiede questi quattro componenti: un oggetto destinazione dell'associazione, una proprietà di destinazione, un'origine dell'associazione e un percorso del valore nell'origine dell'associazione da utilizzare. Se ad esempio si desidera associare il contenuto di un oggetto TextBox alla proprietà Name di un oggetto Employee, l'oggetto destinazione è TextBox, la proprietà di destinazione è Text, il valore da utilizzare è Name e l'oggetto origine è Employee.

  • La proprietà di destinazione deve essere una proprietà di dipendenza. Gran parte delle proprietà degli oggetti UIElement sono proprietà di dipendenza e gran parte delle proprietà di dipendenza, ad eccezione di quelle di sola lettura, supportano l'associazione dati per impostazione predefinita. Solo i tipi DependencyObject possono definire le proprietà di dipendenza e tutti gli oggetti UIElement derivano da DependencyObject.

  • Anche se non specificato nella figura, notare che l'oggetto origine di associazione non è necessariamente costituito da un oggetto CLR personalizzato. L'associazione dati WPF supporta dati nel formato oggetti CLR e XML. Per fornire alcuni esempi, l'origine dell'associazione può essere un oggetto UIElement, qualsiasi oggetto elenco, un oggetto CLR associato a dati ADO.NET o a servizi Web o un XmlNode contenente i dati XML desiderati. Per ulteriori informazioni, vedere Cenni preliminari sulle origini di associazione.

Come detto in altri argomenti concernenti software development kit (SDK), è importante ricordare che quando si stabilisce un'associazione, si associa una destinazione dell'associazione a un'origine dell'associazione. Ad esempio, nel caso in cui vengano visualizzati dati XML sottostanti in un oggetto ListBox mediante associazione dati, si associa ListBox ai dati XML.

Per stabilire un'associazione si utilizza l'oggetto Binding. Nella parte restante di questo argomento vengono illustrati molti dei concetti associati a Binding, nonché alcune proprietà e modalità di utilizzo dell'oggetto.

Direzione del flusso di dati

Come accennato in precedenza e come si evince dalla freccia nella figura precedente, il flusso di dati di un'associazione può andare dalla destinazione dell'associazione all'origine dell'associazione (ad esempio, il valore di origine viene modificato quando un utente modifica il valore di un oggetto TextBox) e/o dall'origine dell'associazione alla destinazione dell'associazione (ad esempio, il contenuto di TextBox viene aggiornato in seguito alle modifiche apportate nell'origine dell'associazione) se l'origine dell'associazione fornisce le notifiche appropriate.

È possibile fare in modo che l'applicazione consenta agli utenti di modificare i dati e propagarli all'oggetto origine. Oppure è possibile fare in modo che gli utenti non aggiornino i dati di origine. A tal proposito occorre impostare la proprietà Mode dell'oggetto Binding. Nella figura che segue vengono illustrati i diversi tipi di flusso di dati:

Flusso di dati dell'associazione dati
  • L'associazione OneWay fa sì che le modifiche apportate alla proprietà di origine comportino un aggiornamento automatico della proprietà di destinazione. Tuttavia, le modifiche apportate alla proprietà di destinazione non vengono propagate alla proprietà di origine. Questo tipo di associazione è adatto nel caso in cui il controllo associato sia implicitamente di sola lettura. Può accadere ad esempio che si effettui un'associazione a un'origine quale un controllo Stock Ticker oppure che la proprietà di destinazione non possieda un'interfaccia di controllo tramite la quale apportare modifiche, come nel caso di un colore di sfondo con associazione a dati di una tabella. Se non vi è alcuna necessità di controllare le modifiche della proprietà di destinazione, è consigliabile utilizzare la modalità di associazione OneWay, in quanto l'associazione TwoWay implicherebbe operazioni inutili.

  • L'associazione TwoWay fa sì che le modifiche apportate alla proprietà di origine o alla proprietà di destinazione comportino un aggiornamento automatico dell'altra proprietà. Questo tipo di associazione è adatto ai moduli modificabili o ad altri scenari di UI completamente interattiva. La maggior parte delle proprietà utilizza per impostazione predefinita l'associazione OneWay. Tuttavia, alcune proprietà di dipendenza, in genere proprietà di controlli modificabili dall'utente quali ad esempio la proprietà Text di TextBox e la proprietà IsChecked di CheckBox, utilizzano per impostazione predefinita l'associazione TwoWay. Per stabilire a livello di codice se, per impostazione predefinita, una proprietà di dipendenza utilizza l'associazione unidirezionale o bidirezionale, è possibile ottenere i metadati della proprietà utilizzando GetMetadata, quindi controllare il valore booleano della proprietà BindsTwoWayByDefault.

  • OneWayToSource è la modalità di associazione inversa rispetto a OneWay. Con questa modalità, la proprietà di origine viene aggiornata in seguito a una modifica della proprietà di destinazione. Può essere utilizzata, ad esempio, nel caso in cui si debba semplicemente rivalutare il valore di origine dall'UI.

  • Un altro tipo di associazione non illustrato nella figura è l'associazione OneTime, la quale fa sì che la proprietà di origine inizializzi la proprietà di destinazione ma non prevede la propagazione delle successive modifiche. Ciò significa che se il contesto dati subisce una modifica o l'oggetto nel contesto dati viene modificato, la modifica non si riflette nella proprietà di destinazione. Questo tipo di associazione è adatto nel caso in cui i dati utilizzati siano effettivamente statici oppure richiedano l'utilizzo di uno snapshot dello stato corrente. È utile anche nel caso in cui si desideri inizializzare la proprietà di destinazione con un valore derivante da una proprietà di origine senza conoscere in anticipo il contesto dati. Si tratta essenzialmente di una forma più semplice di associazione OneWay, in grado di garantire prestazioni migliori nei casi in cui il valore di origine non viene modificato.

Per rilevare le modifiche apportate all'origine, nel caso delle associazioni OneWay e TwoWay, l'origine deve implementare un meccanismo appropriato di notifica delle modifiche alle proprietà, quale ad esempio INotifyPropertyChanged. Per un esempio di implementazione di INotifyPropertyChanged, vedere Procedura: implementare notifiche di modifiche alle proprietà.

La pagina delle proprietà Mode fornisce ulteriori informazioni sulle modalità di associazione e un esempio in cui viene illustrato come specificare la direzione di un'associazione.

Eventi che generano gli aggiornamenti dell'origine

Le associazioni di tipo TwoWay o OneWayToSource attendono le modifiche apportate alla proprietà di destinazione e le propagano all'origine. Questo processo è noto come aggiornamento dell'origine. Può accadere ad esempio che si modifichi il testo di un oggetto TextBox per modificare il valore di origine sottostante. Come descritto nella sezione precedente, la direzione del flusso di dati è determinata dal valore della proprietà Mode dell'associazione.

Ma il valore di origine viene aggiornato durante la modifica del testo o una volta terminata la modifica, quando si sposta il puntatore del mouse da TextBox? La proprietà UpdateSourceTrigger dell'associazione determina l'evento che genera l'aggiornamento dell'origine. Nella figura che segue, i puntini delle frecce rivolte verso destra illustrano il ruolo della proprietà UpdateSourceTrigger:

Diagramma UpdateSourceTrigger

Se il valore di UpdateSourceTrigger è PropertyChanged, il valore indicato dalla freccia a destra dell'associazione TwoWay o OneWayToSource viene aggiornato non appena si modifica la proprietà di destinazione. Se invece il valore di UpdateSourceTrigger è LostFocus, il valore indicato dalla freccia viene aggiornato con il nuovo valore soltanto quando la proprietà di destinazione perde lo stato attivo.

Analogamente alla proprietà Mode, diverse proprietà di dipendenza possiedono valori di UpdateSourceTrigger predefiniti diversi. Il valore predefinito per la maggior parte delle proprietà di dipendenza è PropertyChanged, mentre il valore predefinito per la proprietà Text è LostFocus. Ciò significa che gli aggiornamenti dell'origine avvengono in genere quando si modifica la proprietà di destinazione. Questo comportamento è appropriato nel caso di oggetti CheckBox e di altri controlli semplici. Nel caso dei campi di testo, tuttavia, l'esecuzione di un aggiornamento a ogni pressione di tasto può comportare un calo di prestazioni nonché negare all'utente la possibilità di tornare indietro e correggere eventuali errori di digitazione prima di confermare il nuovo valore. Per questo motivo il valore predefinito della proprietà Text è LostFocus anziché PropertyChanged.

Vedere la pagina delle proprietà UpdateSourceTrigger per informazioni su come individuare il valore di UpdateSourceTrigger predefinito di una proprietà di dipendenza.

Nella tabella che segue viene fornito uno scenario esemplificativo per ogni valore di UpdateSourceTrigger utilizzando TextBox come esempio:

Valore UpdateSourceTrigger

Aggiornamento del valore di origine

Scenario di esempio per TextBox

LostFocus (impostazione predefinita per TextBox..::.Text)

Quando il controllo TextBox perde lo stato attivo

Un oggetto TextBox associato alla logica di convalida (vedere la sezione Convalida dei dati)

PropertyChanged

Durante la digitazione in TextBox

Controlli TextBox nella finestra di una chat

Explicit

Quando l'applicazione chiama UpdateSource

Controlli TextBox in un modulo modificabile (aggiorna i valori di origine solo quando l'utente fa clic sul pulsante di invio)

Per un esempio, vedere Procedura: controllare il momento in cui il database di origine viene aggiornato dal testo di TextBox.

Nella presente sezione sono contenute le seguenti sottosezioni.

Per riepilogare alcuni dei concetti illustrati nelle sezioni precedenti, un'associazione viene stabilita utilizzando l'oggetto Binding e ogni associazione è costituita in genere da quattro componenti: la destinazione dell'associazione, la proprietà di destinazione, l'origine dell'associazione e un percorso del valore di origine da utilizzare. In questa sezione viene illustrato come impostare un'associazione.

Si consideri il seguente esempio, nel quale l'oggetto origine dell'associazione è una classe denominata MyData definita nello spazio dei nomi SDKSample. A scopo dimostrativo, la classe MyData possiede una proprietà stringa denominata ColorName il cui valore è impostato su "Red". In questo esempio viene quindi generato un pulsante con uno sfondo rosso.

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

Per maggiori dettagli sulla sintassi della dichiarazione di associazione e alcuni esempi di impostazione di un'associazione nel codice, vedere Cenni preliminari sulle dichiarazioni di associazione.

Applicando questo esempio allo schema di base, risulterà una figura analoga alla seguente. Si tratta di un'associazione OneWay in quanto, per impostazione predefinita, la proprietà Background supporta questo tipo di associazione.

Diagramma di associazione dati

Ci si potrebbe chiedere come sia possibile tale associazione, dal momento che la proprietà ColorName è di tipo stringa mentre la proprietà Background è di tipo Brush. In questo caso viene utilizzata la conversione di tipi predefinita, illustrata nella sezione Conversione dei dati.

Specificazione dell'origine dell'associazione

Nell'esempio precedente, l'origine dell'associazione viene specificata impostando la proprietà DataContext nell'elemento DockPanel. Button eredita quindi il valore di DataContext da DockPanel, il quale costituisce l'elemento padre. Come già detto, l'oggetto origine dell'associazione è uno dei quattro componenti necessari di un'associazione. Di conseguenza, se non si specifica l'oggetto origine dell'associazione, questa non viene creata.

Esistono diversi modi per specificare l'oggetto origine dell'associazione. L'utilizzo della proprietà DataContext in un elemento padre è utile in caso di associazione di più proprietà alla stessa origine. In alcuni casi, tuttavia, è preferibile specificare l'origine dell'associazione in singole dichiarazioni di associazione. Nel caso dell'esempio precedente, anziché utilizzare la proprietà DataContext, è possibile specificare l'origine dell'associazione impostando la proprietà Source direttamente nella dichiarazione di associazione del pulsante, come avviene nell'esempio che segue:

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

Oltre a impostare la proprietà DataContext direttamente in un elemento, ereditare il valore di DataContext da un predecessore (si veda il pulsante nel primo esempio) e specificare in modo esplicito l'origine dell'associazione impostando la proprietà Source in Binding (si veda il pulsante nell'ultimo esempio), è anche possibile utilizzare la proprietà ElementName o la proprietà RelativeSource per specificare l'origine dell'associazione. La proprietà ElementName è utile in caso di associazione ad altri elementi nell'applicazione, ad esempio in caso di utilizzo di un dispositivo di scorrimento per regolare la larghezza di un pulsante. La proprietà RelativeSource è utile nel caso di un'associazione specificata in un oggetto ControlTemplate o Style. Per ulteriori informazioni, vedere Procedura: specificare l'origine di associazione.

Specificazione del percorso del valore

Se l'origine dell'associazione è un oggetto, si utilizza la proprietà Path per specificare il valore da utilizzare per l'associazione. In caso di associazione a dati XML, utilizzare la proprietà XPath per specificare il valore. In alcuni casi è possibile utilizzare la proprietà Path anche se i dati sono in formato XML. Ad esempio, per accedere alla proprietà Name di un XmlNode restituito in seguito a una query XPath, è necessario utilizzare la proprietà Path oltre alla proprietà XPath.

Per informazioni sulla sintassi e alcuni esempi, vedere le pagine delle proprietà Path e XPath.

Benché sia stato sottolineato che la proprietà Path del valore da utilizzare è uno dei quattro componenti necessari di un'associazione, negli scenari di associazione a un intero oggetto il valore da utilizzare corrisponde all'oggetto origine dell'associazione. In tali casi è possibile non specificare Path. Si consideri l'esempio seguente:

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

Nell'esempio viene utilizzata la sintassi di associazione vuota: {Binding}. In questo caso, ListBox eredita DataContext da un elemento DockPanel padre, assente nell'esempio. Quando il percorso non viene specificato, per impostazione predefinita si esegue un'associazione all'intero oggetto. In altre parole, in questo esempio il percorso è stato tralasciato poiché la proprietà ItemsSource viene associata all'intero oggetto. Per informazioni più dettagliate, vedere la sezione Associazione agli insiemi.

Diversamente dall'associazione a un insieme, questo scenario è utile anche in caso di associazione a un intero oggetto anziché a un'unica proprietà di un oggetto. Si consideri ad esempio un oggetto origine di tipo stringa con un'associazione alla stringa stessa. Un altro scenario comune riguarda l'associazione di un elemento a un oggetto con diverse proprietà.

Alcuni scenari richiedono l'applicazione di una logica personalizzata affinché i dati siano significativi per la proprietà di destinazione associata. Tale logica potrebbe essere un convertitore personalizzato, in assenza della conversione di tipi predefinita. Per informazioni sui convertitori, vedere Conversione dei dati.

Binding e BindingExpression

Prima di analizzare altre funzionalità e modalità di utilizzo dell'associazione dati, è opportuno introdurre la classe BindingExpression. Come si è visto nelle sezioni precedenti, la classe Binding è la classe di alto livello per la dichiarazione di un'associazione e fornisce diverse proprietà che consentono di specificare le caratteristiche dell'associazione. Una classe correlata, BindingExpression, è l'oggetto sottostante che gestisce la connessione tra origine e destinazione. Un'associazione contiene tutte le informazioni condivisibili tra diverse espressioni di associazione. BindingExpression è un'espressione di istanza non condivisibile e contiene tutte le informazioni sull'istanza di Binding.

Si consideri l'esempio che segue, nel quale myDataObject è un'istanza della classe MyData, myBinding è l'oggetto Binding di origine e la classe MyData è una classe definita contenente una proprietà stringa denominata MyDataProperty. Nell'esempio viene associato il contenuto di testo di mytext, un'istanza di TextBlock, a MyDataProperty.

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

È possibile utilizzare lo stesso oggetto myBinding per creare altre associazioni. Ad esempio, è possibile utilizzare l'oggetto myBinding per associare il contenuto di testo di una casella di controllo a MyDataProperty. In un simile scenario si avranno due istanze di BindingExpression che condividono l'oggetto myBinding.

Un oggetto BindingExpression può essere ottenuto tramite il valore restituito di una chiamata a GetBindingExpression in un oggetto con associazione a dati. Negli argomenti che seguono vengono illustrate alcune modalità di utilizzo della classe BindingExpression:

Nell'esempio precedente, il pulsante è rosso perché la proprietà Background è associata a una proprietà stringa con valore "Red". Questa associazione funziona grazie alla presenza di un convertitore di tipi nel tipo Brush, il quale converte il valore stringa in un oggetto Brush.

Aggiungendo queste informazioni alla figura della sezione Creazione di un'associazione, si otterrà uno schema analogo al seguente:

Diagramma di associazione dati

Cosa accade però se, anziché avere una proprietà di tipo stringa, l'oggetto origine dell'associazione possiede una proprietà Color di tipo Color? In tal caso, per fare in modo che l'associazione funzioni, occorre innanzitutto convertire il valore della proprietà Color in un valore accettabile dalla proprietà Background. Occorre quindi creare un convertitore personalizzato implementando l'interfaccia IValueConverter, come avviene nell'esempio seguente:

Visual Basic
    <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
C#
[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;
    }
}

Nella pagina di riferimento relativa a IValueConverter vengono fornite ulteriori informazioni.

Ora il convertitore personalizzato viene utilizzato al posto della conversione predefinita e lo schema appare come segue:

Diagramma di associazione dati

Come già detto, le conversioni predefinite possono essere disponibili o meno a seconda dei convertitori presenti nel tipo a cui si esegue l'associazione. Questo comportamento dipenderà dai convertitori di tipi disponibili nella destinazione. In caso di dubbio, creare un convertitore personalizzato.

Di seguito vengono riportati alcuni scenari tipici in cui può essere opportuno implementare un convertitore di dati:

  • I dati devono essere visualizzati in modo diverso, a seconda delle impostazioni cultura. Ad esempio, è possibile implementare un convertitore di valuta o un convertitore di data/ora nel calendario in base ai valori o agli standard utilizzati in particolari impostazioni cultura.

  • I dati utilizzati non devono necessariamente modificare il valore di testo di una proprietà quanto piuttosto altri valori, ad esempio l'origine di un'immagine oppure il colore o lo stile del testo visualizzato. I convertitori possono essere utilizzati in questo caso per convertire l'associazione di una proprietà considerata poco appropriata, ad esempio l'associazione di un campo di testo alla proprietà Background della cella di una tabella.

  • Più controlli o più proprietà dei controlli sono associati agli stessi dati. In questo caso, l'associazione primaria potrebbe semplicemente visualizzare il testo, mentre le altre associazioni gestiscono problemi di visualizzazione specifici, utilizzando comunque la stessa associazione come informazione di origine.

  • Finora non è stato ancora illustrato MultiBinding, ovvero il caso di una proprietà di destinazione con un insieme di associazioni. Nel caso di MultiBinding si utilizza un oggetto IMultiValueConverter personalizzato per produrre un valore finale partendo dai valori delle associazioni. È possibile ad esempio calcolare il colore dai valori rosso, blu e verde, ovvero valori che possono provenire da oggetti origine dell'associazione identici o differenti. Per esempi e informazioni, vedere la pagina relativa alla classe MultiBinding.

Nella presente sezione sono contenute le seguenti sottosezioni.

Un oggetto origine dell'associazione può essere considerato come un unico oggetto le cui proprietà contengono dati oppure come un insieme di dati di oggetti polimorfici che spesso vengono raggruppati. Un esempio può essere il risultato di una query in un database. Finora è stata illustrata soltanto l'associazione a singoli oggetti, tuttavia l'associazione a un insieme di dati è uno scenario comune. È possibile ad esempio utilizzare un oggetto ItemsControl quale ListBox, ListView o TreeView per visualizzare una raccolta dati, come avviene nell'applicazione illustrata nella sezione Definizione di associazione dati

Anche in questo caso è possibile applicare lo schema di base. In caso di associazione di un oggetto ItemsControl a un insieme, lo schema appare come segue:

Diagramma ItemsControl di associazione dati

Come illustrato in questo schema, l'associazione di ItemsControl a un oggetto insieme richiede l'utilizzo della proprietà ItemsSource. La proprietà ItemsSource può essere considerata il contenuto di ItemsControl. L'associazione dell'esempio è di tipo OneWay, essendo questo il tipo supportato per impostazione predefinita dalla proprietà ItemsSource.

Procedura per l'implementazione degli insiemi

È possibile eseguire enumerazioni in qualsiasi insieme che implementa l'interfaccia IEnumerable. Tuttavia, per impostare associazioni dinamiche in modo tale che l'UI venga automaticamente aggiornata in seguito a operazioni di inserimento o eliminazione nell'insieme, è necessario che questo implementi l'interfaccia INotifyCollectionChanged. Questa interfaccia espone un evento che deve essere generato a ogni modifica dell'insieme sottostante.

In WPF viene fornita la classe ObservableCollection<(Of <(T>)>), un'implementazione incorporata di un insieme di dati che espone l'interfaccia INotifyCollectionChanged. Per supportare pienamente il trasferimento dei valori dei dati dagli oggetti origine alle destinazioni, ogni oggetto nell'insieme che supporta proprietà associabili deve implementare anche l'interfaccia INotifyPropertyChanged. Per ulteriori informazioni, vedere Cenni preliminari sulle origini di associazione.

Prima di implementare un insieme personalizzato, considerare la possibilità di utilizzare ObservableCollection<(Of <(T>)>) o una delle classi di insiemi esistenti, ad esempio List<(Of <(T>)>), Collection<(Of <(T>)>) e BindingList<(Of <(T>)>). Se si desidera implementare un insieme personalizzato in uno scenario avanzato, è opportuno utilizzare IList, il quale fornisce un insieme non generico di oggetti accessibili singolarmente tramite indice e garantisce prestazioni ottimali.

Visualizzazioni di insiemi

Una volta associato ItemsControl a un insieme di dati, è possibile ordinare, filtrare o raggruppare tali dati. A tale scopo si utilizzano le visualizzazioni di insiemi, ovvero classi che implementano l'interfaccia ICollectionView.

Nella presente sezione sono contenute le seguenti sottosezioni.

Definizione di visualizzazione di insieme

Una visualizzazione di insieme rappresenta il livello superiore di un insieme di origine di associazione che consente di spostarsi e visualizzare l'insieme di origine in base a query di ordinamento, filtro e raggruppamento, il tutto senza modificare l'insieme di origine sottostante. Una visualizzazione di insieme mantiene inoltre un puntatore all'elemento corrente nell'insieme. Se l'insieme di origine implementa l'interfaccia INotifyCollectionChanged, le modifiche generate dall'evento CollectionChanged vengono propagate alle visualizzazioni.

Poiché le visualizzazioni non modificano gli insiemi di origine sottostanti, ogni insieme di origine può disporre di più visualizzazioni associate. Si consideri ad esempio un insieme di oggetti Task. Grazie alle visualizzazioni è possibile visualizzare gli stessi dati in modi diversi. Ad esempio, è possibile visualizzare le attività ordinate in base alla priorità nella parte sinistra della pagina e, contemporaneamente nella parte destra, le stesse attività raggruppate in base all'area.

Procedura per la creazione di una visualizzazione

Per creare e utilizzare una visualizzazione, è possibile creare direttamente un'istanza dell'oggetto visualizzazione e utilizzare tale istanza come origine dell'associazione. Ad esempio, considerare l'applicazione Demo di associazione dati illustrata nella sezione Definizione di associazione dati. L'applicazione viene implementata in modo tale che ListBox venga associato a una visualizzazione dell'insieme di dati anziché direttamente all'insieme in questione. L'esempio seguente è stato estratto dall'applicazione Demo di associazione dati (la pagina potrebbe essere in inglese). La classe CollectionViewSource è il proxy Extensible Application Markup Language (XAML) di una classe che eredita da CollectionView. In questo particolare esempio, l'oggetto Source della visualizzazione è associato all'insieme AuctionItems, di tipo ObservableCollection<(Of <(T>)>), dell'oggetto applicazione corrente.

XAML
<Window.Resources>


...


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


...


</Window.Resources>

La risorsa listingDataView funge quindi da origine dell'associazione per gli elementi nell'applicazione, quale ad esempio ListBox:

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


...


</ListBox>

Per creare un'altra visualizzazione per lo stesso insieme, è possibile creare una nuova istanza di CollectionViewSource e attribuirle un nome x:Key diverso.

Nella tabella seguente vengono illustrati i tipi di dati della visualizzazione creati come visualizzazione di insieme predefinita o in base a CollectionViewSource a seconda del tipo di insieme di origine.

Tipo di insieme di origine

Tipo di visualizzazione di insieme

Note

IEnumerable

Tipo interno basato su CollectionView

Impossibile raggruppare gli elementi.

IList

ListCollectionView

Più veloce.

IBindingList

BindingListCollectionView

Utilizzo di una visualizzazione predefinita

Specificare una visualizzazione di insieme come origine di associazione rappresenta un modo per creare e utilizzare una visualizzazione di insieme. WPF crea inoltre una visualizzazione di insieme predefinita per ogni insieme utilizzato come origine di associazione. Se si esegue l'associazione direttamente a un insieme, WPF esegue l'associazione alla relativa visualizzazione predefinita. Questa visualizzazione predefinita è condivisa da tutte le associazioni allo stesso insieme, pertanto una modifica apportata a una visualizzazione predefinita da un controllo associato o dal codice, ad esempio l'ordinamento o una modifica al puntatore dell'elemento corrente illustrati più avanti, viene riflessa in tutte le altre associazioni allo stesso insieme.

Per ottenere la visualizzazione predefinita, utilizzare il metodo GetDefaultView. Per un esempio, vedere Procedura: ottenere la visualizzazione predefinita di una raccolta dati.

Visualizzazioni di insieme con ADO.NET DataTable

Per migliorare le prestazioni, le visualizzazioni di insieme per gli oggetti ADO.NET DataTable o DataView delegano l'ordinamento e il filtraggio all'oggetto DataView. In tal modo, l'ordinamento e il filtraggio vengono condivisi in tutte le visualizzazioni di insieme dell'origine dati. Per consentire a ogni visualizzazione di insieme di ordinare e filtrare in modo indipendente, inizializzare ogni visualizzazione di insieme con il relativo oggetto DataView personalizzato.

Ordinamento

Come detto in precedenza, le visualizzazioni possono applicare un ordinamento a un insieme. I dati presenti nell'insieme sottostante possono avere o non avere un ordine intrinseco. La visualizzazione dell'insieme consente di imporre un ordine o di modificare l'ordine predefinito in base a criteri di confronto forniti dall'utente. Trattandosi di una visualizzazione dei dati basata su client, uno scenario comune prevede che l'utente possa ordinare le colonne di dati tabulari in base al valore al quale corrisponde la colonna. Grazie alle visualizzazioni, è possibile applicare questo ordinamento gestito dall'utente senza apportare alcuna modifica all'insieme sottostante né ripetere la query per il contenuto dell'insieme. Per un esempio, vedere Procedura: ordinare una colonna GridView quando si fa clic su un'intestazione.

Nell'esempio seguente viene illustrata la logica di ordinamento dell'oggetto CheckBox "Sort by category and date" dell'UI dell'applicazione inclusa nella sezione Definizione di associazione dati:

C#
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));
}

Filtraggio

Le visualizzazioni possono anche applicare un filtro a un insieme. In altre parole, anche se un insieme può includere un elemento, una particolare visualizzazione è progettata per visualizzare solo un determinato sottoinsieme di un insieme completo. I dati possono essere filtrati in base a una condizione. Ad esempio, come accade per l'applicazione nella sezione Definizione di associazione dati, l'oggetto CheckBox "Show only bargains" contiene una logica che prevede l'esclusione degli articoli con un costo pari o superiore a $ 25. Il codice seguente viene eseguito per impostare ShowOnlyBargainsFilter come gestore dell'evento Filter quando si seleziona l'oggetto CheckBox in questione:

C#
listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);

L'implementazione del gestore eventi ShowOnlyBargainsFilter è la seguente:

C#
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;
        }
    }
}

Se si utilizza direttamente una delle classi CollectionView anziché CollectionViewSource, occorre utilizzare la proprietà Filter per specificare un callback. Per un esempio, vedere Procedura: filtrare i dati in una visualizzazione.

Raggruppamento

A eccezione della classe interna che visualizza un insieme IEnumerable, tutte le visualizzazioni di insieme supportano la funzionalità di raggruppamento, che consente all'utente di suddividere l'insieme contenuto in una data visualizzazione in gruppi logici. Se l'utente fornisce un elenco di gruppi, questi saranno espliciti; se invece i gruppi vengono generati in modo dinamico in base ai dati, si avranno gruppi impliciti.

Nell'esempio che segue viene illustrata la logica dell'oggetto CheckBox "Group by category":

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

Per un altro esempio di raggruppamento, vedere Procedura: raggruppare gli elementi di un controllo ListView che implementa una GridView.

Puntatori dell'elemento corrente

Le visualizzazioni supportano anche la nozione di elemento corrente. In una visualizzazione di insieme è possibile spostarsi da un oggetto all'altro. Durante lo spostamento viene spostato un puntatore dell'elemento che consente di recuperare l'oggetto presente in un particolare percorso nell'insieme. Per un esempio, vedere Procedura: navigare tra gli oggetti nella visualizzazione di una raccolta dati.

Poiché WPF esegue l'associazione a un insieme solo tramite una visualizzazione, che può essere sia una visualizzazione specificata dall'utente che la visualizzazione predefinita dell'insieme, tutte le associazioni agli insiemi contengono un puntatore dell'elemento corrente. Quando si esegue l'associazione a una visualizzazione, il carattere barra ("/") in un valore Path definisce l'elemento corrente della visualizzazione. Nell'esempio seguente il contesto dati è una visualizzazione di insieme. La prima riga viene associata all'insieme. La seconda riga viene associata all'elemento corrente dell'insieme. La terza riga viene associata alla proprietà Description dell'elemento corrente nell'insieme.

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

La barra e la sintassi della proprietà possono inoltre essere sovrapposte per scorrere una gerarchia di insiemi. Nell'esempio seguente viene eseguita l'associazione all'elemento corrente di un insieme denominato Offices, che è una proprietà dell'elemento corrente dell'insieme di origine.

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

Il puntatore dell'elemento corrente può essere influenzato da un'operazione di ordinamento o filtraggio applicata all'insieme. L'ordinamento mantiene il puntatore dell'elemento corrente sull'ultimo elemento selezionato, ma la visualizzazione di insieme viene ristrutturata in base a tale elemento. Se in precedenza l'elemento selezionato si trovava all'inizio dell'elenco, ora è possibile che si trovi in posizione centrale. Il filtraggio mantiene l'elemento selezionato se la selezione rimane nella visualizzazione dopo il filtraggio. In caso contrario, il puntatore dell'elemento corrente viene impostato sul primo elemento della visualizzazione di insieme filtrata.

Scenario di associazione Master-Details

La nozione di elemento corrente è utile non solo per lo spostamento sugli elementi in un insieme, bensì anche per lo scenario di associazione Master-Details. Si consideri nuovamente l'UI della sezione Definizione di associazione dati. In tale applicazione, la selezione all'interno di ListBox determina il contenuto visualizzato in ContentControl. In altre parole, quando si seleziona un elemento di ListBox, ContentControl visualizza i dettagli dell'elemento selezionato.

Per implementare lo scenario Master-Details, occorre semplicemente avere due o più controlli associati alla stessa visualizzazione. Nell'esempio seguente estratto da Demo di associazione dati viene illustrato il markup degli oggetti ListBox e ContentControl visualizzati nell'UI dell'applicazione illustrata nella sezione Definizione di associazione dati:

XAML
<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"/>

Entrambi i controlli sono associati alla stessa origine, ovvero la risorsa statica listingDataView. Vedere in proposito la definizione di questa risorsa nella sezione Procedura per la creazione di una visualizzazione. Tutto ciò è possibile in quanto, associando un oggetto Singleton (ContentControl in questo caso) a una visualizzazione di insieme, tale oggetto viene automaticamente associato all'oggetto CurrentItem della visualizzazione. Gli oggetti CollectionViewSource sincronizzano automaticamente validità e selezione. Se l'elenco non è associato a un oggetto CollectionViewSource come avviene in questo esempio, sarà necessario impostare la proprietà IsSynchronizedWithCurrentItem su true per fare in modo che la procedura funzioni.

Per altri esempi, vedere Procedura: eseguire l'associazione di un insieme e visualizzare informazioni in base alla selezione effettuata e Procedura: utilizzare il modello Master-Details con dati gerarchici.

Come si può notare, nell'esempio precedente viene utilizzato un modello. I dati non verrebbero infatti visualizzati come desiderato senza l'utilizzo dei modelli, per la precisione quello utilizzato in modo esplicito da ContentControl e quello utilizzato in modo implicito da ListBox. Nella sezione che segue vengono illustrati i modelli di dati.

Senza l'utilizzo di modelli di dati, l'UI dell'applicazione illustrata nella sezione Definizione di associazione dati avrebbe un aspetto simile al seguente:

Data Binding Demo senza Data Templates

Come illustrato nell'esempio incluso nella sezione precedente, sia il controllo ListBox che ContentControl vengono associati all'intero oggetto Collection (o più specificatamente alla vista sull'oggetto Collection) di AuctionItem. Senza istruzioni specifiche per la visualizzazione dell'insieme di dati, l'oggetto ListBox determinerebbe la visualizzazione di una rappresentazione di stringa di ogni oggetto nell'insieme sottostante e l'oggetto ContentControl determinerebbe la visualizzazione di una rappresentazione di stringa dell'oggetto al quale è associato.

Per risolvere il problema, nell'applicazione vengono definiti oggetti DataTemplate. Come illustrato nell'esempio incluso nella sezione precedente, ContentControl utilizza in modo esplicito l'oggetto DataTemplate detailsProductListingTemplate. Il controllo ListBox utilizza in modo implicito il seguente oggetto DataTemplate durante la visualizzazione degli oggetti AuctionItem nell'insieme:

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

Grazie all'utilizzo di questi due oggetti DataTemplate, l'interfaccia utente risultante sarà quella illustrata nella sezione Definizione di associazione dati. Come si evince dalla schermata, oltre a consentire di posizionare i dati all'interno dei controlli, gli oggetti DataTemplate permettono di conferire ai dati un aspetto visivo gradevole. Ad esempio, nel precedente DataTemplate vengono utilizzati oggetti DataTrigger che consentono di visualizzare gli oggetti AuctionItem per i quali SpecialFeatures è impostato su HighLight con un bordo arancione e una stella.

Per ulteriori informazioni sui modelli di dati, vedere Cenni preliminari sui modelli di dati.

Nella presente sezione sono contenute le seguenti sottosezioni.

Gran parte delle applicazioni che accettano input dell'utente devono avere una logica di convalida per garantire che l'utente immetta le informazioni previste. I controlli di convalida possono basarsi sul tipo, l'intervallo, il formato o altri requisiti specifici dell'applicazione. In questa sezione viene illustrato il funzionamento della convalida dei dati in WPF.

Associazione di regole di convalida a un'associazione

Il modello di associazione dati WPF consente di associare ValidationRules all'oggetto Binding. Nell'esempio seguente viene ad esempio associato un oggetto TextBox a una proprietà denominata StartPrice e viene aggiunto un oggetto ExceptionValidationRule alla proprietà Binding..::.ValidationRules.

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

Un oggetto ValidationRule controlla se il valore di una proprietà è valido. In WPF sono disponibili i due tipi di oggetti ValidationRule incorporati seguenti:

È anche possibile creare una regola di convalida personalizzata mediante derivazione dalla classe ValidationRule e implementazione del metodo Validate. Di seguito viene illustrata la regola utilizzata dall'oggetto TextBox "Start Date" contenuto in Add Product Listing, secondo l'esempio riportato nella sezione Definizione di associazione dati:

C#
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;
        }
    }
}

L'oggetto TextBox StartDateEntryForm utilizza FutureDateRule, come illustrato nell'esempio che segue:

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

Poiché il valore di UpdateSourceTrigger è PropertyChanged, il motore di associazione aggiorna il valore di origine a ogni pressione di tasto, il che significa che controlla anche ogni regola nell'insieme ValidationRules a ogni pressione di tasto. L'argomento verrà ulteriormente trattato nella sezione Processo di convalida.

Visualizzazione di un feedback

Se l'utente immette un valore non valido, è possibile fornire un feedback relativo all'errore nell'UI dell'applicazione. Un modo per fornire tale feedback consiste nell'impostare la proprietà associata Validation..::.ErrorTemplate su un oggetto ControlTemplate personalizzato. Come illustrato nella sottosezione precedente, l'oggetto TextBox StartDateEntryForm utilizza un oggetto ErrorTemplate definito validationTemplate. Nell'esempio seguente viene illustrata la definizione di validationTemplate:

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

L'elemento AdornedElementPlaceholder specifica il punto in cui deve essere posizionato il controllo decorato.

In aggiunta, è anche possibile utilizzare un oggetto ToolTip per visualizzare il messaggio di errore. Gli oggetti TextBox StartDateEntryForm e StartPriceEntryForm utilizzano entrambi lo stile textStyleTextBox, il quale crea un oggetto ToolTip in cui viene visualizzato il messaggio di errore. Nell'esempio seguente viene illustrata la definizione di textStyleTextBox. Il valore della proprietà associata Validation..::.HasError è true quando una o più associazioni nelle proprietà dell'elemento associato sono in errore.

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

Utilizzando ErrorTemplate personalizzato e ToolTip, in caso di errore di convalida l'oggetto TextBox StartDateEntryForm appare come segue:

Errore di convalida dell'associazione dati

Se Binding possiede regole di convalida associate ma non viene specificato alcun oggetto ErrorTemplate nel controllo associato, verrà utilizzato un oggetto ErrorTemplate predefinito per avvisare gli utenti in caso di errore di convalida. L'oggetto ErrorTemplate predefinito è un modello di controllo che definisce un bordo rosso nel livello dello strumento decorativo visuale. Utilizzando ErrorTemplate predefinito e ToolTip, in caso di errore di convalida l'UI dell'oggetto TextBox StartPriceEntryForm appare come segue:

Errore di convalida dell'associazione dati

Per un esempio di come fornire una logica di convalida per tutti i controlli in una finestra di dialogo, vedere la sezione Finestre di dialogo personalizzate contenuta in Cenni preliminari sulle finestre di dialogo.

Processo di convalida

La convalida avviene solitamente quando si trasferisce un valore di destinazione alla proprietà di origine dell'associazione. Ciò si verifica con associazioni TwoWay e OneWayToSource. Come già detto, gli eventi che generano un aggiornamento dell'origine dipendono dal valore della proprietà UpdateSourceTrigger, come descritto nella sezione Eventi che generano gli aggiornamenti dell'origine.

Gli elementi seguenti descrivono il processo di convalida. Si noti che se in un qualsiasi momento del processo si verifica un errore di convalida o di altro tipo, il processo viene interrotto.

  1. Successivamente controlla se siano stati definiti oggetti ValidationRule personalizzati con ValidationStep impostato su RawProposedValue per tale oggetto Binding, nel qual caso chiama il metodo Validate su ciascun oggetto ValidationRule fino a che non viene rilevato un errore per uno di essi o finché non restituiscono tutti esito positivo.

  2. Il motore di associazione chiama quindi il convertitore, se presente.

  3. Se il convertitore riesce, il motore di associazione controlla se sono stati definiti oggetti ValidationRule personalizzati il cui valore di ValidationStep sia impostato su ConvertedProposedValue per Binding, nel qual caso chiama il metodo Validate su ciascun oggetto ValidationRule con ValidationStep impostato su ConvertedProposedValue fino a che non viene rilevato un errore per uno di essi o finché non restituiscono tutti esito positivo.

  4. Il motore di associazione imposta la proprietà di origine.

  5. Il motore di associazione controlla se sono stati definiti oggetti ValidationRule personalizzati la cui proprietà ValidationStep sia impostata su UpdatedValue per l'oggetto Binding in questione, nel qual caso chiama il metodo Validate su ciascun oggetto ValidationRule con la proprietà ValidationStep impostata su UpdatedValue fino a che non viene rilevato un errore per uno di essi o finché non restituiscono tutti esito positivo. Se un oggetto DataErrorValidationRule è associato a un'associazione e la relativa proprietà ValidationStep è impostata sul valore predefinito, vale a dire UpdatedValue, DataErrorValidationRule viene controllato a questo punto. Si tratta inoltre del momento in cui vengono controllate le associazioni la cui proprietà ValidatesOnDataErrors è impostata su true.

  6. Il motore di associazione controlla se sono stati definiti oggetti ValidationRule personalizzati la cui proprietà ValidationStep sia impostata su CommittedValue per l'oggetto Binding in questione, nel qual caso chiama il metodo Validate su ciascun oggetto ValidationRule con la proprietà ValidationStep impostata su CommittedValue fino a che non viene rilevato un errore per uno di essi o finché non restituiscono tutti esito positivo.

Se in un qualsiasi momento del processo l’oggetto ValidationRule restituisce esito negativo, il motore di associazione crea un oggetto ValidationError e lo aggiunge all’insieme Validation..::.Errors dell’elemento associato. Prima che il motore di associazione venga eseguito sugli oggetti ValidationRule in un determinato passaggio, esso rimuove qualsiasi oggetto ValidationError aggiunto alla proprietà associata Validation..::.Errors dell’elemento associato durante quel passaggio. Ad esempio, se un oggetto ValidationRule il cui ValidationStep è impostato su UpdatedValue ha avuto esito negativo, la volta successiva in cui si verifica il processo di convalida, il motore di associazione rimuove l'oggetto ValidationError immediatamente prima di chiamare ValidationRule con ValidationStep impostato su UpdatedValue.

Se la proprietà Validation..::.Errors non è vuota, la proprietà associata Validation..::.HasError dell'elemento viene impostata su true. Se, inoltre, la proprietà NotifyOnValidationError dell'oggetto Binding è impostata su true, il motore di associazione genera l'evento associato Validation..::.Error sull'elemento.

Il trasferimento di un valore valido in una delle due direzioni (dalla destinazione all'origine o viceversa) comporta la cancellazione della proprietà associata Validation..::.Errors.

Se all’associazione è associato un oggetto ExceptionValidationRule o se la proprietà ValidatesOnExceptions è impostata su true e viene generata un’eccezione quando il motore di associazione imposta l’origine, il motore di associazione controlla se è presente un oggetto UpdateSourceExceptionFilter. È possibile utilizzare il callback UpdateSourceExceptionFilter per fornire un gestore personalizzato per la gestione delle eccezioni. Se un UpdateSourceExceptionFilter non viene specificato sull'oggetto Binding, il modulo di associazione crea un ValidationError con l'eccezione e lo aggiunge all'insieme Validation..::.Errors dell'elemento associato.

È possibile impostare la proprietà associata PresentationTraceSources..::.TraceLevel in un oggetto correlato all'associazione per ricevere informazioni sullo stato di una particolare associazione.

Contenuti della community   Che cos'è Contenuti della community?
Aggiungi nuovo contenuto RSS  Annotazioni
Processing
© 2012 Microsoft. Tutti i diritti riservati. Condizioni per l'utilizzo | Marchi | Informativa sulla privacy
Page view tracker