xaml
Dataverbinding
Zoeken…
Syntaxis
<TextBlock Text="{Binding Title}"/><TextBlock Text="{Binding Path=Title}"/><TextBlock> <TextBlock.Text> <Binding Path="Title"/> </TextBlock.Text> </TextBlock>
Opmerkingen
Al deze tags produceren hetzelfde resultaat.
Bindingsreeks aan eigenschap Text
Als u UI-inhoud tijdens runtime wilt wijzigen, kunt u Binding . Wanneer de gebonden eigenschap wordt gewijzigd van de code, wordt deze weergegeven in de gebruikersinterface.
<TextBlock Text="{Binding Title}"/>
Om de gebruikersinterface op de hoogte te stellen van wijzigingen, moet PropertyChanged gebeurtenis PropertyChanged verhogen vanuit de INotifyPropertyChanged interface of u kunt de Dependency Property gebruiken.
De binding werkt als de eigenschap 'Title' voorkomt in het bestand xaml.cs of in de klasse Datacontext uit de XAML .
De Datacontext kan rechtstreeks in de XAML worden ingesteld
<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>
Stringbindingen opmaken
Wanneer u iets bindt, bijvoorbeeld een datum, wilt u het misschien in een specifiek formaat weergeven zonder het in de code te verknoeien.
Om dit te doen kunnen we de eigenschap StringFormat gebruiken.
Hier zijn enkele voorbeelden:
Text="{Binding Path=ReleaseDate, StringFormat=dddd dd MMMM yyyy}"
Hiermee wordt mijn datum als volgt opgemaakt:
Dinsdag 16 augustus 2016
Hier is nog een voorbeeld voor temperatuur.
Text="{Binding Path=Temp, StringFormat={}{0}°C}"
Deze indeling voor:
25 ° C
De basis van INotifyPropertyChanged
Als u niet alleen statische objecten wilt weergeven, maar uw gebruikersinterface wilt laten reageren op wijzigingen in samenhangende objecten, moet u de basisprincipes van de INotifyPropertyChanged interface begrijpen.
Ervan uitgaande dat we ons MainWindow gedefinieerd als
<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>
Met ons Viewmodel-Class MainWindowViewModel gedefinieerd als
namespace Application.ViewModels
{
public class MainWindowViewModel
{
private string _applicationStateText;
public string ApplicationStateText
{
get { return _applicationStateText; }
set { _applicationStateText = value; }
}
public MainWindowViewModel()
{
ApplicationStateText = "Hello World!";
}
}
}
het TextBlock van onze applicatie zal de tekst Hello World weergeven vanwege zijn bindende. Als onze ApplicationStateText tijdens runtime verandert, wordt onze gebruikersinterface niet op de hoogte gebracht van dergelijke wijzigingen.
Om dit te implementeren, moet onze DataSource, in dit geval ons MainWindowViewModel , de Interface INotifyPropertyChanged . Hierdoor kunnen onze Bindings zich abonneren op de PropertyChangedEvent .
Het enige wat we moeten doen is de PropertyChangedEventHandler aanroepen wanneer we onze ApplicationStateText eigenschap als volgt wijzigen:
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!";
}
}
}
en zorg ervoor dat onze Binding van TextBlock.Text daadwerkelijk naar een PropertyChangedEvent luistert:
<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>
Binden aan een verzameling objecten met INotifyPropertyChanged en INotifyCollectionChanged
Laten we aannemen dat u een ListView waarvan wordt verondersteld dat elk User wordt weergegeven onder de eigenschap Users van het ViewModel waar eigenschappen van het object Gebruiker programmatisch kunnen worden bijgewerkt.
<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>
Ondanks voor INotifyPropertyChanged beeing correct geïmplementeerd voor het object 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));
}
}
en voor uw ViewModel object
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));
}
}
uw gebruikersinterface wordt niet bijgewerkt als een wijziging in een gebruiker programmatisch wordt aangebracht.
Dit komt simpelweg omdat u INotifyPropertyChanged alleen hebt ingesteld op het exemplaar van de lijst zelf. Alleen als u de lijst volledig opnieuw indient als een eigenschap van een element verandert, wordt uw gebruikersinterface bijgewerkt.// DO NOT DO THIS
User[] userCache = Users.ToArray();
Users = new List<User>(userCache);
Dit is echter erg vermoeiend en ongelooflijk slecht voor de prestaties.
Als u een lijst met 100.000 elementen hebt die zowel de ID als de naam van de gebruiker toont, zijn er 200.000 gegevensbanden aanwezig die elk opnieuw moeten worden gemaakt. Dit resulteert in een merkbare vertraging voor de gebruiker wanneer er iets wordt gewijzigd.
System.ComponentModel.ObservableCollection<T> plaats van List<T> : private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
De ObservableCollection biedt ons het CollectionChanged evenement en implementeert INotifyPropertyChanged zelf. Volgens MSDN zal het evenement stijgen, "[..] wanneer een item wordt toegevoegd , verwijderd , gewijzigd , verplaatst of de hele lijst wordt vernieuwd ".
Je zult echter snel beseffen dat met .NET 4.5.2 en eerder, de ObservableCollection geen CollectionChanged-evenement zal veroorzaken als een eigenschap van een element in de collectie verandert zoals hier besproken.
TrulyObservableCollection<T> implementeren zonder dat de INotifyPropertyChanged beperking voor T alles heeft wat we nodig hebben en of T implementaties INotifyPropertyChanged dan niet INotifyPropertyChanged : /*
* 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);
}
}
en definiëren onze Users van TrulyObservableCollection<User> goed als TrulyObservableCollection<User> in ons ViewModel
private TrulyObservableCollection<string> _users;
public TrulyObservableCollection<string> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
Onze gebruikersinterface krijgt nu een melding zodra een INPC-eigenschap van een element in de collectie verandert zonder dat elke Binding opnieuw hoeft te worden gemaakt.