Compartir a través de


Información general sobre la vinculación de datos

El enlace de datos en Windows Presentation Foundation (WPF) proporciona una manera sencilla y coherente de que las aplicaciones presenten e interactúen con los datos. Los elementos se pueden enlazar a datos de diferentes tipos de orígenes de datos en forma de objetos .NET y XML. Cualquiera ContentControl como Button y cualquier ItemsControl, como ListBox y ListView, tienen funcionalidad integrada para habilitar el estilo flexible de elementos de datos únicos o colecciones de elementos de datos. Las vistas de clasificación, filtrado y agrupación pueden generarse a partir de los datos.

El enlace de datos en WPF tiene varias ventajas sobre los modelos tradicionales, incluida la compatibilidad inherente para el enlace de datos a través de una amplia gama de propiedades, la representación flexible de los datos en la interfaz de usuario, y la separación clara de la lógica empresarial de la interfaz de usuario.

En este artículo se describen primero los conceptos fundamentales para el enlace de datos de WPF y, a continuación, se describe el uso de la Binding clase y otras características del enlace de datos.

¿Qué es el enlace de datos?

El enlace de datos es el proceso que establece una conexión entre la interfaz de usuario de la aplicación y los datos que muestra. Si el enlace tiene la configuración correcta y los datos proporcionan las notificaciones adecuadas, cuando los datos cambian su valor, los elementos enlazados a los datos reflejan los cambios automáticamente. El enlace de datos también puede significar que si cambia una representación externa de los datos de un elemento, los datos subyacentes se pueden actualizar automáticamente para reflejar el cambio. Por ejemplo, si el usuario edita el valor en un TextBox elemento, el valor de datos subyacente se actualiza automáticamente para reflejar ese cambio.

Un uso típico del enlace de datos es colocar datos de configuración local o de servidor en formularios u otros controles de interfaz de usuario. En WPF, este concepto se expande para incluir el enlace de una amplia gama de propiedades a diferentes tipos de orígenes de datos. En WPF, las propiedades de dependencia de los elementos se pueden enlazar a objetos .NET (incluidos objetos ADO.NET u objetos asociados a servicios web y propiedades web) y datos XML.

Conceptos básicos de enlace de datos

Independientemente del elemento que esté enlazando y de la naturaleza del origen de datos, cada enlace siempre sigue el modelo que se muestra en la ilustración siguiente.

Diagrama que muestra el modelo de enlace de datos básico.

Como se muestra en la ilustración, el enlace de datos es básicamente el puente entre el destino de enlace y el origen de enlace. En la ilustración se muestran los siguientes conceptos fundamentales de enlace de datos de WPF:

  • Normalmente, cada enlace tiene cuatro componentes:

    • Objeto de destino de vinculación.
    • Una propiedad de destino.
    • Fuente vinculante.
    • Ruta de acceso al valor del origen de enlace que se va a usar.

    Por ejemplo, si enlaza el contenido de TextBox a la propiedad Employee.Name, configuraría el enlace como en la tabla siguiente.

    Configuración Importancia
    Objetivo TextBox
    Propiedad de destino Text
    Source (objeto) Employee
    Ruta de valor del objeto origen Name
  • La propiedad de destino debe ser una propiedad de dependencia.

    La mayoría UIElement de las propiedades son propiedades de dependencia y la mayoría de las propiedades de dependencia, excepto las de solo lectura, admiten el enlace de datos de forma predeterminada. Solo los tipos derivados de DependencyObject pueden definir propiedades de dependencia. Todos los UIElement tipos derivan de DependencyObject.

  • Los orígenes de enlace no están restringidos a objetos .NET personalizados.

    Aunque no se muestra en la ilustración, se debe tener en cuenta que el objeto de origen de enlace no está restringido a ser un objeto .NET personalizado. El enlace de datos de WPF admite datos en forma de objetos .NET, XML e incluso objetos de elemento XAML. Para proporcionar algunos ejemplos, el origen de enlace puede ser UIElement, cualquier objeto de lista, un objeto de ADO.NET o Servicios Web, o un XmlNode que contenga los datos XML. Para obtener más información, consulte Visión general de los orígenes de enlace.

Es importante recordar que cuando se establece un enlace, se enlaza un destino de enlace a un origen de enlace. Por ejemplo, si estás mostrando algunos datos XML subyacentes en un ListBox utilizando enlace de datos, estás vinculando tu ListBox a los datos XML.

Para establecer un enlace, use el Binding objeto . En el resto de este artículo se describen muchos de los conceptos asociados a y algunas de las propiedades y el uso del Binding objeto.

Contexto de datos

Cuando el enlace de datos se declara en los elementos XAML, estos resuelven el enlace de datos al examinar su propiedad inmediata DataContext. El contexto de datos suele ser el objeto de origen de enlace para la evaluación de la ruta de acceso del valor de origen de enlace . Puede invalidar este comportamiento en el enlace y establecer un valor de objeto de origen de enlace específico. Si no se establece la propiedad DataContext del objeto que hospeda la vinculación, se comprueba la propiedad del elemento DataContext primario, y así sucesivamente, hasta la raíz del árbol de objetos XAML. En resumen, el contexto de datos usado para resolver el enlace se hereda del elemento primario a menos que se establezca explícitamente en el objeto .

Los enlaces se pueden configurar para resolverse con un objeto específico, en lugar de usar el contexto de datos para la resolución de enlaces. Al especificar un objeto de origen directamente se usa cuando, por ejemplo, se enlaza el color de primer plano de un objeto al color de fondo de otro objeto. El contexto de datos no es necesario, ya que el enlace se resuelve entre esos dos objetos. Inversamente, los enlaces que no están enlazados a objetos de origen específicos usan la resolución de contexto de datos.

Cuando cambia la DataContext propiedad, se vuelven a evaluar todos los enlaces que podrían verse afectados por el contexto de datos.

Dirección del flujo de datos

