Following this example to create a grouping for CollectionView, I notice that none of the properties are INotifyPropertyChanged, nor is the base class an ObservableCollection.
While the latter is easy to fix by changing List to ObservableCollection:
public class AnimalGroup : ObservableCollection<Animal>
{
public string Name { get; private set; }
public AnimalGroup(string name, ObservableCollection<Animal> animals) : base(animals)
{
Name = name;
}
private string _someOtherPropertyIWantToChangeAtRuntime = "hey";
public string SomeOtherPropertyIWantToChangeAtRuntime { get => _someOtherPropertyIWantToChangeAtRuntime, set => SetProperty(ref _someOtherPropertyIWantToChangeAtRuntime, value); }
}
It isn't clear how to make Name, or any other property (e.g. SomeOtherPropertyIWantToChangeAtRuntime), I want to associate with the group as an INotifyPropertyChanged. Treating it is as a normal class by adding the interface to base causes this warning:
Base interface 'INotifyPropertyChanged' is redundant because AnimalGroup inherits 'ObservableCollection'
Yet, there is nothing for the setter to call, such as SetProperty(ref _name, Value) and the existing PropertyChanged object is just for monitoring a group's collection changes. It isn't invokable, just handleable.
If I ignore the warning and implement INotifyPropertyChanged anyway (and name my event PropChanged to avoid colliding with ObservableCollection.PropertyChanged),
protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName]string propertyName = "", Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
PropChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
public event PropertyChangedEventHandler PropChanged;
and let my ViewModel manage the value of SomeOtherPropertyIWantToChangeAtRuntime, the bound <Label>
never sees any changes.
<CollectionView ItemsSource="{Binding AnimalGroups}" HorizontalOptions="FillAndExpand">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label
Text="{Binding Name}"
HorizontalOptions="Start"
FontSize="24.44"
TextColor="Black"
FontAttributes="Bold"
Margin="0,0,0,10"/>
<Label
Text="{Binding SomeOtherPropertyIWantToChangeAtRuntime}" FontSize="15"
TextColor="Black"
Margin="0,0,0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.FindGroupAndChangeTextCommand, Source{x:Reference thisPageName}" CommandParameter="{Binding Name}"/>
</Label.GestureRecognizers>
</Label>
...
ViewModel:
public ObservableCollection<AnimalGroup> AnimalGroups {get; private set;}
public ICommand FindGroupAndChangeTextCommand {get; private set;}
public void FindGroupAndChangeText(string name)
{
var group = AnimalGroups.FirstOrDefault(t => t.Name == name);
if (group != null)
group.SomeOtherPropertyIWantToChangeAtRuntime = DateTime.Now.ToString();
}
ViewModel()
{
AnimalGroups = LoadData(); // not shown
FindGroupAndChangeTextCommand = new Command(FindGroupAndChangeText);
}
The result is that the label remains "hey" (which is the default value) and never changes even though I can see that the above command fires and the code finds the group and sets the text.