xaml
Liaison de données
Recherche…
Syntaxe
<TextBlock Text="{Binding Title}"/><TextBlock Text="{Binding Path=Title}"/><TextBlock> <TextBlock.Text> <Binding Path="Title"/> </TextBlock.Text> </TextBlock>
Remarques
Toutes ces balises produisent le même résultat.
Chaîne de liaison à la propriété Text
Pour modifier le contenu de l'interface utilisateur en cours d'exécution, vous pouvez utiliser la Binding . Lorsque la propriété liée est modifiée à partir du code, elle sera affichée dans l'interface utilisateur.
<TextBlock Text="{Binding Title}"/>
Pour notifier les modifications à l'interface utilisateur, la propriété doit INotifyPropertyChanged événement PropertyChanged partir de l'interface INotifyPropertyChanged ou vous pouvez utiliser la Dependency Property .
La liaison fonctionne si la propriété "Title" se trouve dans le fichier xaml.cs ou dans la classe Datacontext du fichier XAML .
Le Datacontext peut être configuré directement dans le XAML
<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>
Mise en forme des liaisons de chaînes
Lorsque vous effectuez une liaison de quelque chose, par exemple une date, vous voudrez peut-être l'afficher dans un format spécifique sans le modifier avec le code.
Pour ce faire, nous pouvons utiliser la propriété StringFormat.
Voici quelques exemples:
Text="{Binding Path=ReleaseDate, StringFormat=dddd dd MMMM yyyy}"
Cela formate ma date comme suit:
Mardi 16 août 2016
Voici un autre exemple de température.
Text="{Binding Path=Temp, StringFormat={}{0}°C}"
Ce formats à:
25 ° C
Les bases de INotifyPropertyChanged
Si vous souhaitez non seulement afficher des objets statiques, mais que votre interface utilisateur répond aux modifications apportées aux objets en corrélation, vous devez comprendre les principes de base de l'interface INotifyPropertyChanged .
En supposant que nous avons notre MainWindow définie comme
<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>
Avec notre classe MainWindowViewModel défini comme
namespace Application.ViewModels
{
public class MainWindowViewModel
{
private string _applicationStateText;
public string ApplicationStateText
{
get { return _applicationStateText; }
set { _applicationStateText = value; }
}
public MainWindowViewModel()
{
ApplicationStateText = "Hello World!";
}
}
}
Le TextBlock de notre application affichera le texte Hello World en raison de sa liaison. Si notre ApplicationStateText change pendant l'exécution, notre interface utilisateur ne sera pas informée de ce changement.
Pour implémenter cela, notre DataSource, dans ce cas notre MainWindowViewModel , doit implémenter l'interface INotifyPropertyChanged . Cela va permettre à nos Bindings de s'abonner à PropertyChangedEvent .
Tout ce que nous devons faire est d'appeler le PropertyChangedEventHandler chaque fois que nous changeons notre propriété ApplicationStateText comme ceci:
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!";
}
}
}
et assurez-vous que notre Binding de TextBlock.Text écoute réellement 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>
Liaison à une collection d'objets avec INotifyPropertyChanged et INotifyCollectionChanged
Supposons que vous ayez un ListView qui est censé afficher tous User objets User listés sous la propriété Users du ViewModel où les propriétés de l'objet User peuvent être mises à jour par programme.
<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>
Malgré pour INotifyPropertyChanged beeing implémenté correctement pour l'objet 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));
}
}
et pour votre objet 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));
}
}
Votre interface utilisateur ne sera pas mise à jour si une modification est apportée à un utilisateur par programmation.
C'est simplement parce que vous avez défini INotifyPropertyChanged uniquement sur l'instance de la liste elle-même. Ce n'est que si vous réinstanciez complètement la liste si une propriété d'un élément change que votre interface utilisateur sera mise à jour.// DO NOT DO THIS
User[] userCache = Users.ToArray();
Users = new List<User>(userCache);
Ceci est cependant très fatigant et incroyablement mauvais pour la performance.
Si vous avez une liste de 100 000 éléments affichant à la fois l'ID et le nom de l'utilisateur, il y aura 200 000 DataBindings en place, chacun devant être recréé. Cela se traduit par un décalage notable pour l'utilisateur chaque fois qu'un changement est apporté à quelque chose.
System.ComponentModel.ObservableCollection<T> au lieu de List<T> : private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
ObservableCollection nous fournit l'événement CollectionChanged et implémente INotifyPropertyChanged lui-même. Selon MSDN, l'événement se produira, "[..] lorsqu'un élément est ajouté , supprimé , modifié , déplacé ou que la liste entière est actualisée ".
Vous réaliserez toutefois rapidement qu'avec .NET 4.5.2 et versions antérieures, ObservableCollection ne ObservableCollection pas d'événement CollectionChanged si une propriété d'un élément de la collection change, comme indiqué ici .
TrulyObservableCollection<T> sans INotifyPropertyChanged contrainte pour T ayant tout ce que nous devons et exposer wether T implémente INotifyPropertyChanged ou non: /*
* 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);
}
}
et définir nos Users propriété comme TrulyObservableCollection<User> dans notre ViewModel
private TrulyObservableCollection<string> _users;
public TrulyObservableCollection<string> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
Notre interface utilisateur sera désormais informée d'une fois une propriété INPC d'un élément dans les modifications de la collection sans qu'il soit nécessaire de recréer chaque Binding .