Como se indica en la flecha de la ilustración anterior, el flujo de datos de un enlace puede ir del destino de enlace al origen de enlace (por ejemplo, el valor de origen cambia cuando un usuario edita el valor de ) TextBoxo desde el origen de enlace al destino de enlace (por ejemplo, el TextBox contenido se actualiza con cambios en el origen de enlace) si el origen de enlace proporciona las notificaciones adecuadas.

Es posible que quiera que la aplicación permita a los usuarios cambiar los datos y volver a propagarlos al objeto de origen. O bien, es posible que no quiera permitir que los usuarios actualicen los datos de origen. Puede controlar el flujo de datos estableciendo .Binding.Mode

En esta ilustración se muestran los distintos tipos de flujo de datos:

Flujo de datos de vinculación de datos

  • OneWay el enlace hace que los cambios en la propiedad de origen actualicen automáticamente la propiedad de destino, pero los cambios en la propiedad de destino no se propagan a la propiedad de origen. Este tipo de enlace es adecuado si el control que se enlaza es de solo lectura implícitamente. Por ejemplo, puede enlazar a una fuente, como un indicador bursátil, o quizás la propiedad de destino no tenga una interfaz de control disponible para efectuar cambios, como un color de fondo vinculado a datos de una tabla. Si no es necesario supervisar los cambios de la propiedad de destino, el uso del OneWay modo de enlace evita la sobrecarga del TwoWay modo de enlace.

  • TwoWay el enlace hace que los cambios en la propiedad de origen o en la propiedad de destino actualicen automáticamente a la otra. Este tipo de enlace es adecuado para formularios editables u otros escenarios de interfaz de usuario totalmente interactivos. La mayoría de las propiedades tienen el enlace OneWay como valor predeterminado, pero algunas propiedades de dependencia (normalmente de controles editables por el usuario, como el TextBox.Text y CheckBox.IsChecked) tienen como valor predeterminado el enlace TwoWay.

    Una manera de determinar mediante programación si una propiedad de dependencia enlaza de manera unidireccional o bidireccional por defecto es obtener los metadatos de la propiedad con DependencyProperty.GetMetadata. El tipo de valor devuelto de este método es PropertyMetadata, que no contiene ningún metadato sobre el enlace. Sin embargo, si este tipo se puede convertir en el derivado FrameworkPropertyMetadata, se puede comprobar el valor booleano de la FrameworkPropertyMetadata.BindsTwoWayByDefault propiedad. En el ejemplo de código siguiente se muestra cómo obtener los metadatos de la TextBox.Text propiedad :

    public static void PrintMetadata()
    {
        // Get the metadata for the property
        PropertyMetadata metadata = TextBox.TextProperty.GetMetadata(typeof(TextBox));
    
        // Check if metadata type is FrameworkPropertyMetadata
        if (metadata is FrameworkPropertyMetadata frameworkMetadata)
        {
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:");
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}");
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}");
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}");
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}");
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}");
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}");
        }
    
        /*  Displays:
         *  
         *  TextBox.Text property metadata:
         *    BindsTwoWayByDefault: True
         *    IsDataBindingAllowed: True
         *          AffectsArrange: False
         *          AffectsMeasure: False
         *           AffectsRender: False
         *                Inherits: False
        */
    }
    
    Public Shared Sub PrintMetadata()
    
        Dim metadata As PropertyMetadata = TextBox.TextProperty.GetMetadata(GetType(TextBox))
        Dim frameworkMetadata As FrameworkPropertyMetadata = TryCast(metadata, FrameworkPropertyMetadata)
    
        If frameworkMetadata IsNot Nothing Then
    
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:")
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}")
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}")
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}")
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}")
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}")
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}")
    
            '  Displays:
            '
            '  TextBox.Text property metadata:
            '    BindsTwoWayByDefault: True
            '    IsDataBindingAllowed: True
            '          AffectsArrange: False
            '          AffectsMeasure: False
            '           AffectsRender: False
            '                Inherits: False
        End If
    
    
    End Sub
    
  • OneWayToSource es la inversa del OneWay enlace; actualiza la propiedad de origen cuando cambia la propiedad de destino. Un escenario de ejemplo es si solo necesita volver a evaluar el valor de origen de la interfaz de usuario.

  • No se muestra en la figura la vinculación OneTime, que hace que la propiedad de origen inicialice la propiedad de destino, pero no propaga los cambios posteriores. Si el contexto de datos cambia o el objeto en el contexto de datos cambia, el cambio no se refleja en la propiedad de destino. Este tipo de enlace es adecuado si una instantánea del estado actual es adecuada o los datos son realmente estáticos. Este tipo de enlace también es útil si desea inicializar la propiedad de destino con algún valor de una propiedad de origen y el contexto de datos no se conoce de antemano. Este modo es básicamente una forma más sencilla de OneWay enlace que proporciona un mejor rendimiento en los casos en los que el valor de origen no cambia.

Para detectar cambios en la fuente (aplicables a los enlaces OneWay y TwoWay), la fuente debe implementar un mecanismo adecuado para la notificación de cambios de propiedad, como INotifyPropertyChanged. Vea Cómo: Implementar la notificación de cambio de propiedad (.NET Framework) para obtener un ejemplo de una INotifyPropertyChanged implementación.

La Binding.Mode propiedad proporciona más información sobre los modos de enlace y un ejemplo de cómo especificar la dirección de un enlace.

¿Qué desencadena las actualizaciones de origen?

Los enlaces que son TwoWay o OneWayToSource escuchan cambios en la propiedad de destino y los propagan de nuevo al origen, conocido como actualización del origen. Por ejemplo, puede editar el texto de un TextBox para cambiar el valor de origen subyacente.

Sin embargo, ¿se actualiza el valor de origen mientras edita el texto o después de terminar de editar el texto y el control pierde el foco? La Binding.UpdateSourceTrigger propiedad determina qué desencadena la actualización del origen. Los puntos de las flechas derechas de la ilustración siguiente muestran el rol de la Binding.UpdateSourceTrigger propiedad .

Diagrama que muestra el rol de la propiedad UpdateSourceTrigger.

