Suche…


Syntax

  • <TextBlock Text="{Binding Title}"/>

  • <TextBlock Text="{Binding Path=Title}"/>

  • <TextBlock> <TextBlock.Text> <Binding Path="Title"/> </TextBlock.Text> </TextBlock>

Bemerkungen

Alle diese Tags erzeugen das gleiche Ergebnis.

Bindungszeichenfolge an die Texteigenschaft

Um den Inhalt der Benutzeroberfläche in Runtime zu ändern, können Sie die Binding . Wenn die gebundene Eigenschaft im Code geändert wird, wird sie der Benutzeroberfläche angezeigt.

<TextBlock Text="{Binding Title}"/>

Um die Benutzeroberfläche über Änderungen zu informieren, muss PropertyChanged das PropertyChanged Ereignis von der INotifyPropertyChanged Schnittstelle INotifyPropertyChanged , oder Sie können Dependency Property .

Die Bindung funktioniert, wenn sich die Eigenschaft "Title" in der Datei xaml.cs oder in der Datacontext-Klasse der XAML .

Der Datacontext kann direkt in der XAML eingerichtet werden

<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>

String-Bindungen formatieren

Wenn Sie etwas binden, z. B. ein Datum, möchten Sie es möglicherweise in einem bestimmten Format anzeigen, ohne es im Code herumzuspielen.

Dazu können wir die StringFormat-Eigenschaft verwenden.

Hier sind einige Beispiele:

Text="{Binding Path=ReleaseDate, StringFormat=dddd dd MMMM yyyy}"

Dies formatiert mein Datum wie folgt:

Dienstag, 16. August 2016


Hier ist ein weiteres Beispiel für die Temperatur.

Text="{Binding Path=Temp, StringFormat={}{0}°C}"

Dieses formatiert zu:

25 ° C

Die Grundlagen von INotifyPropertyChanged

Wenn Sie nicht nur statische Objekte anzeigen möchten, sondern Ihre Benutzeroberfläche auf Änderungen an korrelierenden Objekten reagieren soll, müssen Sie die Grundlagen der INotifyPropertyChanged Schnittstelle kennen.

Angenommen, wir haben unser MainWindow als definiert

<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>

Mit unserer Viewmodel-Klasse MainWindowViewModel definiert als

namespace Application.ViewModels
{
    public class MainWindowViewModel
    {
        private string _applicationStateText;

        public string ApplicationStateText
        {
            get { return _applicationStateText; }
            set { _applicationStateText = value; }
        }
        public MainWindowViewModel() 
        { 
            ApplicationStateText = "Hello World!";
        }

    }
}

Der TextBlock unserer Anwendung zeigt den Text Hello World aufgrund seiner Bindung an. Wenn sich der ApplicationStateText während der Laufzeit ändert, wird unsere Benutzeroberfläche nicht über eine solche Änderung informiert.
Um dies zu implementieren, muss unsere DataSource, in diesem Fall unser MainWindowViewModel , die Schnittstelle INotifyPropertyChanged . Dadurch können unsere Bindings das PropertyChangedEvent abonnieren.
Alles, was wir tun müssen, ist den PropertyChangedEventHandler aufzurufen, wenn wir unsere ApplicationStateText Eigenschaft wie folgt ändern:

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!";
        }
    }
}

und stellen Sie sicher, dass unsere Binding von TextBlock.Text tatsächlich auf ein PropertyChangedEvent hört:

<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 an eine Sammlung von Objekten mit INotifyPropertyChanged und INotifyCollectionChanged

Nehmen wir an, Sie haben eine ListView , in der jedes User angezeigt werden soll, das unter der Eigenschaft " Users des ViewModel in dem Eigenschaften des Benutzerobjekts programmgesteuert aktualisiert werden können.

<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>

Obwohl für INotifyPropertyChanged das User korrekt implementiert wurde

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));
    }
}

und für Ihr 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));
    }
}

Ihre Benutzeroberfläche wird nicht aktualisiert, wenn programmgesteuert eine Änderung an einem Benutzer vorgenommen wird.

Dies liegt einfach daran, dass Sie INotifyPropertyChanged nur für die Instanz der Liste selbst festgelegt haben. Nur wenn Sie die Liste erneut vollständig instanziieren, wenn sich eine Eigenschaft eines Elements ändert, wird Ihre Benutzeroberfläche aktualisiert.

// DO NOT DO THIS
User[] userCache = Users.ToArray();
Users = new List<User>(userCache);

Dies ist jedoch sehr ermüdend und für die Leistung unglaublich schlecht.
Wenn Sie über eine Liste mit 100'000 Elementen verfügen, die sowohl die ID als auch den Namen des Benutzers enthält, sind 200'000 DataBindings vorhanden, die jeweils neu erstellt werden müssen. Dies führt zu einer spürbaren Verzögerung für den Benutzer, wenn eine Änderung an etwas vorgenommen wird.

Um dieses Problem teilweise zu lösen, können Sie System.ComponentModel.ObservableCollection<T> anstelle von List<T> :

private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
    get { return _users; }
    set
    {
        if (_users == value) return;
        _users = value;
        NotifyPropertyChanged();
    }
}

Die ObservableCollection stellt das CollectionChanged Ereignis INotifyPropertyChanged und implementiert INotifyPropertyChanged selbst. Laut MSDN steigt das Ereignis, "[..] wenn ein Element hinzugefügt , entfernt , geändert , verschoben oder die gesamte Liste aktualisiert wird ".
Sie werden jedoch schnell feststellen, dass die ObservableCollection mit .NET 4.5.2 und früheren ObservableCollection kein CollectionChanged-Ereignis auslöst, wenn sich eine Eigenschaft eines Elements in der Auflistung wie hier beschrieben ändert.

Mit dieser Lösung können wir einfach unsere eigene TrulyObservableCollection<T> implementieren, ohne dass die INotifyPropertyChanged Einschränkung für T alles enthält, was wir brauchen, und ob T INotifyPropertyChanged implementiert INotifyPropertyChanged oder nicht:

/*
 * 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);
    }
}

und definieren unsere Property Users als TrulyObservableCollection<User> in unserem Ansichtsmodell

private TrulyObservableCollection<string> _users;
public TrulyObservableCollection<string> Users
{
    get { return _users; }
    set
    {
        if (_users == value) return;
        _users = value;
        NotifyPropertyChanged();
    }
}

Unsere Benutzeroberfläche wird nun benachrichtigt, sobald eine INPC-Property eines Elements innerhalb der Collection geändert wird, ohne dass jede einzelne Binding neu erstellt werden muss.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow