Trigger PropertyChanged of indexed property in Xamarin Forms


Xamarin Forms supports binding to indexed properties. But do you know how to trigger a PropertyChanged event on such a property so that the binding engine gets the updated value?

Binding to indexed properties

Binding to an indexed property in Xamarin Forms is simple. Imagine my ViewModel has an indexed property:

Dictionary<string, string> Data { get; } = new Dictionary<string, string>()
{
    { "123", "Pie" },
    { "456", "Eating" },
    { "789", "Ninjas" }
};
public string this[string id]
{
    get
    {
        return Data[id];
    }
}

I can simply bind to it using the following syntax: <Label Text="{Binding [123]}" />  where 123 is the index I pass to the property.

Notifying the UI

If I now update the underlying data collection (for example: Data["123"] = "Ninjas"; ), my UI won’t be notified about this change. However, I can notify my UI by triggering the PropertyChanged event, passing ‘Item’ as property name:

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));

You can also use the following syntax if you only want to notify the UI about one particular element that has changed:

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[123]"));

Easy, right? You just have to know it… ;-)

If you want to try it our yourself, here is the complete code:

MainPage.xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App7"
             x:Class="App7.MainPage">
    <StackLayout>
        <Label Text="{Binding [123]}" />
        <Label Text="{Binding [456]}" />
        <Label Text="{Binding [789]}" />
        <Button Text="Update" Command="{Binding UpdateCommand}" />
        <Button Text="Trigger" Command="{Binding TriggerUpdateCommand}" />
    </StackLayout>
</ContentPage>

MainPage.xaml.cs

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        this.BindingContext = new MainPageViewModel();
        InitializeComponent();
    }
}

MainPageViewModel.cs

public class MainPageViewModel : INotifyPropertyChanged
{
    Dictionary<string, string> Data { get; } = new Dictionary<string, string>()
    {
        { "123", "Pie" },
        { "456", "Eating" },
        { "789", "Ninjas" }
    };
    public string this[string id]
    {
        get
        {
            return Data[id];
        }
    }

    public ICommand UpdateCommand { get; private set; }
    public ICommand TriggerUpdateCommand { get; private set; }

    public MainPageViewModel()
    {
        UpdateCommand = new Command(OnUpdate);
        TriggerUpdateCommand = new Command(OnTrigger);
    }

    private void OnUpdate(object obj)
    {
        Data["123"] = "Ninjas";
        Data["789"] = "Pie";
    }

    private void OnTrigger(object obj)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Did you enjoy reading this? If my work has saved you time or taught you something new, consider buying me a coffee.

Join the discussion

Archived comments (4)

Archived comments from the original blog, preserved here for reference. Join the discussion above.

  1. HARI RAJAGOPAL
    Hi Pieter, Thanks for this tip and the complete sample code, came in handy. Follow up question, Is there a way to bind to more than one indexed property in a viewmodel? Indexer argument can be overloaded but the xaml is only going to pass in a string, so I am not sure how more than one indexer can be present in a view model. Your thoughts appreciated, Thanks Hari
  2. PieEatingNinjasauthorIn reply
    Hi Hari, I'm afraid that this is not possible. You could perfectly well define a second indexed property (int as indexer argument), but the binding engine won't know which indexed property to pick, resulting in a runtime exception. Glad my post could be of any help to you!