** Si el valor de UpdateSourceTrigger es UpdateSourceTrigger.PropertyChanged, entonces el valor señalado por la flecha hacia la derecha de TwoWay o las vinculaciones de OneWayToSource se actualiza tan pronto como cambie la propiedad objetivo. Sin embargo, si el UpdateSourceTrigger valor es LostFocus, ese valor solo se actualiza con el nuevo valor cuando la propiedad de destino pierde el foco.

De forma similar a la Mode propiedad , las distintas propiedades de dependencia tienen valores predeterminados UpdateSourceTrigger diferentes. El valor predeterminado para la mayoría de las propiedades de dependencia es PropertyChanged, lo que hace que el valor de la propiedad de origen cambie instantáneamente cuando se cambia el valor de la propiedad de destino. Los cambios instantáneos son adecuados para CheckBox y otros controles simples. Sin embargo, en el caso de los campos de texto, la actualización después de cada pulsación de tecla puede disminuir el rendimiento y denegar al usuario la oportunidad habitual de retroceso y corregir los errores de escritura antes de confirmar el nuevo valor. Por ejemplo, la propiedad TextBox.Text tiene por defecto el valor UpdateSourceTrigger, lo que hace que el valor de origen LostFocus solo cambie cuando el elemento de control pierda el foco, y no cuando se cambia la propiedad TextBox.Text. Consulte la UpdateSourceTrigger página de propiedades para obtener información sobre cómo buscar el valor predeterminado de una propiedad de dependencia.

En la tabla siguiente se muestra un ejemplo de escenario para cada valor UpdateSourceTrigger utilizando TextBox como referencia.

Valor del atributo de activación de actualización Cuando se actualiza el valor de origen Escenario de ejemplo para TextBox
LostFocus (valor predeterminado para TextBox.Text) Cuando el control TextBox pierde el foco. TextBox asociado a la lógica de validación (consulte La validación de datos a continuación).
PropertyChanged A medida que escribe en TextBox. Controles TextBox en una ventana de salón de chat.
Explicit Cuando la aplicación llama a UpdateSource. Controles TextBox en un formulario editable (actualiza los valores de origen solo cuando el usuario presiona el botón enviar).

Para obtener un ejemplo, vea Cómo controlar cuándo el texto de TextBox actualiza la fuente (.NET Framework).

Ejemplo de enlace de datos

Para obtener un ejemplo de enlace de datos, eche un vistazo a la siguiente interfaz de usuario de la aplicación , que muestra una lista de elementos de subasta.

Captura de pantalla de ejemplo de enlace de datos

La aplicación muestra las siguientes características del enlace de datos:

  • El contenido de ListBox está enlazado a una colección de objetos AuctionItem . Un objeto AuctionItem tiene propiedades como Description, StartPrice, StartDate, Category y SpecialFeatures.

  • Los datos (objetos AuctionItem) son mostrados en la ListBox de plantilla para mostrar la descripción y el precio actual de cada elemento. La plantilla se crea mediante el uso de un DataTemplate. Además, la apariencia de cada elemento depende del valor SpecialFeatures del objeto AuctionItem que se muestra. Si el valor SpecialFeatures del Objeto AuctionItem es Color, el elemento tiene un borde azul. Si el valor es Resaltar, el elemento tiene un borde naranja y una estrella. La sección Plantillas de datos proporciona información sobre la plantillas de datos.

  • El usuario puede agrupar, filtrar o ordenar los datos mediante el CheckBoxes proporcionado. En la imagen anterior, se seleccionan Agrupar por categoría y Ordenar por categoría y fechaCheckBoxes. Es posible que haya observado que los datos se agrupan en función de la categoría del producto y el nombre de la categoría está en orden alfabético. Es difícil observar de la imagen, pero los elementos también se ordenan por la fecha de inicio dentro de cada categoría. La ordenación se realiza mediante una vista de colección. En la sección Enlace a colecciones se describen las vistas de colección.

  • Cuando el usuario selecciona un elemento, ContentControl muestra los detalles del elemento seleccionado. Esta experiencia se denomina escenario maestro-detalle. En la sección Escenario de maestro-detalle se proporciona información sobre este tipo de vinculación.

  • El tipo de la propiedad StartDate es DateTime, que devuelve una fecha que incluye la hora al milisegundo. En esta aplicación, se ha usado un convertidor personalizado para que se muestre una cadena de fecha más corta. La sección Conversión de datos proporciona información sobre los convertidores.

Cuando el usuario selecciona el botón Agregar producto , aparece el siguiente formulario.

Agregar página de listado de productos

El usuario puede editar los campos en el formulario, obtener una vista previa de la lista de productos mediante los paneles de vista previa breves o detallados y seleccionar Submit para agregar la nueva lista de productos. Cualquier configuración de agrupación, filtrado y ordenación existente se aplicará a la nueva entrada. En este caso concreto, el elemento mostrado en la imagen anterior se visualizará como el segundo elemento dentro de la categoría Computadora.

No se muestra en esta imagen la lógica de validación proporcionada en la Fecha de inicioTextBox. Si el usuario escribe una fecha no válida (formato no válido o una fecha pasada), se notificará al usuario con un ToolTip signo de exclamación rojo junto a TextBox. En la sección Validación de datos se describe cómo crear lógica de validación.

Antes de entrar en las diferentes características del enlace de datos descrito anteriormente, primero analizaremos los conceptos fundamentales que son fundamentales para comprender el enlace de datos de WPF.

Creación de un enlace

Para restar algunos de los conceptos descritos en las secciones anteriores, se establece un enlace mediante el Binding objeto y cada enlace suele tener cuatro componentes: un destino de enlace, una propiedad de destino, un origen de enlace y una ruta de acceso al valor de origen que se va a usar. En esta sección se describe cómo configurar una vinculación.

Las fuentes de enlace están vinculadas al DataContext activo del elemento. Los elementos heredan DataContext automáticamente si no han definido explícitamente uno.

