xaml

Data Binding

Syntax#

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

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

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

Remarks#

All these tags produce the same result.

Binding string to Text property

To change UI content in runtime, you can use Binding. When binded property is changed from the code, it will be displayed to the UI.

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

To notify UI about changes, property must raise PropertyChanged event from INotifyPropertyChanged interface or you can use Dependency Property.

The Binding is working if the property “Title” is in the xaml.cs file or in the Datacontext class from the XAML.

The Datacontext can be set up in the XAML directly

<Window x:Class="Application.MainWindow"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Application">
<Window.DataContext>
   <local:DataContextClass/>
</Window.DataContext>

Formatting String Bindings

When making a bind of something, for example a date you may want to show it in a specific format without messing around with it in the code.

To do this we can use the StringFormat property.

Here are some examples:

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

This formats my date to the following:

Tuesday 16 August 2016


Here is another example for temperature.

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

This formats to:

25°C

The basics of INotifyPropertyChanged

If you do not only wish to display static objects, but have your UI respond to changes to correlating objects, you need to understand the basics of the INotifyPropertyChanged interface.

Assuming we have our MainWindowdefined as

<Window x:Class="Application.MainWindow"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://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>

With our Viewmodel-Class MainWindowViewModel defined as

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

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

    }
}

the TextBlock of our Application will display the Text Hello World due to its binding. If our ApplicationStateText changes during runtime, our UI will not be notified of such change.
In order to implement this, our DataSource, in this case our MainWindowViewModel, needs to implement the Interface INotifyPropertyChanged. This will cause our Bindingsto be able to subscribe to the PropertyChangedEvent.
All we need to do is to Invoke the PropertyChangedEventHandler whenever we change our ApplicationStateText Property like this:

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

and make sure, that our Binding of TextBlock.Text actually listens to a PropertyChangedEvent:

<Window x:Class="Application.MainWindow"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://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>

Binding to a Collection of Objects with INotifyPropertyChanged and INotifyCollectionChanged

Let’s assume you have a ListView wich is supposed to display every User object listed under the Users Property of the ViewModel where Properties of the User object can get updated programatically.

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

Despite for INotifyPropertyChanged beeing implemented correctly for the User object

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

and for your 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));
    }
}

your UI wont update, if a change to a User is made programmatically.

This is simply because you have only set INotifyPropertyChanged on the Instance of the List itself. Only if you completely re-instantiate the List if one property of an Element changes your UI will update.

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

This however is very tiresome and unbelievably bad for performance.
If you have a List of 100’000 Elements showing both the ID and Name of the User, there will be 200’000 DataBindings in place wich each have to be re-created. This results in noticable Lag to the User whenever a change is made to anything.

To partly solve this issue, you can use System.ComponentModel.ObservableCollection<T> instead of List<T>:

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

The ObservableCollection provides us with the CollectionChangedEvent and implements INotifyPropertyChanged itself. According to MSDN the Event will rise, ”[..]when an item is added, removed, changed, moved, or the entire list is refreshed“.
You will however quickly come to realize that with .NET 4.5.2 and prior, the ObservableCollection will not raise a CollectionChanged Event if a Property of an Element in the Collection Changes as discussed here.

Following this solution we can simply implement our own TrulyObservableCollection<T> without the INotifyPropertyChanged constraint for T having everything we need and exposing wether T implements INotifyPropertyChanged or not:

/*
 * Original Class by Simon @StackOverflow https://stackoverflow.com/a/5256827/3766034
 * Removal of the INPC-Constraint by Jirajha @StackOverflow 
 * according to to suggestion of nikeee @StackOverflow https://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);
    }
}

and define our Property Users as TrulyObservableCollection<User> in our ViewModel

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

Our UI will now get notified about once a INPC-Property of an element within the Collection changes without the need to re-create every single Binding.


This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow