Artículo
|
DataBinding |
El propósito de la mayoría de las aplicaciones que hacemos es presentar datos, y permitir que los usuarios los modifiquen teniendo que realizar las operaciones de: ordenar, filtrar, convertir, relacionarlos…
WPF incorpora un motor de Databinding que es una de las partes más potentes de WPF, no hay limitaciones en cuanto a lo que puedes hacer un Databinding.
Podríamos realizar las tareas anteriores sin DataBinding pero con mucho mas código, por ejemplo para la ventana.
El XAML seria
El codigo de la clase Person
En el code-behind de la ventana
El código crea un objeto persona, y lo usa para inicializar los campos de la ventana. Cuando se presiona el botón del cumpleaños, la persona se modifica, y los datos se muestran en un message box.
Es un ejemplo bastante sencillo y fácil de implementar, si ahora quisiéramos reflejar los cambios en la ventana principal.
Parece una forma sencilla de mejorarla, pero tiene un problema No escala bien a medida que la aplicación se hace mas complicada, requiriendo cada vez mas de estas líneas de código
Cambios en los objetos
Una forma mas robusta para que la UI lleve registro de los cambios, es que el objeto avise cuando una propiedad cambia, por ejemplo, lanzando un evento La forma correcta de hacer esto, es que el objeto implemente INotifyPropertyChanged
Cuando la propiedad Age de la persona cambia, debida a la acción del handler de la ventana, el objeto persona hace un raise del evento PropertyChanged Podríamos manualmente capturar este evento, colocar un handler y reaccionar ante el mismo, como hacemos con cualquier otro handler…
Con el código anterior, ocurre una cascada de eventos que hacen que el handler del botón del cumpleaños, no tenga que actualizar la UI Gráficamente, se puede apreciar en el siguiente diagrama este flujo de eventos…
Sin embargo, hemos resuelto parte del problema Aun tenemos que actualizar manualmente en el handler del cambio de propiedad, la interfaz grafica
Pero tenemos otro problema…
Necesitamos también un mecanismo que detecte los cambios en la interfaz grafica, y los propague hacia el objeto. En el ejemplo anterior, si cambiamos el nombre y presionamos el botón Birthday, puede pasarnos esto…
El TextBox de Name tiene un valor diferente al que se muestra en el MessageBox, para solucionar esto debemos agregar el siguiente código
Sin importar entonces en donde se de el cambio (objeto ,UI), ambos elementos se mantienen sincronizados.in embargo, a pesar de que tenemos lo que buscábamos, tuvimos que escribir bastante código.
Claramente, la cantidad de código a escribir se nos va de las manos, si la cantidad de objetos, propiedades de los objetos y eventos aumenta. emás, este tipo de tareas parecen bastante repetitivas. ahí que WPF las abstrae en funcionalidades del framework, dándole el nombre de Data Binding.
Hasta Ahora hemos visto lo que teníamos que hacer para realizar sincronización de los datos con la interfaz de usuario sin Databinding, a partir de ahora vamos a ver el DataBinding
Consiste en registrar un par de propiedades, una de ellas en un objeto (Person.Name), la otra en la interfaz grafica (Textbox.Text), con el motor de Data Binding Este motor es el que se encarga de mantener ambas sincronizadas, convirtiendo los tipos de datos según sea necesario.
Para registrar ambas propiedades (tanto del elemento, que seria la clase persona como del objeto que seria el Textbox), utilizamos el objeto Binding
Todas las siguientes son equivalentes…
es equivalente a…
En general, podemos pensar que Path es la ruta para acceder a una propiedad dentro del objeto fuente de la información. En el caso anterior, el binding se da entre el TextBox (propiedad Text) y la propiedad Age de un objeto que será determinado luego.
En el caso anterior, el textbox actúa como binding target , consumiendo los cambios binding source. El binding target puede ser cualquier elemento WPF, pero solo podemos hacer binding contra las dependency properties. El binding source puede ser cualquier propiedad de cualquier elemento WPF.En este caso, el binding source puede especificarse en runtime Sin embargo, en general, el binding provendrá de un Data Context.
Un DataContext es el lugar en el que los bindings buscan por su data source si no se les ha dado ninguna instrucción especial. En WPF todo FrameworkElement y todoFrameworkContentElement tienen una propiedad DataContext, y recordamos el grafico del post donde veíamos que todos los controles derivan de FrameWorkElement
El DataContext es de tipo Object, por lo que podemos colocar cualquier cosa que deseemos.
Cuando se busca un objeto para utilizar como data source de un binding, se recorre el árbol de componentes en forma inversa, partiendo del componente en cuestión Esto es bastante útil cuando queremos que dos componentes compartan el mismo data source
Esto funciona así…
Hacer que ambos textbox compartan el mismo objeto Person, es cuestión de colocarlo en el DataContext del Grid
Entonces, si bien la funcionalidad es la misma, el código de sincronización se ha reducido enormemente y sobre todo es mas escalable sin mucho impacto en el código
El DataBinding tiene diferentes direcciones de enlace.
Dirección del Databind
Debemos de tener en cuenta que todas las clases que hagamos Binding deben de ser a DependencyProperties o Propiedad, si son a estas ultimas las clases deben de implementar el interfaz INotifyPropertyChanged para notificar los cambios de la clase al Interfaz de Usuario, sino cambiaríamos el valor y no se reflejaría en el control enlazado.
Recordar que en el DataBinding no solo lo podemos hacer de clases de negocio a objetos de interfaz de usuario, sino también entre los propios objetos de interfaz de usuario, esto da un gran poder en los diseños de interfaz de usuario.
Por ejemplo:
En este código cada vez que movamos el slider, se mostrar el valor en el TextBox.
Cuando se hace el Databinding existe atributos opcionales para añadir a las expresiones de Binding.
Autor del artículo: Oscar Álvarez Guerra
WPF incorpora un motor de Databinding que es una de las partes más potentes de WPF, no hay limitaciones en cuanto a lo que puedes hacer un Databinding.
Podríamos realizar las tareas anteriores sin DataBinding pero con mucho mas código, por ejemplo para la ventana.
.png)
El XAML seria
<!-- Window1.xaml --> <Window ...> <Grid> ... <TextBlock ...>Name:</TextBlock> <TextBox Name="nameTextBox" ... /> <TextBlock ...>Age:</TextBlock> <TextBox Name="ageTextBox" ... /> <Button Name="birthdayButton" ...>Birthday</Button> </Grid> </Window>
El codigo de la clase Person
public class Person {
string name;
public string Name {
get { return this.name; }
set { this.name = value; }
}
int age;
public int Age {
get { return this.age; }
set { this.age = value; }
}
public Person( ) {}
public Person(string name, int age) {
this.name = name;
this.age = age;
}
}
En el code-behind de la ventana
// Window1.xaml.cs
...
public class Person {...}
public partial class Window1 : Window {
Person person = new Person("Tom", 11);
public Window1( ) {
InitializeComponent( );
// Fill initial person fields
this.nameTextBox.Text = person.Name;
this.ageTextBox.Text = person.Age.ToString();
// Handle the birthday button click event
this.birthdayButton.Click += birthdayButton_Click;
}
void birthdayButton_Click(object sender, RoutedEventArgs e) {
++person.Age;
MessageBox.Show(string.Format(
"Happy Birthday, {0}, age {1}!",
person.Name,
person.Age),
"Birthday");
}
}
El código crea un objeto persona, y lo usa para inicializar los campos de la ventana. Cuando se presiona el botón del cumpleaños, la persona se modifica, y los datos se muestran en un message box.
Es un ejemplo bastante sencillo y fácil de implementar, si ahora quisiéramos reflejar los cambios en la ventana principal.
void birthdayButton_Click(object sender,
RoutedEventArgs e) {
++person.Age;
// Manually update the UI
this.ageTextBox.Text = person.Age.ToString();
MessageBox.Show(string.Format(
"Happy Birthday, {0}, age {1}!",
person.Name,
person.Age),
"Birthday");
}
Parece una forma sencilla de mejorarla, pero tiene un problema No escala bien a medida que la aplicación se hace mas complicada, requiriendo cada vez mas de estas líneas de código
Cambios en los objetos
Una forma mas robusta para que la UI lleve registro de los cambios, es que el objeto avise cuando una propiedad cambia, por ejemplo, lanzando un evento La forma correcta de hacer esto, es que el objeto implemente INotifyPropertyChanged
using System.ComponentModel; // INotifyPropertyChanged
...
public class Person : INotifyPropertyChanged {
// INotifyPropertyChanged Members
public event PropertyChangedEventHandler
PropertyChanged;
protected void Notify(string propName) {
if( this.PropertyChanged != null ) {
PropertyChanged(
this,
new PropertyChangedEventArgs(propName));
}
}
string name;
public string Name {
get { return this.name; }
set {
if( this.name == value ) { return; }
this.name = value;
Notify("Name");
}
}
int age;
public int Age {
get { return this.age; }
set {
if(this.age == value ) { return; }
this.age = value;
Notify("Age");
}
}
public Person( ) {}
public Person(string name, int age) {
this.name = name;
this.age = age;
}
}
Cuando la propiedad Age de la persona cambia, debida a la acción del handler de la ventana, el objeto persona hace un raise del evento PropertyChanged Podríamos manualmente capturar este evento, colocar un handler y reaccionar ante el mismo, como hacemos con cualquier otro handler…
Notificación de cambios // Window1.xaml.cs
...
public class Person : INotifyPropertyChanged {...}
public partial class Window1 : Window {
Person person = new Person("Tom", 11);
public Window1( ) {
InitializeComponent( );
// Fill initial person fields
this.nameTextBox.Text = person.Name;
this.ageTextBox.Text = person.Age.ToString( );
// Watch for changes in Tom's properties
person.PropertyChanged += person_PropertyCha
// Handle the birthday button click event
this.birthdayButton.Click += birthdayButton_Click;
}
void person_PropertyChanged(
object sender,
PropertyChangedEventArgs e) {
switch( e.PropertyName ) {
case "Name":
this.nameTextBox.Text = person.Name;
break;
case "Age":
this.ageTextBox.Text = person.Age.ToStrin
break;
}
}
void birthdayButton_Click(object sender,
RoutedEventArgs e) {
++person.Age;
// person_PropertyChanged will update ageTextBox
MessageBox.Show(
string.Format(
"Happy Birthday, {0}, age {1}!",
person.Name,
person.Age),
"Birthday");
}
}
Con el código anterior, ocurre una cascada de eventos que hacen que el handler del botón del cumpleaños, no tenga que actualizar la UI Gráficamente, se puede apreciar en el siguiente diagrama este flujo de eventos…
.png)
Sin embargo, hemos resuelto parte del problema Aun tenemos que actualizar manualmente en el handler del cambio de propiedad, la interfaz grafica
Pero tenemos otro problema…
Necesitamos también un mecanismo que detecte los cambios en la interfaz grafica, y los propague hacia el objeto. En el ejemplo anterior, si cambiamos el nombre y presionamos el botón Birthday, puede pasarnos esto…
.png)
El TextBox de Name tiene un valor diferente al que se muestra en el MessageBox, para solucionar esto debemos agregar el siguiente código
public partial class Window1 : Window {
Person person = new Person("Tom", 11);
public Window1( ) {
InitializeComponent( );
// Fill initial person fields
this.nameTextBox.Text = person.Name;
this.ageTextBox.Text = person.Age.ToString( );
// Watch for changes in Tom's properties
person.PropertyChanged += person_PropertyChanged;
// Watch for changes in the controls
this.nameTextBox.TextChanged += nameTextBox_TextChanged;
this.ageTextBox.TextChanged += ageTextBox_TextChanged;
// Handle the birthday button click event
this.birthdayButton.Click += birthdayButton_Click;
}
void nameTextBox_TextChanged(object sender, TextChangedEventArgs e) {
person.Name = nameTextBox.Text;
}
void ageTextBox_TextChanged(object sender, TextChangedEventArgs e) {
int age = 0;
if( int.TryParse(ageTextBox.Text, out age) ) {
person.Age = age;
}
}
void birthdayButton_Click(object sender, RoutedEventArgs e) {
++person.Age;
MessageBox.Show(string.Format(
"Happy Birthday, {0}, age {1}!",
person.Name,
person.Age),
"Birthday“
);
}
}