Considere el ejemplo siguiente, en el que el objeto de origen de enlace es una clase denominada MyData que se define en el espacio de nombres SDKSample . Para fines de demostración, MyData tiene una propiedad de cadena denominada ColorName cuyo valor se establece en "Red". Por lo tanto, este ejemplo genera un botón con un fondo rojo.

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

Para obtener más información sobre la sintaxis de declaración de enlace y ejemplos de cómo configurar un enlace en el código, consulte Resumen de declaraciones de enlace.

Si aplicamos este ejemplo a nuestro diagrama básico, la ilustración resultante es similar a la siguiente. En esta figura se describe un OneWay enlace porque la propiedad Background admite OneWay enlace de forma predeterminada.

Diagrama que muestra la propiedad Background del enlace de datos.

Es posible que se pregunte por qué funciona este enlace aunque la propiedad ColorName sea de tipo cadena mientras la Background propiedad es de tipo Brush. Este enlace usa la conversión de tipos predeterminada, que se describe en la sección Conversión de datos .

Especificación del origen de enlace

Observe que en el ejemplo anterior, el origen de enlace se especifica estableciendo la propiedad DockPanel.DataContext . El Button hereda el valor DataContext de DockPanel, que es su elemento padre. Para repetir, el objeto de origen de enlace es uno de los cuatro componentes necesarios de un enlace. Por lo tanto, sin que se especifique el objeto de origen de enlace, el enlace no haría nada.

Hay varias maneras de especificar el objeto de origen de enlace. El uso de la DataContext propiedad en un elemento primario es útil cuando se enlazan varias propiedades al mismo origen. Sin embargo, a veces puede ser más adecuado especificar el origen de enlace en declaraciones de enlace individuales. En el ejemplo anterior, en lugar de usar la DataContext propiedad , puede especificar el origen de enlace estableciendo la Binding.Source propiedad directamente en la declaración de enlace del botón, como en el ejemplo siguiente.

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

Aparte de establecer la DataContext propiedad en un elemento directamente, heredar el DataContext valor de un antecesor (como el botón del primer ejemplo) y especificar explícitamente el origen de enlace estableciendo la Binding.Source propiedad en el enlace (como el botón del último ejemplo), también puede usar la Binding.ElementName propiedad o la Binding.RelativeSource propiedad para especificar el origen de enlace. La ElementName propiedad es útil cuando se enlazan a otros elementos de la aplicación, como cuando se usa un control deslizante para ajustar el ancho de un botón. La propiedad RelativeSource es útil cuando el enlace se especifica en un ControlTemplate o un Style. Para obtener más información, consulte Visión general de los orígenes de enlace.

Especificación de la ruta de acceso al valor

Si el origen del enlace es un objeto, use la Binding.Path propiedad para especificar el valor que se va a usar para el enlace. Si va a enlazar a datos XML, use la Binding.XPath propiedad para especificar el valor. En algunos casos, puede resultar aplicable utilizar la propiedad Path incluso cuando sus datos son XML. Por ejemplo, si desea tener acceso a la propiedad Name de un XmlNode devuelto (como resultado de una consulta XPath), debe usar la Path propiedad además de la XPath propiedad .

Para obtener más información, consulte las Path propiedades y XPath .

Aunque hemos destacado que el valor que se va a utilizar para Path es uno de los cuatro componentes necesarios de una vinculación, en los escenarios donde desea vincular a un objeto completo, el valor que se utilizará sería el mismo que el objeto fuente de la vinculación. En esos casos, es adecuado no especificar un Path. Considere el ejemplo siguiente.

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

En el ejemplo anterior se usa la sintaxis de enlace vacía: {Binding}. En este caso, ListBox hereda dataContext de un elemento DockPanel primario (no se muestra en este ejemplo). Cuando no se especifica la ruta de acceso, el valor predeterminado es enlazar a todo el objeto. En otras palabras, en este ejemplo, se ha dejado fuera la ruta de acceso porque estamos enlazando la propiedad ItemsSource a todo el objeto. (Consulte la sección Enlace a colecciones para obtener una explicación detallada).

Aparte del enlace a una colección, este escenario también es útil cuando se desea enlazar a un objeto completo en lugar de solo una propiedad de un objeto. Por ejemplo, si el objeto de origen es de tipo String, es posible que simplemente desee enlazar a la propia cadena. Otro escenario común es cuando se quiere enlazar un elemento a un objeto con varias propiedades.

Es posible que necesite aplicar lógica personalizada para que los datos sean significativos para la propiedad enlazada de destino. La lógica personalizada puede estar en forma de convertidor personalizado si la conversión de tipos predeterminada no existe. Consulte Conversión de datos para obtener información sobre los convertidores.

Binding y BindingExpression

Antes de entrar en otras características y usos del enlace de datos, resulta útil introducir la BindingExpression clase . Como ha visto en las secciones anteriores, la Binding clase es la clase de alto nivel para la declaración de un enlace; proporciona muchas propiedades que permiten especificar las características de un enlace. Una clase relacionada, BindingExpression, es el objeto subyacente que mantiene la conexión entre el origen y el destino. Un enlace contiene toda la información que se puede compartir entre varias expresiones de enlace. Es BindingExpression una expresión de instancia que no se puede compartir y contiene toda la información de instancia de Binding.

Considere el ejemplo siguiente, donde myDataObject es una instancia de la MyData clase , myBinding es el objeto de Binding origen y MyData es una clase definida que contiene una propiedad de cadena denominada ColorName. En este ejemplo se enlaza el contenido de texto de myText, una instancia de TextBlock, a ColorName.

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

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

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

Puede usar el mismo objeto myBinding para crear otros enlaces. Por ejemplo, puede usar el objeto myBinding para enlazar el contenido de texto de una casilla a ColorName. En ese escenario, habrá dos instancias de BindingExpression que comparten el objeto myBinding.

Se devuelve un BindingExpression objeto al llamar a GetBindingExpression en un objeto enlazado a los datos. En los artículos siguientes se muestran algunos de los usos de la BindingExpression clase :

Conversión de datos

