xaml
Databindning
Sök…
Syntax
<TextBlock Text="{Binding Title}"/><TextBlock Text="{Binding Path=Title}"/><TextBlock> <TextBlock.Text> <Binding Path="Title"/> </TextBlock.Text> </TextBlock>
Anmärkningar
Alla dessa taggar ger samma resultat.
Binder strängen till egenskapen Text
För att ändra UI-innehåll under körning kan du använda Binding . När bindad egenskap ändras från koden, kommer den att visas till UI.
<TextBlock Text="{Binding Title}"/>
För att meddela användargränssnittet om ändringar måste egenskapen höja PropertyChanged händelsen från INotifyPropertyChanged gränssnittet eller så kan du använda Dependency Property .
Bindningen fungerar om egenskapen "Titel" finns i filen xaml.cs eller i Datacontext-klassen från XAML .
Datacontext kan ställas in direkt i 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>
Formatera strängbindningar
När du gör en bindning av något, till exempel ett datum, kanske du vill visa det i ett specifikt format utan att röra med det i koden.
För att göra detta kan vi använda egenskapen StringFormat.
Här är några exempel:
Text="{Binding Path=ReleaseDate, StringFormat=dddd dd MMMM yyyy}"
Detta formaterar mitt datum till följande:
Tisdag 16 augusti 2016
Här är ett annat exempel på temperatur.
Text="{Binding Path=Temp, StringFormat={}{0}°C}"
Detta formaterar till:
25 ° C
Grunderna i INotifyPropertyChanged
Om du inte bara vill visa statiska objekt, utan låter användargränssnittet svara på förändringar i korrelerande objekt, måste du förstå grunderna i gränssnittet INotifyPropertyChanged .
Förutsatt att vi har vår MainWindow definierad som
<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>
Med vår Viewmodel-klass MainWindowViewModel definierad som
namespace Application.ViewModels
{
public class MainWindowViewModel
{
private string _applicationStateText;
public string ApplicationStateText
{
get { return _applicationStateText; }
set { _applicationStateText = value; }
}
public MainWindowViewModel()
{
ApplicationStateText = "Hello World!";
}
}
}
TextBlock för vår applikation kommer att visa Text Hello World på grund av dess bindande. Om vår ApplicationStateText ändras under körning kommer vårt användargränssnitt inte att meddelas om sådan ändring.
För att implementera detta måste vår DataSource, i detta fall vår MainWindowViewModel , implementera gränssnittet INotifyPropertyChanged . Detta kommer att göra att våra Bindings kan prenumerera på PropertyChangedEvent .
Allt vi behöver göra är att åberopa PropertyChangedEventHandler när vi ändrar vår ApplicationStateText egenskap som denna:
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!";
}
}
}
och se till att vår Binding of TextBlock.Text faktiskt lyssnar på en 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>
Bindning till en samling av objekt med INotifyPropertyChanged och INotifyCollectionChanged
Låt oss anta att du har en ListView som är tänkt att visa alla User listade under Users egendom ViewModel där egenskaper användarobjektet kan bli uppdaterad via programmering.
<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>
Trots för INotifyPropertyChanged beeing förs korrekt för User objektet
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));
}
}
och för ditt ViewModel objekt
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));
}
}
din UI kommer inte att uppdatera om en ändring av en användare görs programmatiskt.
Detta beror helt enkelt på att du bara har ställt INotifyPropertyChanged i förekomst av själva listan. Endast om du återinstanserar listan helt om en egenskap i ett element ändrar ditt användargränssnitt uppdateras.// DO NOT DO THIS
User[] userCache = Users.ToArray();
Users = new List<User>(userCache);
Detta är dock mycket tröttsamt och otroligt dåligt för prestanda.
Om du har en lista med 100'000 element som visar både ID och namn på användaren kommer det att finnas 200'000 DataBindings på plats som var och en måste återskapas. Detta resulterar i märkbar fördröjning för användaren när en ändring görs till någonting.
System.ComponentModel.ObservableCollection<T> istället för List<T> : private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
ObservableCollection förser oss med CollectionChanged Event och implementerar INotifyPropertyChanged själv. Enligt MSDN kommer händelsen att stiga, "[..] när ett objekt läggs till , tas bort , ändras , flyttas eller hela listan uppdateras ".
Du kommer dock snabbt att inse att med .NET 4.5.2 och tidigare kommer ObservableCollection inte att höja en CollectionChanged-händelse om en egenskap hos ett element i samlingen ändras som diskuterats här .
TrulyObservableCollection<T> utan att INotifyPropertyChanged begränsningen för att T har allt vi behöver och avslöjar alla T redskap INotifyPropertyChanged eller inte: /*
* 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);
}
}
och definiera vår fastighet Users som TrulyObservableCollection<User> i vår Viewmodel
private TrulyObservableCollection<string> _users;
public TrulyObservableCollection<string> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
Vårt användargränssnitt kommer nu att meddelas om en gång en INPC-egenskap av ett element inom samlingen ändras utan att behöva skapa om varje enskild Binding .