Sin importar entonces en donde se de el cambio (objeto ,UI), ambos elementos se mantienen sincronizados.in embargo, a pesar de que tenemos lo que buscábamos, tuvimos que escribir bastante código.
- El constructor de Window1 tuvo que inicializar los datos en los controles, convirtiendo según necesidad a String El constructor Window1 tuvo que enganchar los handlers de los eventos PropertyChanged del objeto Person, para tomar los nuevos datos de la persona, convirtiendo a String según sea necesario
- El constructor de Window1 debe enganchar el handler de TextChanged pra propagar cambios en la UI a la persona, convirtiendo de String según sea necesario
Claramente, la cantidad de código a escribir se nos va de las manos, si la cantidad de objetos, propiedades de los objetos y eventos aumenta. emás, este tipo de tareas parecen bastante repetitivas. ahí que WPF las abstrae en funcionalidades del framework, dándole el nombre de Data Binding.
Hasta Ahora hemos visto lo que teníamos que hacer para realizar sincronización de los datos con la interfaz de usuario sin Databinding, a partir de ahora vamos a ver el DataBinding
Consiste en registrar un par de propiedades, una de ellas en un objeto (Person.Name), la otra en la interfaz grafica (Textbox.Text), con el motor de Data Binding Este motor es el que se encarga de mantener ambas sincronizadas, convirtiendo los tipos de datos según sea necesario.