En la sección Crear un enlace , el botón es rojo porque su Background propiedad está enlazada a una propiedad de cadena con el valor "Red". Este valor de cadena funciona porque un convertidor de tipos está presente en el Brush tipo para convertir el valor de cadena en un Brush.

Agregar esta información a la figura en la sección Crear una vinculación tiene este aspecto.

Diagrama que muestra la propiedad Predeterminada de la vinculación de datos.

Sin embargo, ¿qué ocurre si, en lugar de tener una propiedad de tipo string, el objeto de origen de enlace tiene una propiedad Color de tipo Color? En ese caso, para que el enlace funcione, primero tendría que convertir el valor de la propiedad Color en algo que acepte la Background propiedad. Tendría que crear un convertidor personalizado mediante la implementación de la IValueConverter interfaz, como en el ejemplo siguiente.

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

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

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

Consulte IValueConverter para obtener más información.

Ahora se usa el convertidor personalizado en lugar de la conversión predeterminada y nuestro diagrama tiene este aspecto.

Diagrama que muestra el convertidor personalizado de enlace de datos.

Para repetir, las conversiones predeterminadas pueden estar disponibles debido a convertidores de tipos que están presentes en el tipo al que se enlaza. Este comportamiento dependerá de los convertidores de tipos disponibles en el destino. En caso de duda, cree su propio convertidor.

A continuación se muestran algunos escenarios típicos en los que tiene sentido implementar un convertidor de datos:

  • Los datos deben mostrarse de forma diferente, dependiendo de la cultura. Por ejemplo, puede que quiera implementar un convertidor de monedas o un convertidor de fecha y hora de calendario en función de las convenciones usadas en una referencia cultural determinada.

  • Los datos que se usan no están diseñados necesariamente para cambiar el valor de texto de una propiedad, pero en su lugar está pensado para cambiar algún otro valor, como el origen de una imagen, o el color o estilo del texto para mostrar. Los convertidores se pueden usar en esta instancia convirtiendo el enlace de una propiedad que podría no parecer adecuado, como enlazar un campo de texto a la propiedad Background de una celda de tabla.

  • Más de un control o varias propiedades de los controles están enlazados a los mismos datos. En este caso, el enlace principal podría mostrar simplemente el texto, mientras que otros enlaces controlan problemas de visualización específicos, pero siguen usando el mismo enlace que la información de origen.

  • Una propiedad de destino tiene una colección de enlaces, que se denomina MultiBinding. Para MultiBinding, se utiliza un IMultiValueConverter personalizado para generar un valor final a partir de los valores de las vinculaciones. Por ejemplo, el color se puede calcular a partir de valores rojo, azul y verde, que pueden ser valores de los mismos objetos de origen de enlace o diferentes. Consulte MultiBinding para obtener ejemplos e información.

Vinculación a colecciones

Un objeto de origen de enlace se puede tratar como un único objeto cuyas propiedades contienen datos o como una colección de datos de objetos polimórficos que a menudo se agrupan (como el resultado de una consulta en una base de datos). Hasta ahora solo hemos analizado el enlace a objetos únicos. Sin embargo, el enlace a una colección de datos es un escenario común. Por ejemplo, un escenario común consiste en usar un ItemsControl como , ListBoxListViewo TreeView para mostrar una colección de datos, como en la aplicación que se muestra en la sección ¿Qué es el enlace de datos?.

Afortunadamente, nuestro diagrama básico todavía se aplica. Si estás enlazando un ItemsControl a una colección, el diagrama tiene este aspecto.

Diagrama que muestra el enlace de datos del objeto ItemsControl.

Como se muestra en este diagrama, para enlazar un ItemsControl a un objeto de colección, ItemsControl.ItemsSource es la propiedad que se debe usar. Puede considerar ItemsSource como el contenido de ItemsControl. La vinculación es OneWay porque la propiedad ItemsSource admite la vinculación OneWay por defecto.

Cómo implementar colecciones

Puede recorrer cualquier colección que implemente la interfaz IEnumerable. Sin embargo, para configurar enlaces dinámicos para que las inserciones o eliminaciones de la colección actualicen la interfaz de usuario automáticamente, la colección debe implementar la INotifyCollectionChanged interfaz. Esta interfaz expone un evento que se debe generar cada vez que cambia la colección subyacente.

WPF proporciona la ObservableCollection<T> clase , que es una implementación integrada de una colección de datos que expone la INotifyCollectionChanged interfaz . Para admitir completamente la transferencia de valores de datos de objetos de origen a destinos, cada objeto de la colección que admita propiedades enlazables también debe implementar la INotifyPropertyChanged interfaz . Para obtener más información, consulte Visión general de los orígenes de enlace.

Antes de implementar su propia colección, considere la posibilidad de usar ObservableCollection<T> o una de las clases de colección existentes, como List<T>, Collection<T>y BindingList<T>, entre muchas otras. Si tiene un escenario avanzado y desea implementar su propia colección, considere la posibilidad de usar IList, que proporciona una colección no genérica de objetos a los que el índice puede tener acceso individualmente y, por tanto, proporciona el mejor rendimiento.

Vistas de colección

Una vez que su ItemsControl esté enlazado a un conjunto de datos, es posible que quiera ordenar, filtrar o agrupar los datos. Para ello, se usan vistas de colección, que son clases que implementan la ICollectionView interfaz .

¿Qué son las vistas de colección?

Una vista de colección es una capa sobre una colección de orígenes de enlace que permite navegar y mostrar la colección de origen en función de consultas de ordenación, filtro y grupo, sin tener que cambiar la propia colección de origen subyacente. Una vista de colección también mantiene un puntero al elemento actual de la colección. Si la colección de origen implementa la INotifyCollectionChanged interfaz, los cambios generados por el CollectionChanged evento se propagan a las vistas.

