TOC

This article has been localized into Spanish by the community.

Ligado de datos:

Respondiendo a los cambios

Hasta aquí en este tutorial, hemos creado prácticamente enlaces entre elementos UI y clases existentes, pero en las aplicaciones de la vida real, usted obviamente enlazará sus propios objetos de datos. Esto se hará de la misma manera, pero una vez que comience a hacerlo, podría descubrir algo que lo decepcione: los cambios no son automáticamente reflejados como lo fueron en los ejemplos previos. Como aprenderá en este artículo, sólo necesita un poco de trabajo extra para que suceda esto, pero afortunadamente, WPF hace esto bastante fácil.

Respondiendo a cambios en las fuentes de datos

Hay dos escenarios diferentes que puedes; o no, tratar cuando trabajas con cambios en las fuentes de datos: cambios sobre la lista de elementos y cambios en las propiedades enlazadas en cada uno de los objetos de datos. La manera de tratarlos puede variar dependiendo en lo que estés haciendo y lo que quieras conseguir, pero WPF viene ya con dos soluciones sencillas que puedes usar: la propiedad ObservableCollection y la interfaz INotifyPropertyChanged

El siguiente ejemplo te mostrará porqué necesitamos estas dos cosas:

<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ChangeNotificationSample" Height="150" Width="300">
	<DockPanel Margin="10">
		<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
			<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
			<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
			<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
		</StackPanel>
		<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
	</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;

namespace WpfTutorialSamples.DataBinding
{
	public partial class ChangeNotificationSample : Window
	{
		private List<User> users = new List<User>();

		public ChangeNotificationSample()
		{
			InitializeComponent();

			users.Add(new User() { Name = "John Doe" });
			users.Add(new User() { Name = "Jane Doe" });

			lbUsers.ItemsSource = users;
		}

		private void btnAddUser_Click(object sender, RoutedEventArgs e)
		{
			users.Add(new User() { Name = "New user" });
		}

		private void btnChangeUser_Click(object sender, RoutedEventArgs e)
		{
			if(lbUsers.SelectedItem != null)
				(lbUsers.SelectedItem as User).Name = "Random Name";
		}

		private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
		{
			if(lbUsers.SelectedItem != null)
				users.Remove(lbUsers.SelectedItem as User);
		}
	}

	public class User
	{
		public string Name { get; set; }
	}
}

Intenta arrancarlo tú mismo y comprueba como incluso añadiendo algo en la lista o cambiando el nombre de uno de los usuarios, nada en la UI se ha actualizado. El ejemplo es muy sencillo, con una clase User que mantendrá el nombre del usuario, un ListBox para mostrarlos y algunos botones para manipular tanto la lista como su contenido. El ItemsSource de la lista se asigna a una lista rápida con un par de usuarios que creamos en el constructor de la ventana. El problema es que ninguno de esos botones parece funcionar. Vamos a arreglarlo en dos sencillos pasos.

Reflejando cambios en la lista lista de fuente de datos

El primer paso es conseguir que la UI responda a los cambios en la fuente de la lista (ItemsSource), cuando añadimos o eliminamos un usuario, por ejemplo. Lo que necesitamos es una lista que notifique cualquier destino de cambios a su contenido. Y afortunadamente, WPF ofrece un tipo de lista que hará esto precisamente. Se llama ObservableCollection, y se usa prácticamente como una List<T> normal, con unas pocas diferencias.

En el último ejemplo, que encontrará abajo; tan sólo hemos reemplazado la List<User> con un ObservableCollection<User> - y es lo único que se necesita hacer! Esto hará que los botones Add y Delete funcionen, pero no afectará al botón de "Change name", porque el cambio ocurrirá al enlazar el objeto de datos en sí mismo y no a la lista fuente - trataremos este escenario en el segundo paso.

Reflejando cambios en los objetos de datos

El segundo paso es permitir que nuestra clase User personalizada implemente la interfaz INotifyPropertyChanged. Haciendo esto, nuestros objetos User son capaces de alertar a la capa UI de los cambios de sus propiedades. Esto es un poco más tedioso que cambiar simplemente el tipo de lista, como habíamos hecho antes, pero es de todas formas el camino más simple para conseguir actualizaciones automáticas.

El ejemplo final que funciona correctamente

Con los dos cambios descritos arriba, tenemos ahora un ejemplo que REFLEJARÁ los cambios de la fuente de datos. Y tendrá esta pinta:

<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ChangeNotificationSample" Height="135" Width="300">
	<DockPanel Margin="10">
		<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
			<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
			<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
			<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
		</StackPanel>
		<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
	</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace WpfTutorialSamples.DataBinding
{
	public partial class ChangeNotificationSample : Window
	{
		private ObservableCollection<User> users = new ObservableCollection<User>();

		public ChangeNotificationSample()
		{
			InitializeComponent();

			users.Add(new User() { Name = "John Doe" });
			users.Add(new User() { Name = "Jane Doe" });

			lbUsers.ItemsSource = users;
		}

		private void btnAddUser_Click(object sender, RoutedEventArgs e)
		{
			users.Add(new User() { Name = "New user" });
		}

		private void btnChangeUser_Click(object sender, RoutedEventArgs e)
		{
			if(lbUsers.SelectedItem != null)
				(lbUsers.SelectedItem as User).Name = "Random Name";
		}

		private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
		{
			if(lbUsers.SelectedItem != null)
				users.Remove(lbUsers.SelectedItem as User);
		}
	}

	public class User : INotifyPropertyChanged
	{
		private string name;
		public string Name {
			get { return this.name; }
			set
			{
				if(this.name != value)
				{
					this.name = value;
					this.NotifyPropertyChanged("Name");
				}
			}
		}

		public event PropertyChangedEventHandler PropertyChanged;

		public void NotifyPropertyChanged(string propName)
		{
			if(this.PropertyChanged != null)
				this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
		}
	}
}

Resumen

Como puede ver, implementar INotifyPropertyChanged es muy fácil, pero sí genera un poco más de código extra en las clases, y añade un poco más de lógica en las propiedades. Este es el precio a pagar si quieres enlazarte a tus propias clases y ver los cambios reflejados en la UI de manera inmediata. Obviamente solo hay que llamar a NotifyPropertyChanged en los setter's de las propiedades a los que te adhieres - el resto pueden permanecer de la forma en la que se crean.

ObservableCollection es, sin embargo, muy fácil de manejar - simplemente hay que usar esta lista específica en las situaciones donde quieres que los cambios que ocurren sobre una lista fuente se vean reflejados en el destino enlazado.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!