Para registrar ambas propiedades (tanto del elemento, que seria la clase persona como del objeto que seria el Textbox), utilizamos el objeto Binding
Todas las siguientes son equivalentes…
<TextBox ...>
<TextBox.Text>
<Binding Path="Age" />
</TextBox.Text>
</TextBox>
<TextBox Text="{Binding Path=Age}" />
<TextBox Text="{Binding Age}" />
es equivalente a…

En general, podemos pensar que Path es la ruta para acceder a una propiedad dentro del objeto fuente de la información. En el caso anterior, el binding se da entre el TextBox (propiedad Text) y la propiedad Age de un objeto que será determinado luego.
En el caso anterior, el textbox actúa como binding target , consumiendo los cambios binding source. El binding target puede ser cualquier elemento WPF, pero solo podemos hacer binding contra las dependency properties. El binding source puede ser cualquier propiedad de cualquier elemento WPF.En este caso, el binding source puede especificarse en runtime Sin embargo, en general, el binding provendrá de un Data Context.
Un DataContext es el lugar en el que los bindings buscan por su data source si no se les ha dado ninguna instrucción especial. En WPF todo FrameworkElement y todoFrameworkContentElement tienen una propiedad DataContext, y recordamos el grafico del post donde veíamos que todos los controles derivan de FrameWorkElement
El DataContext es de tipo Object, por lo que podemos colocar cualquier cosa que deseemos.
Cuando se busca un objeto para utilizar como data source de un binding, se recorre el árbol de componentes en forma inversa, partiendo del componente en cuestión Esto es bastante útil cuando queremos que dos componentes compartan el mismo data source