Dado que las vistas no cambian las colecciones de origen subyacentes, cada colección de origen puede tener varias vistas asociadas. Por ejemplo, puede tener una colección de objetos Task . Con el uso de vistas, puede mostrar esos mismos datos de maneras diferentes. Por ejemplo, en el lado izquierdo de la página puede mostrar las tareas ordenadas por prioridad y, en el lado derecho, agrupadas por área.

Cómo crear una vista

Una manera de crear y usar una vista es crear una instancia del objeto de vista directamente y, a continuación, usarlo como origen de enlace. Por ejemplo, considere la que se muestra en la sección ¿Qué es el enlace de datos? La aplicación se implementa de forma que ListBox vincula a una vista de la colección de datos en lugar de directamente a la colección de datos. El ejemplo siguiente se extrae de la aplicación . La CollectionViewSource clase es el proxy XAML de una clase que hereda de CollectionView. En este ejemplo concreto, el Source de la vista se enlaza a la colección AuctionItems (de tipo ObservableCollection<T>) del objeto de aplicación actual.

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

A continuación, listingDataView actúa como origen de enlace para los elementos de la aplicación, como ListBox.

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

Para crear otra vista para la misma colección, puede crear otra CollectionViewSource instancia y asignarle un nombre diferente x:Key .

En la tabla siguiente se muestran qué tipos de datos de vista se crean como la vista de recopilación predeterminada o por CollectionViewSource, según el tipo de colección de origen.

Tipo de colección de origen Tipo de vista de colección Notas
IEnumerable Un tipo interno basado en CollectionView No se pueden agrupar elementos.
IList ListCollectionView El más rápido.
IBindingList BindingListCollectionView

Uso de una vista predeterminada

Especificar una vista de colección como origen de enlace es una manera de crear y usar una vista de colección. WPF también crea una vista de colección predeterminada para cada colección usada como origen de enlace. Si enlaza directamente a una colección, WPF se enlaza a su vista predeterminada. Esta vista predeterminada se comparte mediante todos los enlaces a la misma colección, por lo que un cambio realizado en una vista predeterminada por un control enlazado o código (como ordenar o cambiar al puntero de elemento actual, descrito más adelante) se refleja en todos los demás enlaces a la misma colección.

Para obtener la vista predeterminada, use el GetDefaultView método . Para obtener un ejemplo, vea Obtener la vista predeterminada de una colección de datos (.NET Framework).

Vistas de colección con ADO.NET DataTables

Para mejorar el rendimiento, las vistas de colección para ADO.NET DataTable u DataView objetos delegan la ordenación y el filtrado en DataView, lo que hace que la ordenación y el filtrado se compartan en todas las vistas de colección del origen de datos. Para habilitar cada vista de colección para ordenar y filtrar de forma independiente, inicialice cada vista de colección con su propio DataView objeto.

Ordenación

Como se mencionó antes, las vistas pueden aplicar un criterio de ordenación a una colección. Como existe en la colección subyacente, los datos pueden tener o no un orden inherente relevante. La vista sobre la colección permite imponer un pedido o cambiar el orden predeterminado, en función de los criterios de comparación que proporcione. Dado que es una vista basada en cliente de los datos, un escenario común es que el usuario podría querer ordenar columnas de datos tabulares por el valor al que corresponde la columna. Con las vistas, se puede aplicar esta ordenación controlada por el usuario, de nuevo sin realizar ningún cambio en la colección subyacente o incluso tener que volver a consultar el contenido de la colección. Para obtener un ejemplo, vea Ordenar una columna GridView cuando se hace clic en un encabezado (.NET Framework).

En el ejemplo siguiente se muestra la lógica de ordenación de "Ordenar por categoría y fecha" CheckBox de la interfaz de usuario en la sección ¿Qué es el enlace de datos.

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

Filtros

Las vistas también pueden aplicar un filtro a una colección, de modo que la vista solo muestre un subconjunto determinado de la colección completa. Puede filtrar por una condición en los datos. Por ejemplo, como lo hace la aplicación en la sección ¿Qué es el enlace de datos?, "Mostrar solo ofertas" CheckBox contiene lógica para filtrar los elementos que cuestan 25 dólares o más. El código siguiente se ejecuta para establecer ShowOnlyBargainsFilter como el manejador de eventos Filter cuando se selecciona CheckBox.

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

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

El controlador de eventos ShowOnlyBargainsFilter tiene la siguiente implementación.

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

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

    ' Start with everything excluded
    e.Accepted = False

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

    If product IsNot Nothing Then

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

    End If

End Sub

Si está utilizando una de las clases CollectionView directamente en lugar de CollectionViewSource, debe usar la propiedad Filter para especificar una función de devolución de llamada. Para obtener un ejemplo, vea Filtrar datos en una vista (.NET Framework).

Agrupación

Excepto para la clase interna que ve una IEnumerable colección, todas las vistas de colección admiten la agrupación, lo que permite al usuario particionar la colección en la vista de colección en grupos lógicos. Los grupos pueden ser explícitos, donde el usuario proporciona una lista de grupos, o implícitos, donde los grupos se generan dinámicamente según los datos.

En el ejemplo siguiente se muestra la lógica de "Agrupar por categoría". CheckBox

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

Para obtener otro ejemplo de agrupación, vea Agrupar elementos en un control ListView que implemente una clase GridView (.NET Framework).

Punteros de elemento actuales

Las vistas también admiten la noción de un elemento vigente. Puede navegar por los objetos de una vista de colección. A medida que navega, mueve el puntero de un elemento que le permite recuperar el objeto que existe en esa ubicación específica de la colección. Para obtener un ejemplo, vea Navegar por los objetos de un data CollectionView (.NET Framework).

Dado que WPF se enlaza a una colección solo mediante una vista (ya sea una vista que especifique o la vista predeterminada de la colección), todos los enlaces a colecciones tienen un puntero de elemento actual. Cuando se enlaza a una vista, el carácter barra ("/") en un valor Path designa el elemento actual de la vista. En el ejemplo siguiente, el contexto de datos es una vista de colección. La primera línea vincula a la colección. La segunda línea se enlaza al elemento actual de la colección. La tercera línea enlaza a la Description propiedad del elemento actual de la colección.

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

