Información general sobre el enlace de datos
El enlace de datos de Windows Presentation Foundation (WPF) proporciona un método simple y coherente para que las aplicaciones presenten e interactúen con datos. Los elementos se pueden enlazar a los datos de diversos orígenes de datos en forma de objetos common language runtime (CLR) y XML. Los objetos ContentControl como Button y los objetos ItemsControl como ListBox y ListView tienen funciones integradas que permiten definir de forma flexible elementos de datos individuales o colecciones de elementos de datos. A partir de estos datos se pueden generar vistas de ordenación, filtro y agrupación.
La funcionalidad de enlace de datos de WPF presenta varias ventajas con respecto a los modelos tradicionales, como un mayor número de propiedades que admiten de forma inherente el enlace de datos, una representación flexible de los datos en la UI y la separación bien definida de la lógica del negocio de la UI.
En este tema se describen en primer lugar los conceptos fundamentales del enlace de datos de WPF y, a continuación, se explica el uso de la clase Binding y otras características del enlace de datos.
Este tema contiene las secciones siguientes.
- ¿Qué es el enlace de datos?
- Conceptos básicos del enlace de datos
- Crear un enlace
- Conversión de datos
- Enlace a colecciones
- Inclusión de datos en plantillas
- Validación de datos
- Mecanismo de depuración
- Temas relacionados
¿Qué es el enlace de datos?
El enlace de datos es el proceso que establece una conexión entre la UI de la aplicación y la lógica del negocio. Si el enlace está correctamente configurado y los datos proporcionan las notificaciones adecuadas, cuando los datos cambian su valor, los elementos que están enlazados a ellos reflejan los cambios automáticamente. El enlace de datos también puede implicar la actualización automática de los datos que subyacen a una representación externa de los datos de un elemento, cuando esta representación cambia. Por ejemplo, si el usuario modifica el valor en un elemento TextBox, el valor de los datos subyacentes se actualiza automáticamente para reflejar ese cambio.
Un uso típico del enlace de datos es colocar los datos de configuración locales o de servidor en formularios o en otros controles de la UI. En WPF, este concepto se expande para incluir el enlace de un gran número de propiedades a una gran variedad de orígenes de datos. En WPF, las propiedades de dependencia de los elementos se pueden enlazar a objetos CLR (incluidos los objetos ADO.NET u objetos asociados a servicios Web y propiedades web) y datos XML.
Para obtener un ejemplo de enlace de datos, examine la siguiente UI de aplicación de Data Binding Demo:
El ejemplo anterior es la UI de una aplicación que muestra una lista de artículos subastados. La aplicación muestra las características siguientes de enlace de datos:
El contenido de ListBox se enlaza a una colección de objetos AuctionItem. Un objeto AuctionItem tiene propiedades como Descripción, StartPrice, StartDate, Categoría, SpecialFeatures, etc.
Los datos (objetos AuctionItem ) mostrados en ListBox se incluyen en una plantilla para mostrar la descripción y el precio actual de cada artículo. Para ello se utiliza un objeto DataTemplate. Asimismo, el aspecto de cada artículo depende del valor SpecialFeatures del elemento AuctionItem que se va a mostrar. Si el valor SpecialFeatures de AuctionItem es Color, el artículo tiene un borde azul. Si el valor es Highlight, el artículo tiene un borde naranja y una estrella. En la sección Incluir datos en plantillas se proporciona información sobre las plantillas de datos.
El usuario puede agrupar, filtrar u ordenar los datos mediante los controles CheckBox proporcionados. En la imagen anterior, los controles CheckBox "Group by category" y "Sort by category and date" están seleccionados. Es posible que haya observado que los datos se agrupan en función de la categoría del producto, y el nombre de las categorías se muestra en orden alfabético. Aunque no se aprecia muy bien en la imagen, los artículos están ordenados también por fecha de inicio dentro de cada categoría. Para ello se utiliza una vista de colección. En la sección Enlace a colecciones se describe este tipo de vistas.
Cuando el usuario selecciona un artículo, ContentControl muestra los detalles del artículo seleccionado. Esto recibe el nombre de escenario principal-detalle. En la sección Escenario principal-detalle se proporciona información sobre este tipo de escenario de enlace.
El tipo de la propiedad StartDate es DateTime, que devuelve una fecha que incluye el tiempo en milisegundos. En esta aplicación, se ha utilizado un convertidor personalizado para que se muestre una cadena de fecha más corta. En la sección Conversión de datos se proporciona información sobre los convertidores.
Cuando el usuario hace clic en el botón Add Product, se muestra el siguiente formulario:
El usuario puede modificar los campos del formulario, obtener una vista previa de la lista de productos mediante la vista previa abreviada y los paneles de vista previa más detallada y, a continuación, hacer clic en submit para agregar la nueva lista de productos. Todas las funciones de agrupación, filtrado y ordenación existentes se aplicarán a la nueva entrada. En este caso en concreto, el artículo especificado en la imagen anterior se mostrará como el segundo artículo dentro de la categoría Computer.
Lo que no se muestra en esta imagen es la lógica de validación proporcionada en el control TextBox Start Date. Si el usuario escribe una fecha no válida (con un formato no válido o una fecha pasada), se le notificará con un control ToolTip y un signo de exclamación de color rojo situado junto al control TextBox. En la sección Validación de datos se explica cómo crear lógica de validación.
Antes de abordar las diferentes características de enlace de datos citadas anteriormente, en la siguiente sección explicaremos los conceptos fundamentales imprescindibles para comprender el enlace de datos de WPF.
Conceptos básicos del enlace de datos
Este tema contiene las subsecciones siguientes.
- Dirección del flujo de datos
- Qué desencadena la actualización del origen
Independientemente del elemento que se vaya a enlazar y de la naturaleza del origen de datos, cada enlace sigue siempre el modelo que se muestra en la ilustración siguiente:
Como se muestra en la ilustración anterior, el enlace de datos es esencialmente el puente entre el destino del enlace y el origen del enlace. En la ilustración se muestran los siguientes conceptos fundamentales del enlace de datos de WPF:
Normalmente, cada enlace tiene estos cuatro componentes: un objeto de destino del enlace, una propiedad de destino, un origen del enlace y una ruta de acceso al valor en el origen del enlace que se va a usar. Por ejemplo, si desea enlazar el contenido de TextBox a la propiedad Nombre de un objeto Empleado, su objeto de destino es TextBox, la propiedad de destino es la propiedad Text, el valor que se va a utilizar es Nombre y el objeto de origen es el objeto Empleado.
La propiedad de destino debe ser una propiedad de dependencia. La mayoría de las propiedades UIElement son propiedades de dependencia y la mayoría de las propiedades de dependencia, excepto las de sólo lectura, admiten el enlace de datos de forma predeterminada. (Sólo los tipos DependencyObject pueden definir propiedades de dependencia y todos los UIElement se derivan de DependencyObject).
Aunque no se especifica en la figura, hay que destacar que el objeto de origen de enlace no está restringido a ser un objeto CLR personalizado. El enlace de datos de WPF admite datos en forma de objetos CLR y XML. Para proporcionar algunos ejemplos, el origen del enlace puede ser un objeto UIElement, cualquier objeto de lista, un objeto de CLR asociado a datos de ADO.NET o servicios Web, o bien un objeto XMLNode que contiene datos XML. Para obtener más información, vea Información general sobre orígenes de enlaces.
Cuando consulte otros temas del software development kit (SDK), es importante tener en cuenta que cuando establece un enlace, enlaza un destino del enlace a un origen del enlace. Por ejemplo, si va a mostrar algunos datos XML subyacentes en ListBox utilizando el enlace de datos, enlazará ListBox a los datos XML.
Para establecer un enlace, se utiliza el objeto Binding. En el resto de este tema se explican muchos de los conceptos asociados al objeto Binding y algunas de las propiedades y uso de este objeto
Dirección del flujo de datos
Como se mencionó anteriormente y como se indica por la flecha de la ilustración anterior, el flujo de datos de un enlace puede ir desde el destino del enlace al origen del enlace (por ejemplo, el valor de origen cambia cuando un usuario modifica el valor de TextBox) o desde el origen del enlace al destino del enlace (por ejemplo, el contenido de TextBox se actualiza con los cambios en el origen del enlace) si el origen del enlace proporciona las notificaciones correspondientes.
Tal vez desee que su aplicación permita que los usuarios cambien los datos y los propaguen al objeto de origen. O tal vez no desee permitir que los usuarios actualicen los datos de origen. Puede controlar este comportamiento estableciendo la propiedad Mode del objeto Binding. En la ilustración siguiente se muestran los tipos diferentes de flujo de datos:
El enlace OneWay permite 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 de nuevo a la propiedad de origen. Este tipo de enlace es adecuado si el control que se va a enlazar es de sólo lectura de forma implícita. Por ejemplo, podría enlazar a un origen como un tablero de cotizaciones o quizás su propiedad de destino no tenga ninguna interfaz de control para realizar modificaciones, como un color de fondo enlazado a datos de una tabla. Si no hay necesidad de supervisar los cambios de la propiedad de destino, con el modo de enlace OneWay evitará el trabajo adicional que supone usar el modo de enlace TwoWay.
El enlace TwoWay permite que los cambios realizados en la propiedad de origen o en la de destino se actualicen automáticamente en el otro. Este tipo de enlace es adecuado para formularios modificables u otros escenarios de UI totalmente interactivos. La mayoría de las propiedades tienen el enlace OneWay de forma predeterminada, pero algunas propiedades de dependencia (normalmente las propiedades de controles modificables por el usuario como la propiedad Text de TextBox y la propiedad IsChecked de CheckBox) tienen el enlace TwoWay de manera predeterminada. Una manera de determinar mediante programación si una propiedad de dependencia se enlaza de forma predeterminada de modo unidireccional o bidireccional es obtener los metadatos de la propiedad mediante GetMetadata y, a continuación, comprobar el valor booleano de la propiedad BindsTwoWayByDefault.
OneWayToSource es el enlace inverso de OneWay; actualiza la propiedad de origen cuando cambia la propiedad de destino. Podría utilizar este tipo de enlace si, por ejemplo, sólo necesita volver a evaluar el valor de origen de la UI.
El enlace OneTime, no mostrado en la ilustración, permite que la propiedad de origen inicialice la propiedad de destino, pero los demás cambios no se propagan. Esto significa que si el contexto de los datos sufre un cambio o el objeto del contexto de datos cambia, el cambio no se refleja en la propiedad de destino. Este tipo de enlace es adecuado si utiliza los datos allí donde es adecuado utilizar una captura del estado actual o cuando 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 no se conoce el contexto de los datos de antemano. Se trata básicamente de una forma más fácil de enlace OneWay que proporciona un mejor rendimiento en los casos en los que no cambia el valor de origen.
Observe que para detectar los cambios en el origen (aplicables a los enlaces OneWay y TwoWay), el origen debe implementar un mecanismo apropiado de notificación de cambios de propiedades, como INotifyPropertyChanged. Vea Cómo: Implementar la notificación de cambio de propiedad para obtener un ejemplo de implementación de INotifyPropertyChanged.
En la página de propiedades de Mode se proporciona más información sobre los modos de enlace y un ejemplo sobre cómo especificar la dirección de un enlace.
Qué desencadena la actualización del origen
Los enlaces TwoWay u OneWayToSource realizan escuchas para detectar los cambios en la propiedad de destino y los propagan de nuevo al origen. Esto se conoce como la actualización del origen. Por ejemplo, puede modificar el texto de un control TextBox para cambiar el valor de origen subyacente. Como se describe en la última sección, el valor de la propiedad Mode del enlace determina la dirección del flujo de datos.
Pero ¿se actualizará su valor de origen mientras modifica el texto o después de modificarlo y sacar el mouse fuera del control TextBox? La propiedad UpdateSourceTrigger del enlace determina qué desencadena la actualización del origen. Los puntos de las flechas derecha en la ilustración siguiente muestran la función de la propiedad UpdateSourceTrigger:
Si el valor de UpdateSourceTrigger es PropertyChanged, el valor al que apunta la fecha derecha del enlace TwoWay u OneWayToSource se actualizará en cuanto cambie la propiedad de destino. Sin embargo, si el valor de UpdateSourceTrigger es LostFocus, ese valor sólo se actualizará con el nuevo valor cuando la propiedad de destino pierda el foco.
Al igual que ocurre con la propiedad Mode, las diferentes propiedades de dependencia tienen valores de UpdateSourceTrigger predeterminados diferentes. El valor predeterminado de la mayoría de las propiedades de dependencia es PropertyChanged, mientras que la propiedad Text tiene un valor predeterminado de LostFocus. Esto significa que el origen se suele actualizar cuando la propiedad de destino cambia, lo que es adecuado para controles CheckBox y otros controles sencillos. Sin embargo, para los campos de texto, la actualización cada vez que se pulsa una tecla puede disminuir el rendimiento y deniega al usuario la oportunidad usual de retroceder y corregir los errores tipográficos antes confirmar el nuevo valor. Por ese motivo, la propiedad Text tiene un valor predeterminado de LostFocus en lugar de PropertyChanged.
Vea la página de propiedades de UpdateSourceTrigger para obtener información sobre cómo determinar el valor de UpdateSourceTrigger predeterminado de una propiedad de dependencia.
En la tabla siguiente se proporciona un escenario de ejemplo para cada valor de UpdateSourceTrigger utilizando TextBox como ejemplo:
Valor UpdateSourceTrigger |
Cuándo se actualiza el valor de origen |
Escenario de ejemplo de TextBox |
---|---|---|
LostFocus (valor predeterminado para TextBox.Text) |
Cuando el control TextBox pierde el foco |
TextBox asociado a lógica de validación (vea la sección Datos de validación) |
PropertyChanged |
Cuando escribe en el control TextBox |
Controles TextBox en una ventana de chat |
Explicit |
Cuando la aplicación llama a UpdateSource |
Controles TextBox en un formulario modificable (sólo actualiza los valores de origen cuando el usuario hace clic en el botón de envío) |
Para obtener un ejemplo, vea Cómo: Controlar cuándo el texto de TextBox actualiza el origen.
Crear un enlace
Este tema contiene las subsecciones siguientes.
- Especificar el origen del enlace
- Especificar la ruta de acceso al valor
- Binding y BindingExpression
Como recapitulación de algunos de los conceptos descritos en las secciones anteriores, recordemos que un enlace se establece mediante el objeto Binding y que cada enlace tiene normalmente cuatro componentes: el destino del enlace, la propiedad de destino, el origen del enlace y una ruta de acceso al valor de origen que se va a utilizar. En esta sección se explica cómo configurar un enlace.
Considere el ejemplo siguiente, en el que el objeto de origen del enlace es una clase denominada MyData que se define en el espacio de nombres SDKSample. En esta demostración, la clase MyData tiene una propiedad de cadena denominada ColorName, cuyo valor se establece en "Red". Por tanto, este ejemplo genera un botón con un fondo rojo.
<DockPanel
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample">
<DockPanel.Resources>
<c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<DockPanel.DataContext>
<Binding Source="{StaticResource myDataSource}"/>
</DockPanel.DataContext>
<Button Background="{Binding Path=ColorName}"
Width="150" Height="30">I am bound to be RED!</Button>
</DockPanel>
Para obtener información más detallada sobre la sintaxis de declaración de enlaces y ejemplos sobre cómo configurar un enlace en código, vea Información general sobre declaraciones de enlaces.
Si aplicamos este ejemplo a nuestro diagrama básico, la ilustración resultante tendrá el siguiente aspecto. Se trata de un enlace OneWay porque la propiedad Background admite el enlace OneWay de forma predeterminada.
Tal vez se pregunte por qué esto funciona si la propiedad ColorName es de tipo string mientras que la propiedad Background es de tipo Brush. Se ha aplicado la conversión predeterminada de tipos, que se explica en la sección Conversión de datos.
Especificar el origen del enlace
Observe que en el ejemplo anterior, el origen del enlace se especifica estableciendo la propiedad DataContext en el elemento DockPanel. A continuación, Button hereda el valor DataContext de DockPanel, que es su elemento primario. Recordemos que el objeto de origen del enlace es uno de los cuatro componentes necesarios de un enlace. Por tanto, si no se especifica el objeto de origen del enlace, el enlace no funcionará.
Hay varias formas de especificar el objeto de origen del enlace. Utilizar la propiedad DataContext en un elemento primario es útil si va a enlazar varias propiedades al mismo origen. Sin embargo, a veces puede ser más adecuado especificar el origen del enlace en declaraciones de enlace individuales. En el ejemplo anterior, en lugar de utilizar la propiedad DataContext, puede especificar el origen del enlace estableciendo directamente la propiedad Source en la declaración de enlace del botón, como en el ejemplo siguiente:
<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>
Además de establecer directamente la propiedad DataContext en un elemento, heredar el valor de DataContext de un antecesor (como el botón del primer ejemplo) y especificar explícitamente el origen del enlace estableciendo la propiedad Source en Binding (como el botón del último ejemplo), también puede utilizar la propiedad ElementName o la propiedad RelativeSource para especificar el origen del enlace. La propiedad ElementName es útil cuando se enlaza a otros elementos de la aplicación como, por ejemplo, cuando se utiliza un control deslizante para ajustar el ancho de un botón. La propiedad RelativeSource es útil cuando el enlace se especifica en ControlTemplate o Style. Para obtener más información, vea Cómo: Especificar el origen de enlace.
Especificar la ruta de acceso al valor
Si su origen del enlace es un objeto, utiliza la propiedad Path para especificar el valor que se va a usar para el enlace. Si va a enlazar a datos XML, utiliza la propiedad XPath para especificar el valor. En algunos casos, puede ser pertinente usar la propiedad Path aunque los datos sean XML. Por ejemplo, si desea tener acceso a la propiedad Name de un XMLNode devuelto (como resultado de una consulta XPath), debe utilizar la propiedad Path además de la propiedad XPath.
Para obtener información sobre la sintaxis y algunos ejemplos, vea las páginas de propiedades de Path y XPath.
Observe que aunque hemos recalcado que el Path al valor que se va a utilizar es uno de los cuatro componentes necesarios de un enlace, en los escenarios en los que se enlaza a un objeto completo, el valor que se utiliza será el mismo que el objeto de origen del enlace. En esos casos, se puede no especificar un Path. Considere el ejemplo siguiente:
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="true"/>
En el ejemplo anterior se utiliza la sintaxis de enlace vacía: {Binding}. En este caso, ListBox hereda el objeto DataContext de un elemento DockPanel principal (no mostrado en este ejemplo). Cuando no se especifica la ruta de acceso, el comportamiento predeterminado es enlazar al objeto completo. Es decir, en este ejemplo, la ruta de acceso se ha omitido porque estamos enlazando la propiedad ItemsSource al objeto completo. (Vea la sección Enlace a colecciones para obtener información detallada).
Además del enlace a una colección, este escenario es útil también cuando se desea enlazar a un objeto completo en lugar de simplemente a una propiedad individual de un objeto. Por ejemplo, si el objeto de origen es de tipo string y simplemente desea enlazar a la propia cadena. Otro escenario común es cuando se desea enlazar un elemento a un objeto con varias propiedades.
Observe que puede ser necesario aplicar lógica personalizada para que los datos sean significativos para la propiedad de destino enlazada. La lógica personalizada puede consistir en un convertidor personalizado (si no existe la conversión de tipos predeterminada). Vea Conversión de datos para obtener información sobre los convertidores.
Binding y BindingExpression
Antes de explicar otras características y usos del enlace de datos, es conveniente presentar la clase BindingExpression. Como ha visto en las secciones anteriores, la clase Binding es la clase de alto nivel para la declaración de un enlace; la clase Binding 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. Una clase BindingExpression es una expresión de instancia que no se puede compartir y que contiene toda la información de las instancias de la clase Binding.
Considere, por ejemplo, lo siguiente, donde myDataObject es una instancia de la clase MyData, myBinding es el objeto Binding de origen y la clase MyData es una clase definida que contiene una propiedad de cadena denominada MyDataProperty. En este ejemplo se enlaza el contenido de texto de mytext, una instancia de TextBlock, a MyDataProperty.
Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
//make a new source
MyData myDataObject = new MyData(DateTime.Now);
Binding myBinding = new Binding("MyDataProperty");
myBinding.Source = myDataObject;
myText.SetBinding(TextBlock.TextProperty, myBinding);
Puede utilizar el mismo objeto myBinding para crear otros enlaces. Por ejemplo, podría utilizar el objeto myBinding para enlazar el contenido de texto de una casilla a MyDataProperty. En ese escenario, habrá dos instancias de BindingExpression que comparten el objeto myBinding.
Un objeto BindingExpression se puede obtener a través del valor devuelto por la llamada a GetBindingExpression en un objeto enlazado a datos. En los temas siguientes se muestran algunos de los usos de la clase BindingExpression:
Cómo: Obtener el objeto de enlace a partir de una propiedad de destino enlazada
Cómo: Controlar cuándo el texto de TextBox actualiza el origen
Conversión de datos
En el ejemplo anterior, el botón es rojo porque su propiedad Background está enlazada a una propiedad de cadena con el valor "Red". Esto funciona porque hay un convertidor de tipos en el tipo Brush para convertir el valor de cadena en Brush.
Al agregar esta información a la ilustración de la sección Crear un enlace, obtenemos el siguiente diagrama:
Pero ¿y si en lugar de una propiedad de tipo string, el objeto de origen del enlace tiene una propiedad Color de tipo Color? En ese caso, para que el enlace funcione tendrá que convertir el valor de la propiedad Color en algún valor que la propiedad Background acepte. Tendrá que crear un convertidor personalizado implementando la interfaz IValueConverter, como en el ejemplo siguiente:
<ValueConversion(GetType(Color), GetType(SolidColorBrush))>
Public Class ColorBrushConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Dim color As Color = CType(value, Color)
Return New SolidColorBrush(color)
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Return Nothing
End Function
End Class
[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Color color = (Color)value;
return new SolidColorBrush(color);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
En la página de referencia de IValueConverter encontrará más información al respecto.
Ahora que se ha utilizado el convertidor personalizado en lugar de la conversión predeterminada, nuestro diagrama presenta el siguiente aspecto:
Recordemos que las conversiones predeterminadas pueden estar disponibles si el tipo que se va a enlazar contiene convertidores de tipo. Este comportamiento dependerá de los convertidores de tipos disponibles en el destino. Si no está seguro, cree su propio convertidor.
A continuación, se incluyen algunos escenarios típicos en los que sería lógico implementar un convertidor de datos:
Los datos se muestran de forma diferente, dependiendo de la referencia cultural. Por ejemplo, tal vez desee implementar un convertidor de monedas o un convertidor de fechas y horas del calendario en función de los valores o normas utilizados en una determinada referencia cultural.
Los datos que se utilizan no están diseñados necesariamente para cambiar el valor textual de una propiedad, sino para cambiar otro valor, como el origen de una imagen, o el color o estilo del texto que se va a mostrar. En este caso se pueden utilizar convertidores para convertir el enlace de una propiedad que tal vez no sea adecuada, como el enlace de un campo de texto a la propiedad Background de una celda de tabla.
Hay varios controles o varias propiedades de controles enlazados a los mismos datos. En ese caso, el enlace principal podría mostrar simplemente el texto, mientras que los otros enlaces controlan los aspectos de presentación, pero se sigue utilizando el mismo enlace como información de origen.
Hasta el momento, no hemos proporcionado aún una descripción del enlace MultiBinding, en el que una propiedad de destino tiene una colección de enlaces. En el caso de un enlace MultiBinding, se utiliza un IMultiValueConverter personalizado para generar un valor final a partir de los valores de los enlaces. Por ejemplo, el color podría calcularse a partir de los valores de rojo, azul y verde, que pueden ser valores de los mismos o de diferentes objetos de origen del enlace. Vea la página de la clase MultiBinding para obtener información y ejemplos al respecto.
Enlace a colecciones
Este tema contiene las subsecciones siguientes.
- Cómo implementar colecciones
- Vistas de colección
Un objeto de origen del enlace se puede tratar como un objeto único cuyas propiedades contienen los datos, o como una recolección de datos de objetos polimórficos que suelen estar agrupados (como el resultado de una consulta a una base de datos). Hasta ahora sólo hemos explicado el enlace a objetos individuales, pero el enlace a una recolección de datos es un escenario común. Por ejemplo, es habitual utilizar un ItemsControl como ListBox, ListView o TreeView para mostrar una recolecció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 aún sigue siendo válido. Si enlaza un ItemsControl a una colección, el diagrama tendrá el siguiente aspecto:
Como se muestra en este diagrama, para enlazar ItemsControl a un objeto de colección, la propiedad que se utiliza es ItemsSource. Puede considerar la propiedad ItemsSource como el contenido del ItemsControl. Observe que el enlace es OneWay porque la propiedad ItemsSource admite el enlace OneWay de forma predeterminada.
Cómo implementar colecciones
Es posible enumerar cualquier colección que implementa la interfaz IEnumerable. Sin embargo, para configurar enlaces dinámicos de modo que las inserciones o eliminaciones que se realicen en la colección actualicen la UI de forma automática, la colección debe implementar la interfaz INotifyCollectionChanged. Esta interfaz expone un evento que debe provocarse siempre que se realicen cambios en la colección subyacente.
WPF proporciona la clase ObservableCollection<T>, que es una implementación integrada de una recolección de datos que expone la interfaz INotifyCollectionChanged. Observe que para permitir totalmente la transferencia de valores de datos de los objetos de origen a los destinos, cada objeto de la colección que admite propiedades enlazables debe implementar también la interfaz INotifyPropertyChanged. Para obtener más información, vea Información general sobre orígenes de enlaces.
Antes de implementar su propia colección, considere la posibilidad de utilizar ObservableCollection<T> o una de las clases de colección existentes, como List<T>, Collection<T> y BindingList<T>, entre otras muchas. Si cuenta con un escenario avanzado y desea implementar su propia colección, considere la posibilidad de utilizar IList, que proporciona una colección no genérica de objetos a los que se puede obtener acceso individualmente por índice y, por consiguiente, proporciona el máximo rendimiento.
Vistas de colección
Una vez que ItemsControl esté enlazado a una recolección de datos, quizás desee ordenar, filtrar o agrupar los datos. Para ello, se utilizan vistas de colección, que son clases que implementan la interfaz ICollectionView.
Este tema contiene las subsecciones siguientes.
- ¿Qué son las vistas de colección?
- Cómo crear una vista
- Ordenar
- Filtrar
- Grupo
- Punteros de elemento actual
- Escenario de enlace principal-detalle
¿Qué son las vistas de colección?
Una vista de colección es un nivel situado encima de la colección de origen del enlace, que le permite navegar y mostrar la colección de origen en función de las consultas de ordenación, filtrado y agrupación, sin tener que cambiar la propia colección de origen subyacente. Una vista de colección también contiene un puntero al elemento actual de la colección. Si la colección de origen implementa la interfaz INotifyCollectionChanged, los cambios provocados por el evento CollectionChanged se propagarán a las vistas.
Dado que las vistas no cambian las colecciones de origen subyacente, cada colección de origen puede tener varias vistas asociadas. Por ejemplo, podría tener una colección de objetos Task. El uso de vistas le permite mostrar los mismos datos de formas diferentes. Por ejemplo, en el lado izquierdo de la página es posible que desee mostrar las tareas ordenadas por prioridad y, en el lado derecho, agrupadas por área.
Cómo crear una vista
Una manera de crear y utilizar una vista es crear directamente una instancia del objeto de vista y utilizar a continuación esa instancia como el origen del enlace. Por ejemplo, considere la aplicación Data Binding Demo que se muestra en la sección ¿Qué es el enlace de datos? La aplicación se implementa de forma que ListBox se enlaza a una vista a través de la recolección de datos en lugar de la colección de datos directamente. El ejemplo siguiente se ha extraído de la aplicación Data Binding Demo. La clase CollectionViewSource es el proxy de Extensible Application Markup Language (XAML) de una clase que hereda de CollectionView. En este ejemplo en concreto, la propiedad 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 el origen del 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}}">
...
</ListBox>
Para crear otra vista para la misma colección, puede crear otra instancia de CollectionViewSource y asignarle un nombre x:Key diferente.
En la tabla siguiente se muestra qué tipos de datos de vista se crean como vista de colección predeterminada o por CollectionViewSource en función del tipo de colección de origen.
Tipo de colección de origen |
Tipo de vista de colección |
Notas |
---|---|---|
Un tipo interno basado en CollectionView |
No se pueden agrupar los elementos. |
|
Más rápido. |
||
Utilizar una vista predeterminada
Especificar una vista de colección como origen de enlace es una forma de crear y utilizar una vista de colección. WPF también crea una vista de colección predeterminada para cada colección utilizada como origen de enlace. Si enlaza directamente a una colección, WPF enlaza a su vista predeterminada. Tenga en cuenta que todos los enlaces a una misma colección comparten esta vista predeterminada, de modo que si se realiza un cambio en una vista predeterminada a través de un control enlazado o mediante código (como un cambio de ordenación o en el puntero de elemento actual, que se describe más adelante), éste se refleja en el resto de los enlaces a la misma colección.
Para obtener la vista predeterminada, se utiliza el método GetDefaultView. Para obtener un ejemplo, vea Cómo: Obtener la vista predeterminada de una recolección de datos.
Vistas de colección con tablas de datos de ADO.NET
Para mejorar el rendimiento, las vistas de colección para objetos DataTable o DataView de ADO.NET delegan la ordenación y el filtrado a DataView. Esto hace que todas las vistas de colección del origen de datos compartan la ordenación y el filtrado. Para habilitar la ordenación y el filtrado independientes de cada vista de colección, inicialice cada vista de este tipo con su propio objeto DataView.
Ordenar
Como se mencionó anteriormente, las vistas pueden aplicar un criterio de ordenación a una colección. Cuando este criterio existe en la colección subyacente, los datos pueden o no tener un orden relevante inherente. La vista de la colección le permite aplicar un orden o cambiar el orden predeterminado, en función de los criterios de comparación especificados. Como es una vista de datos basada en un cliente, podría ser habitual que el usuario quisiera ordenar las columnas de los datos de tabla por el valor correspondiente a la columna. Con las vistas, se puede aplicar esta ordenación controlada por el usuario, sin tener que realizar ningún cambio en la colección subyacente ni tener tampoco que volver a consultar el contenido de la colección. Para obtener un ejemplo, vea Cómo: Ordenar una columna de GridView cuando se hace clic en un encabezado.
En el siguiente ejemplo se muestra la lógica de ordenación del control CheckBox "Sort by category and date" de la UI de aplicación de la sección ¿Qué es el enlace de datos?:
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));
}
Filtrar
Las vistas pueden aplicar también un filtro a una colección. Esto significa que aunque un elemento pueda existir en la colección, esta vista en concreto está destinada a mostrar únicamente determinado subconjunto de la colección completa. Podría filtrar los datos en función de una condición. Por ejemplo, como ocurre en la aplicación de la sección ¿Qué es el enlace de datos?, el control CheckBox "Show only bargains" contiene lógica para filtrar los artículos con un costo de 25 dólares o más. El código siguiente se ejecuta para establecer ShowOnlyBargainsFilter como el controlador de eventos Filter cuando se selecciona CheckBox:
listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);
El controlador de eventos ShowOnlyBargainsFilter se implementa del modo siguiente:
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;
}
}
}
Si utiliza directamente alguna de las clases CollectionView en lugar de CollectionViewSource, deberá usar la propiedad Filter para especificar una devolución de llamada. Para obtener un ejemplo, vea Cómo: Filtrar datos en una vista.
Grupo
Salvo la clase interna que ve una colección IEnumerable, todas las vistas de colección admiten la funcionalidad de agrupación, que permite al usuario dividir 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 en función de los datos.
En e ejemplo siguiente se muestra la lógica del control CheckBox "Group by category":
// This groups the items in the view by the property "Category"
PropertyGroupDescription groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
Para obtener otro ejemplo de agrupación, vea Cómo: Agrupar elementos en un control ListView que implementa un modo GridView.
Punteros de elemento actual
Las vistas admiten también la noción de elemento actual. Puede navegar por los objetos en una vista de colección. A medida que navega por los objetos, mueve un puntero de elemento que le permite recuperar el objeto ubicado concretamente en esa posición en la colección. Para obtener un ejemplo, vea Cómo: Navegar por los objetos de una colección de datos mediante CollectionView.
Dado que WPF sólo se enlaza a una colección mediante una vista (una vista especificada por el usuario o la vista predeterminada de la colección), todos los enlaces a las colecciones tienen un puntero de elemento actual. Al enlazar a una vista, el carácter de barra diagonal ("/") de 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 enlaza a la colección. La segunda línea enlaza al elemento actual de la colección. La tercera línea enlaza a la propiedad Description del elemento actual de la colección.
<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />
La sintaxis de barra diagonal y propiedad también puede apilarse 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/}" />
Toda ordenación o filtrado que se aplique a la colección puede afectar al puntero del elemento actual. La ordenación conserva el puntero del elemento actual en el último elemento seleccionado, pero se reestructura la vista de colección a su alrededor. (Quizás el elemento seleccionado estaba antes al principio de la lista, pero ahora puede que esté en alguna parte del medio). El filtrado conserva el elemento seleccionado si la selección permanece en la vista después del filtrado. De lo contrario, el puntero del elemento actual se establece en el primer elemento de la vista de colección filtrada.
Escenario de enlace principal-detalle
La noción de elemento actual no es sólo útil para la navegación de elementos en una colección, sino también para el escenario de enlace principal-detalle. Considere de nuevo la UI de aplicación de la sección ¿Qué es el enlace de datos? En esa aplicación, la selección dentro de ListBoxdetermina el contenido mostrado en ContentControl. O dicho de otra forma, cuando se selecciona un elemento ListBox, el control ContentControl muestra los detalles del elemento seleccionado.
Puede implementar el escenario principal-detalle simplemente con dos o más controles enlazados a la misma vista. El ejemplo siguiente de Data Binding Demo muestra el marcado de los controles ListBox y ContentControl que ve en la UI de aplicación de 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}}">
...
</ListBox>
...
<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 (vea la definición de este recurso en la sección Cómo crear una vista). Esto funciona porque cuando un objeto singleton (el control ContentControl en este caso) está enlazado a una vista de colección, se enlaza automáticamente a la propiedad CurrentItem de la vista. Observe que los objetos CollectionViewSource 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, tendrá que establecer su propiedad IsSynchronizedWithCurrentItem en true para que funcione.
Para obtener otros ejemplos, vea Cómo: Enlazar a una colección y mostrar información basada en la selección y Cómo: Usar el patrón principal-detalle con datos jerárquicos.
Tal vez haya observado que en el ejemplo anterior se utiliza una plantilla. De hecho, los datos no se mostrarían de la forma deseada sin el uso de plantillas (la utilizada explícitamente por el control ContentControl y la utilizada implícitamente por el control ListBox). Trataremos el tema de la inclusión de datos en plantillas en la siguiente sección.
Inclusión de datos en plantillas
Sin el uso de plantillas de datos, nuestra UI de aplicación de la sección ¿Qué es el enlace de datos? tendría el aspecto siguiente:
Como se muestra en el ejemplo de la sección anterior, tanto el control ListBox como 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 de cómo mostrar la recolección de datos, el control ListBox muestra una representación de cadena de cada objeto de la colección subyacente y el control ContentControl muestra una representación de cadena del objeto al que está enlazado.
Para resolver ese problema, la aplicación define objetos DataTemplate. Como se muestra en el ejemplo de la sección anterior, ContentControl usa explícitamente el DataTemplate detailsProductListingTemplate. El control ListBox utiliza implícitamente el objeto DataTemplate siguiente 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 estos dos objetos DataTemplate, la interfaz de usuario resultante es la que se muestra en la sección ¿Qué es el enlace de datos? Como puede ver en esa captura de pantalla, además de permitir colocar los datos en los controles, los objetos DataTemplate le permiten definir un aspecto atractivo para sus datos. Por ejemplo, se utilizan objetos DataTrigger en el objeto DataTemplate anterior para que los elementos AuctionItem con el valor SpecialFeatures de HighLight se muestren con un borde naranja y una estrella.
Para obtener más información sobre las plantillas de datos, vea Información general sobre plantillas de datos.
Validación de datos
Este tema contiene las subsecciones siguientes.
- Asociar reglas de validación a un enlace
- Proporcionar comentarios visuales
- Proceso de validación
La mayoría de las aplicaciones que toman datos proporcionados por el usuario requieren lógica de validación para asegurarse de que el usuario ha escrito la información esperada. Las comprobaciones de validación pueden basarse en el tipo, intervalo, 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.
Asociar reglas de validación a un enlace
El modelo de enlace de datos de WPF permite asociar ValidationRules al objeto Binding. Por ejemplo, en el ejemplo siguiente se enlaza un control TextBox a una propiedad denominada StartPrice y se agrega un objeto ExceptionValidationRule a la propiedad Binding.ValidationRules.
<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 objeto ValidationRule comprueba si el valor de una propiedad es válido. WPF tiene los dos tipos siguientes de objetos ValidationRule integrados:
ExceptionValidationRule comprueba las excepciones producidas durante la actualización de la propiedad de origen de enlace. En el ejemplo anterior, StartPrice es de tipo entero. Cuando el usuario especifica un valor que no se puede convertir en un entero, se produce una excepción, lo que ocasiona que el enlace se marque como no válido. Una sintaxis alternativa para establecer explícitamente ExceptionValidationRule es establecer la propiedad ValidatesOnExceptions en true en el objeto Binding o MultiBinding.
Un objeto DataErrorValidationRule comprueba los errores producidos por objetos que implementan la interfaz IDataErrorInfo. Para obtener un ejemplo del uso de esta regla de validación, vea DataErrorValidationRule. Una sintaxis alternativa para establecer explícitamente DataErrorValidationRule es establecer la propiedad ValidatesOnDataErrors en true en el objeto Binding o MultiBinding.
Puede crear también su propia regla de validación derivando de la clase ValidationRule e implementando el método Validate. En el ejemplo siguiente se muestra la regla utilizada por el control TextBox Add Product Listing "Start Date" de la sección ¿Qué es un enlace de datos?:
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;
}
}
}
StartDateEntryFormTextBoxutiliza FutureDateRule, como se muestra en el ejemplo siguiente:
<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>
Observe que como el valor de UpdateSourceTrigger es PropertyChanged, el motor de enlace actualiza el valor de origen cada vez que se pulsa una tecla, lo que significa que también comprueba cada regla de la colección ValidationRules cada vez que se pulsa una tecla. Ofreceremos una descripción más detallada en la sección Proceso de validación.
Proporcionar comentarios visuales
Si el usuario especifica un valor no válido, quizás desee proporcionar algunos comentarios sobre el error en la UI de la aplicación. Una manera de proporcionar esos comentarios es establecer la propiedad adjunta Validation.ErrorTemplate en un objeto ControlTemplate personalizado. Como se muestra en el subapartado anterior, el control TextBox StartDateEntryForm utiliza una propiedad ErrorTemplate denominada 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 elemento AdornedElementPlaceholder especifica dónde se debe colocar el control que se va a adornar.
Asimismo, podría utilizar un objeto ToolTip para mostrar el mensaje de error. Los controles TextBox StartDateEntryForm y StartPriceEntryForm utilizan el estilo textStyleTextBox, que crea un objeto ToolTip que muestra el mensaje de error. En el ejemplo siguiente se muestra la definición de textStyleTextBox. La propiedad adjunta Validation.HasError es true cuando uno o varios de los enlaces de las propiedades del elemento enlazado producen un 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 RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Con la propiedad ErrorTemplate personalizada y el objeto ToolTip, el control TextBox StartDateEntryForm adopta el siguiente aspecto cuando se produce un error de validación:
Si el objeto Binding tiene reglas de validación asociadas, pero no especifica ErrorTemplate en el control enlazado, se utilizará una propiedad ErrorTemplate predeterminada para informar a los usuarios de que se ha producido un error de validación. La propiedad ErrorTemplate predeterminada es una plantilla de control que define un borde rojo en la capa de adorno. Con la propiedad ErrorTemplate predeterminada y el objeto ToolTip, la UI del control TextBox StartPriceEntryForm adopta el siguiente aspecto cuando se produce un error de validación:
Para ver un ejemplo sobre 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 Información general sobre cuadros de diálogo.
Proceso de validación
La validación normalmente se produce cuando el valor de un destino se transfiere a la propiedad de origen del enlace. Esto se produce en los enlaces TwoWay y OneWayToSource. Como ya se ha comentado, lo que causa una actualización del origen depende del valor de la propiedad UpdateSourceTrigger, como se describe en la sección Qué desencadena la actualización del origen.
A continuación, se describe el proceso de validación: Tenga en cuenta que si se produce un error de validación o de cualquier otro tipo en cualquier momento del proceso, éste se detiene.
El motor de enlace comprueba si se ha definido algún objeto ValidationRule cuya propiedad ValidationStep se haya establecido en RawProposedValue para dicho objeto Binding, en cuyo caso llama al método Validate en cada ValidationRule hasta que una de las reglas de validación detecte un error o hasta que se cumplan todas las reglas de validación.
A continuación, el motor de enlace llama al convertidor, si existe alguno.
Si el convertidor funciona correctamente, el motor de enlace comprueba si se ha definido algún objeto ValidationRule cuya propiedad ValidationStep se haya establecido en ConvertedProposedValue para dicho objeto Binding, en cuyo caso llama al método Validate en cada ValidationRule que tenga la propiedad ValidationStep establecida en ConvertedProposedValue hasta que una de las reglas de validación detecte un error o hasta que se cumplan todas las reglas de validación.
El motor de enlace establece la propiedad de origen.
El motor de enlace comprueba si se ha definido algún objeto ValidationRule personalizado cuya propiedad ValidationStep se haya establecido en UpdatedValue para dicho objeto Binding, en cuyo caso llama al método Validate en cada ValidationRule que tenga la propiedad ValidationStep establecida en UpdatedValue hasta que una de las reglas de validación detecte un error o hasta que se cumplan todas las reglas de validación. Si un objeto DataErrorValidationRule está asociado a un enlace y su propiedad ValidationStep está establecida en el valor predeterminado, UpdatedValue, en este momento se comprueba DataErrorValidationRule. Este también es el momento en que se comprueban los enlaces que tienen la propiedad ValidatesOnDataErrors establecida en true.
El motor de enlace comprueba si se ha definido algún objeto ValidationRule personalizado cuya propiedad ValidationStep se haya establecido en CommittedValue para dicho objeto Binding, en cuyo caso llama al método Validate en cada ValidationRule que tenga la propiedad ValidationStep establecida en CommittedValue hasta que una de las reglas de validación detecte un error o hasta que se cumplan todas las reglas de validación.
Si un objeto ValidationRule no pasa en ningún momento por 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 objetos ValidationRule en un paso determinado, éste quita cualquier objeto ValidationError que se haya agregado a la propiedad adjunta Validation.Errors del elemento enlazado durante dicho paso. Por ejemplo, si se produce un error de un objeto ValidationRule cuya propiedad ValidationStep se ha establecido en UpdatedValue, la próxima vez que se realice el proceso de validación, el motor de enlace quitará dicho objeto ValidationError inmediatamente antes de llamar a cualquier objeto ValidationRule que tenga la propiedad ValidationStep establecida en UpdatedValue.
Cuando la propiedad Validation.Errors no está vacía, la propiedad adjunta Validation.HasError del elemento se establece en true. Además, si la propiedad NotifyOnValidationError de Binding se establece en true, el motor de enlace provoca el evento adjunto Validation.Error en el elemento.
Asimismo, observe que una transferencia de valor válida en cualquiera de las dos direcciones (del destino al origen o del origen al destino) borra la propiedad adjunta Validation.Errors.
Si el enlace tiene un objeto ExceptionValidationRule asociado, o tiene la propiedad ValidatesOnExceptions establecida en true, y se produce una excepción cuando el motor de enlace establece el origen, dicho motor comprueba si hay una propiedad UpdateSourceExceptionFilter. Puede usar la devolución de llamada de UpdateSourceExceptionFilter para proporcionar un controlador personalizado que controle las excepciones. Si no se especifica una propiedad UpdateSourceExceptionFilter en el objeto Binding, el motor de enlaces crea un objeto ValidationError con la excepción y lo agrega a la colección Validation.Errors del elemento enlazado.
Mecanismo de depuración
Puede establecer la propiedad adjunta PresentationTraceSources.TraceLevel en un objeto relacionado con el enlace para recibir información sobre el estado de un enlace específico.
Vea también
Tareas
Cómo: Enlazar a los resultados de una consulta LINQ
Cómo: Enlazar a un origen de datos ADO.NET
Referencia
Conceptos
Optimizar el rendimiento: Enlace de datos