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:
<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>
public partial class MainPage : ContentPage { public MainPage() { this.BindingContext = new MainPageViewModel(); InitializeComponent(); } }
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; }
October 11, 2017 at 8:39 pm
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
October 12, 2017 at 7:28 am
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!