La barra diagonal y la sintaxis de propiedades también se pueden apilar para recorrer una jerarquía de colecciones. En el ejemplo siguiente se enlaza al elemento actual de una colección denominada Offices, que es una propiedad del elemento actual de la colección de origen.

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

El puntero de elemento actual puede verse afectado por cualquier ordenación o filtrado que se aplique a la colección. La ordenación conserva el puntero del elemento actual en el último elemento seleccionado, pero la vista de colección ahora se reorganiza en torno a este punto. (Quizás el elemento seleccionado estaba al principio de la lista antes, pero ahora el elemento seleccionado podría estar en algún lugar del centro). El filtrado conserva el elemento seleccionado si esa selección permanece en la vista después del filtrado. De lo contrario, el puntero de elemento actual se establece en el primer elemento de la vista de colección filtrada.

Escenario de enlace maestro-detalle

La noción de un elemento actual no solo es útil para la navegación de elementos de una colección, sino también para el escenario de enlace maestro-detalle. Considere la interfaz de usuario de la aplicación en la sección ¿Qué es el enlace de datos ? En esa aplicación, la selección dentro de ListBox determina el contenido que se muestra en ContentControl. Para colocarlo de otra manera, cuando se selecciona un ListBox elemento, muestra ContentControl los detalles del elemento seleccionado.

Puede implementar el escenario master-detail simplemente teniendo dos o más controles enlazados a la misma vista. En el siguiente ejemplo de la aplicación de demostración de se muestra el marcado de ListBox y ContentControl que se ve en la interfaz de usuario de la aplicación en la sección ¿Qué es el enlace de datos.

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

Observe que ambos controles están enlazados al mismo origen, el recurso estático listingDataView (consulte la definición de este recurso en la sección Creación de una vista). Este enlace funciona porque cuando un objeto (en ContentControl este caso) está enlazado a una vista de colección, se enlaza automáticamente a la CurrentItem de la vista. Los CollectionViewSource objetos sincronizan automáticamente la moneda y la selección. Si el control de lista no está enlazado a un objeto CollectionViewSource como en este ejemplo, entonces necesitaría establecer su propiedad IsSynchronizedWithCurrentItem a true para que funcione.

Para obtener otros ejemplos, vea Enlazar a una colección y mostrar información basada en la selección (.NET Framework) y Usar el patrón de maestro y detalle con datos jerárquicos (.NET Framework).

Es posible que haya observado que en el ejemplo anterior se usa una plantilla. De hecho, los datos no se mostrarían como deseamos sin el uso de plantillas (la que usa explícitamente ContentControl y la que usa implícitamente ListBox). Ahora vamos a la creación de plantillas de datos en la sección siguiente.

Creación de plantillas de datos

Sin el uso de plantillas de datos, la interfaz de usuario de la aplicación en la sección Ejemplo de enlace de datos tendría el siguiente aspecto:

Demostración de vinculación de datos sin plantillas de datos

Como se muestra en el ejemplo de la sección anterior, tanto el ListBox control como el ContentControl están enlazados a todo el objeto de colección (o más concretamente, la vista sobre el objeto de colección) de AuctionItems. Sin instrucciones específicas sobre cómo mostrar la colección de datos, ListBox muestra la representación de cadena de cada objeto de la colección subyacente y ContentControl muestra la representación de cadena del objeto al que está enlazado.

Para resolver ese problema, la aplicación define DataTemplates. Como se muestra en el ejemplo de la sección anterior, usa ContentControl explícitamente la plantilla de datos detailsProductListingTemplate . El ListBox control usa implícitamente la siguiente plantilla de datos al mostrar los objetos AuctionItem en la colección.

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

Con el uso de esas dos dataTemplates, la interfaz de usuario resultante es la que se muestra en la sección ¿Qué es el enlace de datos ? Como puede ver desde esa captura de pantalla, además de permitirle colocar datos en los controles, DataTemplates le permite definir objetos visuales atractivos para los datos. Por ejemplo, DataTrigger se utilizan en la parte mencionada anteriormente DataTemplate para que AuctionItems con un valor de SpecialFeatures de HighLight se muestren con un borde naranja y una estrella.

Para obtener más información sobre las plantillas de datos, consulte información general sobre plantillas de datos (.NET Framework).

Validación de datos

La mayoría de las aplicaciones que toman la entrada del usuario deben tener lógica de validación para asegurarse de que el usuario ha escrito la información esperada. Las comprobaciones de validación se pueden basar en el tipo, el intervalo, el formato, u otros requisitos específicos de la aplicación. En esta sección se describe cómo funciona la validación de datos en WPF.

Asociación de reglas de validación con una vinculación

El modelo de enlace de datos de WPF permite asociar ValidationRules con el Binding objeto . Por ejemplo, en el ejemplo siguiente se enlaza un TextBox objeto a una propiedad denominada StartPrice y se agrega un ExceptionValidationRule objeto a la Binding.ValidationRules propiedad .

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

Un ValidationRule objeto comprueba si el valor de una propiedad es válido. WPF tiene dos tipos de objetos integrados ValidationRule :

También puede crear su propia regla de validación derivando de la ValidationRule clase e implementando el Validate método . El siguiente ejemplo muestra la regla usada por la sección Agregar listado de productos "Fecha de inicio" TextBox de la sección ¿Qué es el enlace de datos.

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

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

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

        Dim inputDate As Date

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

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

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

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

    End Function

End Class

StartDateEntryFormTextBox usa este FutureDateRule, como se muestra en el ejemplo siguiente.

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

Dado que el UpdateSourceTrigger valor es PropertyChanged, el motor de enlace actualiza el valor de origen en cada pulsación de tecla, lo que significa que también comprueba todas las reglas de la ValidationRules colección en cada pulsación de tecla. Esto se describe más adelante en la sección Proceso de validación.

Proporcionar comentarios visuales