Esto funciona así…
- El binding busca un DataContext en el propio TextBox
- El binding busca un DataContext en la Grid
- El binding busca un DataContext en el Window
Hacer que ambos textbox compartan el mismo objeto Person, es cuestión de colocarlo en el DataContext del Grid
// Window1.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
namespace WithBinding {
public partial class Window1 : Window {
Person person = new Person("Tom", 11);
public Window1( ) {
InitializeComponent( );
grid.DataContext = person;
this.birthdayButton.Click += birthdayButton_Click;
}
void birthdayButton_Click(object sender,
RoutedEventArgs e) {
++person.Age;
MessageBox.Show(string.Format(
"Happy Birthday, {0}, age {1}!",
person.Name,
person.Age),
"Birthday");
}
}
}
Entonces, si bien la funcionalidad es la misma, el código de sincronización se ha reducido enormemente y sobre todo es mas escalable sin mucho impacto en el código
El DataBinding tiene diferentes direcciones de enlace.
Dirección del Databind
- OneWay: de origen a destino y cuando el origen cambie
- TwoWat: en ambos sentidos y con notificaciones de cambio
- OneWayToSource: es igual que OneWay pero al reves, de destino a origen
- OneTime: enlace a datos igual que OneWay pero únicamente una vez.

Debemos de tener en cuenta que todas las clases que hagamos Binding deben de ser a DependencyProperties o Propiedad, si son a estas ultimas las clases deben de implementar el interfaz INotifyPropertyChanged para notificar los cambios de la clase al Interfaz de Usuario, sino cambiaríamos el valor y no se reflejaría en el control enlazado.
Recordar que en el DataBinding no solo lo podemos hacer de clases de negocio a objetos de interfaz de usuario, sino también entre los propios objetos de interfaz de usuario, esto da un gran poder en los diseños de interfaz de usuario.
Por ejemplo:
<Slider Name="Slider1" />
<TextBox Name="TextBox1" Text="{Binding ElementName=Slider1, Path=Value}" />
En este código cada vez que movamos el slider, se mostrar el valor en el TextBox.
Cuando se hace el Databinding existe atributos opcionales para añadir a las expresiones de Binding.
- Source – usado en lugar ElementName para referirse al objeto
- RelativeSource –usado en lugar ElementName para referirse al elemento actual. con los valores Self, FindAncestor, y PreviousData.
- Mode - OneTime|OneWay|TwoWay|OneWayToSource – para indicar la dirección y frecuencia del binding
- Converter – para especificar una particular conversión de los datos a la interfaz de usuario
- ConverterParameter – para pasar un parámetro adicional al converter
- UpdateSourceTrigger - LostFocus|PropertyChanged|Explicit – para indicar cuando modificar la propiedad destino
- FallbackValue – para indicar un valor por defecto cuando el binding falla
Autor del artículo: Oscar Álvarez Guerra
![]() |
Publicado por: Angel Carrero |
|
|
Comentarios
Últimas noticias
· factura
Últimos artículos














































