xaml
El enlace de datos
Buscar..
Sintaxis
<TextBlock Text="{Binding Title}"/><TextBlock Text="{Binding Path=Title}"/><TextBlock> <TextBlock.Text> <Binding Path="Title"/> </TextBlock.Text> </TextBlock>
Observaciones
Todas estas etiquetas producen el mismo resultado.
Enlace de cadena a propiedad de texto
Para cambiar el contenido de la interfaz de usuario en tiempo de ejecución, puede utilizar Binding . Cuando se cambia la propiedad enlazada del código, se mostrará a la interfaz de usuario.
<TextBlock Text="{Binding Title}"/>
Para notificar a la UI sobre los cambios, la propiedad debe generar el evento PropertyChanged desde la interfaz INotifyPropertyChanged o puede usar la Dependency Property .
El enlace funciona si la propiedad "Título" está en el archivo xaml.cs o en la clase Datacontext de XAML .
El Datacontext se puede configurar en el XAML directamente
<Window x:Class="Application.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Application">
<Window.DataContext>
<local:DataContextClass/>
</Window.DataContext>
Formato de enlaces de cadena
Al hacer un enlace de algo, por ejemplo, una fecha, es posible que desee mostrarlo en un formato específico sin alterarlo en el código.
Para hacer esto podemos usar la propiedad StringFormat.
Aquí hay unos ejemplos:
Text="{Binding Path=ReleaseDate, StringFormat=dddd dd MMMM yyyy}"
Esto formatea mi fecha a la siguiente:
Martes 16 de agosto de 2016
Aquí hay otro ejemplo para la temperatura.
Text="{Binding Path=Temp, StringFormat={}{0}°C}"
Este formato para:
25 ° C
Los fundamentos de INotifyPropertyChanged
Si no solo desea mostrar objetos estáticos, sino que su interfaz de usuario responde a los cambios para correlacionar objetos, debe comprender los conceptos básicos de la interfaz INotifyPropertyChanged .
Suponiendo que tenemos nuestro MainWindow definido como
<Window x:Class="Application.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:Application.ViewModels>
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<TextBlock Text={Binding Path=ApplicationStateText}" />
</Grid>
</Window>
Con nuestro modelo de vista de clase MainWindowViewModel definido como
namespace Application.ViewModels
{
public class MainWindowViewModel
{
private string _applicationStateText;
public string ApplicationStateText
{
get { return _applicationStateText; }
set { _applicationStateText = value; }
}
public MainWindowViewModel()
{
ApplicationStateText = "Hello World!";
}
}
}
El TextBlock de nuestra aplicación mostrará el texto Hello World debido a su enlace. Si nuestro ApplicationStateText cambia durante el tiempo de ejecución, nuestra interfaz de usuario no será notificada de dicho cambio.
Para implementar esto, nuestro DataSource, en este caso nuestro MainWindowViewModel , necesita implementar la Interfaz INotifyPropertyChanged . Esto hará que nuestros Bindings puedan suscribirse al PropertyChangedEvent .
Todo lo que tenemos que hacer es invocar PropertyChangedEventHandler cada vez que cambiemos nuestra propiedad ApplicationStateText esta forma:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Application.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged( [CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _applicationStateText;
public string ApplicationStateText
{
get { return _applicationStateText; }
set
{
if (_applicationStateText != value)
{
_applicationStateText = value;
NotifyPropertyChanged();
}
}
}
public MainWindowViewModel()
{
ApplicationStateText = "Hello World!";
}
}
}
y asegúrese de que nuestro Binding de TextBlock.Text realmente escucha un PropertyChangedEvent :
<Window x:Class="Application.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:Application.ViewModels">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<TextBlock Text={Binding Path=ApplicationStateText, UpdateSourceTrigger=PropertyChanged }" />
</Grid>
</Window>
Enlace a una colección de objetos con INotifyPropertyChanged y INotifyCollectionChanged
Supongamos que tiene un ListView que se supone que muestra todos los objetos de User enumerados en la Propiedad de Users de ViewModel donde las Propiedades del objeto de Usuario pueden actualizarse mediante programación.
<ListView ItemsSource="{Binding Path=Users}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type models:User}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5,3,15,3"
Text="{Binding Id, Mode=OneWay}" />
<TextBox Width="200"
Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Delay=450}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
A pesar de que INotifyPropertyChanged implementó correctamente para el objeto User
public class User : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
private string _name;
public int Id
{
get { return _id; }
private set
{
if (_id == value) return;
_id = value;
NotifyPropertyChanged();
}
}
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
NotifyPropertyChanged();
}
}
public User(int id, string name)
{
Id = id;
Name = name;
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
y para su objeto ViewModel
public sealed class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private List<User> _users;
public List<User> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
public MainWindowViewModel()
{
Users = new List<User> {new User(1, "John Doe"), new User(2, "Jane Doe"), new User(3, "Foo Bar")};
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
su interfaz de usuario no se actualizará, si se realiza un cambio a un usuario mediante programación.
Esto es simplemente porque solo ha configurado INotifyPropertyChanged en la instancia de la lista. Solo si vuelve a crear una instancia completa de la Lista si una propiedad de un elemento cambia, su interfaz de usuario se actualizará.// DO NOT DO THIS
User[] userCache = Users.ToArray();
Users = new List<User>(userCache);
Sin embargo, esto es muy pesado e increíblemente malo para el rendimiento.
Si tiene una lista de 100,000 elementos que muestran tanto el ID como el nombre del usuario, habrá 200,000 enlaces de datos en su lugar y cada uno tendrá que volver a crearlos. Esto da como resultado un retraso notable para el usuario cuando se realiza un cambio en cualquier cosa.
System.ComponentModel.ObservableCollection<T> lugar de List<T> : private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
ObservableCollection nos proporciona el evento CollectionChanged e implementa el propio INotifyPropertyChanged . Según MSDN, el evento aumentará, "[..] cuando un elemento se agregue , elimine , cambie , mueva o se actualice la lista completa ".
Sin embargo, pronto se dará cuenta de que, con .NET 4.5.2 y anteriores, ObservableCollection no generará un evento CollectionChanged si la propiedad de un elemento en la colección cambia, como se explica aquí .
TrulyObservableCollection<T> sin la restricción INotifyPropertyChanged para T teniendo todo lo que necesitamos y exponiendo si T implementa INotifyPropertyChanged o no: /*
* Original Class by Simon @StackOverflow http://stackoverflow.com/a/5256827/3766034
* Removal of the INPC-Constraint by Jirajha @StackOverflow
* according to to suggestion of nikeee @StackOverflow http://stackoverflow.com/a/10718451/3766034
*/
public sealed class TrulyObservableCollection<T> : ObservableCollection<T>
{
private readonly bool _inpcHookup;
public bool NotifyPropertyChangedHookup => _inpcHookup;
public TrulyObservableCollection()
{
CollectionChanged += TrulyObservableCollectionChanged;
_inpcHookup = typeof(INotifyPropertyChanged).GetTypeInfo().IsAssignableFrom(typeof(T));
}
public TrulyObservableCollection(IEnumerable<T> items) : this()
{
foreach (var item in items)
{
this.Add(item);
}
}
private void TrulyObservableCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (NotifyPropertyChangedHookup && e.NewItems != null && e.NewItems.Count > 0)
{
foreach (INotifyPropertyChanged item in e.NewItems)
{
item.PropertyChanged += ItemPropertyChanged;
}
}
if (NotifyPropertyChangedHookup && e.OldItems != null && e.OldItems.Count > 0)
{
foreach (INotifyPropertyChanged item in e.OldItems)
{
item.PropertyChanged -= ItemPropertyChanged;
}
}
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}
y defina nuestros Users Propiedad como TrulyObservableCollection<User> en nuestro ViewModel
private TrulyObservableCollection<string> _users;
public TrulyObservableCollection<string> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
Ahora se notificará a nuestra interfaz de usuario una vez que una propiedad de INPC de un elemento dentro de la colección cambie sin la necesidad de volver a crear cada Binding .