Si el usuario escribe un valor no válido, es posible que quiera proporcionar algunos comentarios sobre el error en la interfaz de usuario de la aplicación. Una manera de proporcionar estos comentarios es establecer la Validation.ErrorTemplate propiedad adjunta en un personalizado ControlTemplate. Como se muestra en la subsección anterior, StartDateEntryFormTextBox usa una ErrorTemplate llamada validationTemplate. En el ejemplo siguiente se muestra la definición de validationTemplate.

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

El AdornedElementPlaceholder elemento especifica dónde se debe colocar el control que está siendo adornado.

Además, también puede usar un ToolTip para mostrar el mensaje de error. StartDateEntryForm y StartPriceEntryFormTextBoxes usan el estilo textStyleTextBox, que crea un ToolTip que muestra el mensaje de error. En el ejemplo siguiente se muestra la definición de textStyleTextBox. La propiedad Validation.HasError adjunta es true cuando uno o varios de los enlaces de las propiedades del elemento enlazado están en error.

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

Con el diseño personalizado ErrorTemplate y ToolTip, el StartDateEntryFormTextBox tiene el siguiente aspecto cuando hay un error de validación.

Error de validación de la vinculación de datos para la fecha

Si su Binding tiene reglas de validación asociadas, pero no especifica un ErrorTemplate en el control enlazado, se usará un ErrorTemplate predeterminado para notificar a los usuarios cuando haya un error de validación. El valor predeterminado ErrorTemplate es una plantilla de control que define un borde rojo en la capa de adorno. Con el valor predeterminado ErrorTemplate y ToolTip, la interfaz de usuario de StartPriceEntryFormTextBox es similar a la siguiente cuando se produce un error de validación.

Error de validación de la vinculación de datos del precio

Para obtener un ejemplo de cómo proporcionar lógica para validar todos los controles de un cuadro de diálogo, vea la sección Cuadros de diálogo personalizados en la introducción a los cuadros de diálogo.

Proceso de validación

La validación suele producirse cuando el valor de un destino se transfiere a la propiedad de origen de enlace. Esta transferencia se produce en las asociaciones TwoWay y OneWayToSource. Para reiterar, lo que provoca que una actualización de origen dependa del valor de la propiedad UpdateSourceTrigger, como se describe en la sección Qué desencadena las actualizaciones de origen.

Los siguientes elementos describen el proceso de validación . Si se produce un error de validación u otro tipo de error en cualquier momento durante este proceso, el proceso se detiene:

  1. El motor de enlace comprueba si hay objetos personalizados ValidationRule definidos cuyo ValidationStep está establecido como RawProposedValue para ese Binding, en cuyo caso llama al método Validate en cada ValidationRule hasta que uno de ellos encuentra un error o hasta que todos ellos se ejecutan correctamente.

  2. A continuación, el motor de enlace llama al convertidor, si existe uno.

  3. Si el convertidor tiene éxito, el motor de enlace comprueba si hay objetos personalizados ValidationRule definidos cuyos ValidationStep se establece ConvertedProposedValue para ese Binding, en cuyo caso llama al método Validate en cada ValidationRule que tenga ValidationStep establecido ConvertedProposedValue hasta que uno de ellos encuentre un error o hasta que todos ellos pasen.

  4. El motor de vinculación establece la propiedad de origen.

  5. El motor de enlace comprueba si hay objetos personalizados ValidationRule definidos cuyos ValidationStep se establecen en UpdatedValue para ese Binding, en cuyo caso llama al método Validate en cada ValidationRule que tiene ValidationStep establecido en UpdatedValue hasta que ocurra un error o hasta que todos ellos pasen. Si un DataErrorValidationRule está asociado a una vinculación y su ValidationStep se establece en el valor predeterminado, UpdatedValue, se comprueba el DataErrorValidationRule en ese momento. En este momento, se comprueba cualquier enlace que tenga ValidatesOnDataErrors establecido en true.

  6. El motor de enlace comprueba si hay objetos personalizados ValidationRule definidos cuyos ValidationStep se establecen en CommittedValue para ese Binding, en cuyo caso llama al método Validate en cada ValidationRule que tiene ValidationStep establecido en CommittedValue hasta que ocurra un error o hasta que todos ellos pasen.

Si un ValidationRule no pasa en un momento dado durante este proceso, el motor de enlace crea un objeto ValidationError y lo agrega a la colección Validation.Errors del elemento enlazado. Antes de que el motor de enlace ejecute los ValidationRule objetos en cualquiera de los pasos dados, elimina cualquier ValidationError que se haya agregado a la propiedad adjunta Validation.Errors del elemento al que se ha enlazado durante ese paso. Por ejemplo, si un ValidationRule cuya propiedad ValidationStep está establecida en UpdatedValue falla, la próxima vez que se produzca el proceso de validación, el motor de enlace elimina ese ValidationError inmediatamente antes de llamar a cualquier ValidationRule que tenga ValidationStep establecida en UpdatedValue.

Cuando Validation.Errors no está vacío, la Validation.HasError propiedad adjunta del elemento se establece en true. Además, si la NotifyOnValidationError propiedad de Binding se establece en true, el motor de enlace genera el evento adjunto Validation.Error en el elemento.

Tenga en cuenta también que una transferencia de valores válida en cualquier dirección (de destino a origen o de origen a destino) borra la Validation.Errors propiedad adjunta.

Si el enlace tiene asociado ExceptionValidationRule o la propiedad ValidatesOnExceptions se establece en true, y se produce una excepción cuando el motor de enlace configura el origen, el motor de enlace verifica si hay un UpdateSourceExceptionFilter. Puede usar la UpdateSourceExceptionFilter devolución de llamada para proporcionar un controlador personalizado para gestionar excepciones. Si no se especifica un UpdateSourceExceptionFilter en el Binding, el motor de enlace crea un ValidationError con la excepción y la agrega a la colección Validation.Errors del elemento enlazado.

Mecanismo de depuración

Puede establecer la propiedad PresentationTraceSources.TraceLevel adjunta en un objeto relacionado con el enlace para recibir información sobre el estado de un enlace específico.

Consulte también