0

If I am understanding ObservableCollection correctly, it binds to objects of a class, and knowns when those objects are added and removed, and updates the UI accordingly, but not necessarily when each element in an object is changed.

I have a simple UI that obtains information of a signal and then displays it. The UI used to update correctly, but I wanted to offload some of the work to a static class SignalInfo, which implements a method called querySignals(), which updates lteInfos. After I did this, my observable collection does not update in the UI. This is my new UI Class:

namespace CellularSignal1;

public partial class DisplaySignalInfo : ContentPage
{
    public ObservableCollection<LTEInfo> lteInfos { get; set; } = new ObservableCollection<LTEInfo>();

    public DisplaySignalInfo()
    {
        InitializeComponent();
        BindingContext = this;
    }

    protected override async void OnAppearing()
    {
        await SignalInfo.querySignals();
        lteInfos = SignalInfo.lteInfos;
    }
}

Where the static class simply updates lteInfos with LTEInfos:

namespace CellularSignal1
{
    public class LTEInfo : ConnectionCellInfo, IEquatable<LTEInfo>
    {
        public int Rssi { get; set; }
        public int Rsrp { get; set; }
        public int Rsrq { get; set; }
        public int Level { get; set; }
        public List<int> Bands { get; set; }
    }
}

This is the xaml of my UI Class:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:CellularSignal1"
             x:Class="CellularSignal1.DisplaySignalInfo"
             x:DataType="local:DisplaySignalInfo"
             Title="Signal">
    <ScrollView>
      <StackLayout>
         <CollectionView ItemsSource="{Binding lteInfos}">
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="local:LTEInfo">
                        <Grid Margin="0,10">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>

                            <Label Grid.Row="0" Text="{Binding Pci,StringFormat='PCI: {0:F0}'}"/>
                            <CollectionView Grid.Row="1" ItemsSource="{Binding Bands}">
                                <CollectionView.ItemTemplate>
                                    <DataTemplate>
                                        <StackLayout Orientation="Horizontal">
                                            <Label Text="{Binding .,StringFormat='Band: {0}'}" />
                                        </StackLayout>
                                    </DataTemplate>
                                </CollectionView.ItemTemplate>
                            </CollectionView>

                            <Label Grid.Row="2" Text="{Binding Level,StringFormat='Level: {0:F0}'}"/>
                            <Label Grid.Row="3" Text="{Binding Rssi,StringFormat='RSSI: {0:F0} dBm'}"/>
                            <Label Grid.Row="4" Text="{Binding Rsrp,StringFormat='RSRP: {0:F0} dBm'}"/>
                            <Label Grid.Row="5" Text="{Binding Rsrq,StringFormat='RSRQ: {0:F0}'}"/>
                        </Grid>

                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
      </StackLayout>
    </ScrollView>

I can see that upon startup, there is at least one LTEInfo inside the collection lteInfos, but the UI doesn't show it at all. I would understand if the info inside lteInfos weren't updated maybe, but the whole collection which has at least one element inside of it is not showing. Any help to understand why my Observable Collection doesn't update the UI anymore is helpful. Thanks!

rickster26ter1
  • 373
  • 3
  • 10
  • 1
    you are binding `lteInfos` **before** you are assigning a value to it. Either set the `BindingContext` **after** you assign the value, or you need to implement `INotifyPropertyChanged` and raise a `PropertyChanged` event in the setter of `lteInfos` – Jason Mar 18 '23 at 16:01
  • Isn't `lteInfos` supposed to know that a value is added to it, and update the binding context accordingly? I thought that was the main purpose of using ObservableCollection, is that it implements `INotifyPropertyChanged` on each of it's members, and updates bindings when something is added or removed from it? – rickster26ter1 Mar 18 '23 at 16:09
  • an `ObservableCollection` only raises events when items are added or removed. In this case the initial value of `lteInfos` is **NULL**, and then you assign a value to it. The `ObservableCollection` has not even been created yet. You need INPC to in order to raise an event when that initial assignment occurs. Or just wait and set `BindingContext` after you assign the value. – Jason Mar 18 '23 at 16:13
  • Your binding attaches to the instance of collection, created in the property's initializer. And after this you replaces the collection with a new instance. But binding does not know anything about this new instance and continues to work with the old one. Generally, I would suggest to make `lteInfos` get-only and just add/remove elements from collection instead of replacing the entire collection object. It can really save many debugging time. – Serg Mar 18 '23 at 16:16
  • Thanks guys, I don't know why I didn't catch the reassignment. Appreciate it! – rickster26ter1 Mar 18 '23 at 16:49
  • You can use the INotifyPropertyChanged interface to notify the UI that the variable lteInfos has changed. And you can refer to this case about [Can't bind a ViewModel ObservableCollection to listview](https://stackoverflow.com/questions/47502327/cant-bind-a-viewmodel-observablecollection-to-listview). Or you can use the Community Toolkit.Mvvm package to make you project be MVVM. – Liyun Zhang - MSFT Mar 20 '23 at 09:16

0 Answers0