xaml
Associazione dati
Ricerca…
Sintassi
<TextBlock Text="{Binding Title}"/><TextBlock Text="{Binding Path=Title}"/><TextBlock> <TextBlock.Text> <Binding Path="Title"/> </TextBlock.Text> </TextBlock>
Osservazioni
Tutti questi tag producono lo stesso risultato.
Associazione della stringa alla proprietà Text
Per modificare il contenuto dell'interfaccia utente in runtime, puoi utilizzare Binding . Quando la proprietà associata viene modificata dal codice, verrà visualizzata nell'interfaccia utente.
<TextBlock Text="{Binding Title}"/>
Per notificare all'interfaccia utente le modifiche, la proprietà deve generare l'evento PropertyChanged dall'interfaccia INotifyPropertyChanged oppure è possibile utilizzare la Dependency Property .
Il binding funziona se la proprietà "Title" si trova nel file xaml.cs o nella classe Datacontext da XAML .
Datacontext può essere impostato direttamente in 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>
Formattare i binding di stringhe
Quando crei un legame con qualcosa, ad esempio una data, potresti volerlo mostrare in un formato specifico senza fare confusione con esso nel codice.
Per fare questo possiamo usare la proprietà StringFormat.
Ecco alcuni esempi:
Text="{Binding Path=ReleaseDate, StringFormat=dddd dd MMMM yyyy}"
Questo formatta la mia data come segue:
Martedì 16 agosto 2016
Ecco un altro esempio di temperatura.
Text="{Binding Path=Temp, StringFormat={}{0}°C}"
Questo formato per:
25 ° C
Le basi di INotifyPropertyChanged
Se non si desidera solo visualizzare oggetti statici, ma la propria UI deve rispondere alle modifiche apportate agli oggetti correlati, è necessario comprendere le nozioni di base dell'interfaccia INotifyPropertyChanged .
Supponendo di avere la nostra MainWindow definita come
<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 il nostro ViewModel- MainWindowViewModel definito come
namespace Application.ViewModels
{
public class MainWindowViewModel
{
private string _applicationStateText;
public string ApplicationStateText
{
get { return _applicationStateText; }
set { _applicationStateText = value; }
}
public MainWindowViewModel()
{
ApplicationStateText = "Hello World!";
}
}
}
il TextBlock della nostra applicazione visualizzerà il testo Hello World a causa del suo legame. Se il nostro ApplicationStateText cambia durante il runtime, la nostra interfaccia utente non verrà informata di tale modifica.
Per implementare questo, il nostro DataSource, in questo caso il nostro MainWindowViewModel , ha bisogno di implementare l'interfaccia INotifyPropertyChanged . Ciò farà sì che i nostri Bindings possano sottoscrivere l'oggetto PropertyChangedEvent .
Tutto ciò che dobbiamo fare è richiamare PropertyChangedEventHandler ogni volta che modifichiamo la nostra proprietà ApplicationStateText questo modo:
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!";
}
}
}
e assicurati che il nostro Binding di TextBlock.Text realtà ascolti 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>
Associazione a una raccolta di oggetti con INotifyPropertyChanged e INotifyCollectionChanged
Supponiamo che tu abbia una ListView che dovrebbe visualizzare ogni oggetto User elencato nella Proprietà Users del ViewModel dove le Proprietà dell'oggetto User possono essere aggiornate in modo programmatico.
<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>
Nonostante per INotifyPropertyChanged sia stato implementato correttamente per l'oggetto 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));
}
}
e per il tuo oggetto 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));
}
}
la tua interfaccia utente non si aggiornerà, se viene apportata una modifica a un utente a livello di codice.
Questo è semplicemente perché hai impostato solo INotifyPropertyChanged sull'istanza dell'elenco stesso. Solo se riattiverai completamente l'Elenco se una proprietà di un elemento cambia, l'interfaccia utente verrà aggiornata.// DO NOT DO THIS
User[] userCache = Users.ToArray();
Users = new List<User>(userCache);
Ciò tuttavia è molto faticoso e incredibilmente negativo per le prestazioni.
Se si dispone di un elenco di 100.000 elementi che mostrano sia l'ID che il nome dell'utente, saranno presenti 200.000 associazioni di dati che dovranno essere ricreate. Ciò si traduce in un ritardo notevole per l'utente ogni volta che viene apportata una modifica a qualcosa.
System.ComponentModel.ObservableCollection<T> anziché List<T> : private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
ObservableCollection ci fornisce l'evento CollectionChanged e implementa INotifyPropertyChanged . Secondo MSDN, l'evento aumenterà "[..] quando un articolo viene aggiunto , rimosso , modificato , spostato o l' intero elenco viene aggiornato ".
Tuttavia, ti renderai presto conto che con .NET 4.5.2 e versioni precedenti, ObservableCollection non genererà un evento CollectionChanged se una proprietà di un elemento nelle modifiche alla raccolta come discusso qui .
TrulyObservableCollection<T> senza il vincolo INotifyPropertyChanged per T che ha tutto ciò di cui abbiamo bisogno ed esponendo se 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);
}
}
e definisci i nostri Users proprietà come TrulyObservableCollection<User> nel nostro ViewModel
private TrulyObservableCollection<string> _users;
public TrulyObservableCollection<string> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
La nostra interfaccia utente riceverà ora una notifica circa una volta una proprietà INPC di un elemento all'interno delle modifiche della raccolta senza la necessità di ricreare ogni singolo Binding .