Search code examples
c#xamarinxamarin.formsxamarin.androidxamarin.ios

Xamarin Forms ListView Bindings don't get updated


I have a problem. I created a ListView with a ViewModel. In my ListView I have a few Labels with Text that is bound to the objects in the ItemSource. Now when I change a value in the ViewModel of an item in the ObservableCollection, nothing changes on the screen!

Here is my ViewModel:

public class VM_DeviceList : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;


    public ICommand cmdDeleteDevice
    {
        get
        {
            return new Command<int>((x) => DeleteDevice_Handler(x));
        }
    }

    public ICommand cmdTogglePower
    {
        get
        {
            return new Command<int>((x) => TogglePower_Handler(x));
        }
    }


    private ObservableCollection<DisplayedDevice> _knownDeviceList;
    public ObservableCollection<DisplayedDevice> knownDeviceList
    {
        get
        {
            return _knownDeviceList;
        }
        set
        {
            if (_knownDeviceList != value)
            {
                _knownDeviceList = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("knownDeviceList"));
                }
            }
        }
    }

Here is my class for the ObservableCollection:

public class DisplayedDevice : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string State { get; set; }
    public string StateShown { get; set; }
    public string deviceImage { get; set; }
    public string Color { get; set; }
    public string PowerStateColor { get; set; }
    public string DeviceImageColor { get; set; }

    public string DeviceImage
    {
        get
        {
            return deviceImage;
        }

        set
        {
            deviceImage = value;
            OnPropertyChanged();
        }
    }
}

And here is the xaml:

<ListView ItemsSource="{Binding knownDeviceList}" SelectionMode="None" RowHeight="90" ItemTapped="device_Clicked" x:Name="MyListView">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <AbsoluteLayout HeightRequest="70" Margin="20,10,20,10">
                    <StackLayout Opacity="0.3" BackgroundColor="White"
                        AbsoluteLayout.LayoutBounds="0,0,1,1" 
                        AbsoluteLayout.LayoutFlags="All" />
                    <StackLayout AbsoluteLayout.LayoutBounds="0,0,1,1" 
                        AbsoluteLayout.LayoutFlags="All">
                        <Grid RowSpacing="0">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="35" />
                                <RowDefinition Height="35" />
                            </Grid.RowDefinitions>

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="70" />
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="70" />
                            </Grid.ColumnDefinitions>

                            <Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}" Margin="0,3,0,0"
                        FontAttributes="Bold" FontSize="24" TextColor="White" />
                            <Label Grid.Row="1" Grid.Column="1" Text="{Binding StateShown}" FontSize="18" TextColor="White" />

                            <!-- <Image Source="power" Grid.RowSpan="2" Grid.Column="2" Margin="5" /> -->
                            <controls:IconView x:Name="btnPower" Source="power" Grid.RowSpan="2" Grid.Column="2" Margin="5"
                                            Foreground="{Binding PowerStateColor}">
                                <controls:IconView.GestureRecognizers>
                                    <TapGestureRecognizer Command="{Binding Path=BindingContext.cmdTogglePower, Source={x:Reference MyListView}}" CommandParameter="{Binding Id}" />
                                </controls:IconView.GestureRecognizers>
                            </controls:IconView>
                        </Grid>
                    </StackLayout>
                </AbsoluteLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Now what I expected to happen, was that when I click on the IconView I change the IconView color and the label with the StateShown binding. But nothing changes when I click on the IconView!

What am I doing wrong?


Solution

  • Add OnPropertyChanged method call to every property on DisplayedDevice for those you want the UI to notice.

    public class DisplayedDevice : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        private string name;
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
                OnPropertyChanged();
            }
        }
    
        private int id;
        public int Id
        {
            get
            {
                return id;
            }
            set
            {
                Id = value;
                OnPropertyChanged();
            }
        }
    
        private string state;
        public string State
        {
            get
            {
                return state;
            }
            set
            {
                state = value;
                OnPropertyChanged();
            }
        }
        ......
    
    }