Custom View Cell: View not getting updated in case PropertyChanged event

I have a listview which consumes custom view cell.
However View is not getting updated in case of property change event is triggered .
I can see that View Model is getting updated values in case event is triggered but my View is not getting updated.

CustomViewCell.xaml:

  <Label HorizontalTextAlignment="Start"
                                   VerticalTextAlignment="End"
                                   FontSize="16"
                                   FontAttributes="Bold"
                                   LineBreakMode="WordWrap"
                                   Grid.Column="0"
                                   Grid.Row="0"
                                   Text="{Binding TextMain}"
                                   x:Name="MainTitleLabel"/>
  <Label HorizontalTextAlignment="Start"
                                   VerticalTextAlignment="Center"
                                   FontSize="14"
                                   Grid.Column="0"
                                   Grid.Row="1"
                                   Text="{Binding TextDetail}"
                                   x:Name="TextDetailLabel"/>
  <Image VerticalOptions="Center"
                                   HorizontalOptions="EndAndExpand"
                                   HeightRequest="24"
                                   WidthRequest="24"
                                   IsOpaque="True"
                                   Grid.Column="2"
                                   Grid.Row="0"
                                   Grid.RowSpan="2"
                                   Source="{Binding ImageSource}}"
                                   x:Name="ImageSourceLabel"/>

CustomViewCell.xaml.cs:

public CustomViewCell()
{
  InitializeComponent();
}

public static BindableProperty MainTitleProperty = BindableProperty.Create(nameof(MainTitle), typeof(string), typeof(CustomViewCell));
public string MainTitle
{
  get => (string)GetValue(MainTitleProperty);
  set => SetValue(MainTitleProperty, value);
}    

public static BindableProperty TitleDetailProperty = BindableProperty.Create(nameof(TitleDetail), typeof(string), typeof(CustomViewCell));
public string TitleDetail
{
  get => (string)GetValue(TitleDetailProperty);
  set => SetValue(TitleDetailProperty, value);      
}

public static BindableProperty ConnectionImageSourceProperty = BindableProperty.Create(nameof(ConnectionImageSource), typeof(Image), typeof(CustomViewCell));
public Image ConnectionImageSource
{
  get => (Image)GetValue(ConnectionImageSourceProperty);
  set => SetValue(ConnectionImageSourceProperty, value);
}
protected override void OnBindingContextChanged()
{
  MainTitleLabel.Text = MainTitle;
  DetailLabel.Text = TitleDetail;
  ImageSourceLabel.Source = ConnectionImageSource.Source;
  base.OnBindingContextChanged();
}   

CustomViewCellViewModel .cs:

public class CustomViewCellViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

private string _mainTitleText = null;
public string MainTitleText
{
  get { return _mainTitleText; }
  set
  {
    if (_mainTitleText == null || !_mainTitleText.Equals(value))
    {
      _mainTitleText = value;
      OnPropertyChanged("MainTitleText");
    }
  }
}

private string _titleDetailText = null;
public string TitleDetailText
{
  get { return _titleDetailText; }
  set
  {
    if (_titleDetailText == null || !_titleDetailText.Equals(value))
    {
      _titleDetailText = value;
      OnPropertyChanged("TitleDetailText");
    }
  }
}

private string _imageSource = null;
public string ImageSource
{
  get { return _imageSource; }
  set
  {
    if (_imageSource == null || !_imageSource.Equals(value))
    {
      _imageSource = value;
      OnPropertyChanged("ImageSource");
    }
  }
}

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    OnBindingContextChanged will only be called when the binding context is changed. So when you set the Label's text or image's source in that event with the view cell's bindable property, it will work at initializing time. But it failed when you want to update the model's value as the binding context hasn't been changed.
    I notice that you are using binding to update the control's value, I think there's no need to add more bindable properties for setting values.
    Modify your custom view cell: comment your bindable property and use the binding directly.

    public partial class CustomViewCell : ViewCell
    {
        public CustomViewCell ()
        {
            InitializeComponent ();
        }
    
        //public static BindableProperty MainTitleProperty = BindableProperty.Create(nameof(MainTitle), typeof(string), typeof(CustomViewCell));
        //public string MainTitle
        //{
        //    get => (string)GetValue(MainTitleProperty);
        //    set => SetValue(MainTitleProperty, value);
        //}
    
        //public static BindableProperty TitleDetailProperty = BindableProperty.Create(nameof(TitleDetail), typeof(string), typeof(CustomViewCell));
        //public string TitleDetail
        //{
        //    get => (string)GetValue(TitleDetailProperty);
        //    set => SetValue(TitleDetailProperty, value);
        //}
    
        //public static BindableProperty ConnectionImageSourceProperty = BindableProperty.Create(nameof(ConnectionImageSource), typeof(Image), typeof(CustomViewCell));
        //public Image ConnectionImageSource
        //{
        //    get => (Image)GetValue(ConnectionImageSourceProperty);
        //    set => SetValue(ConnectionImageSourceProperty, value);
        //}
        //protected override void OnBindingContextChanged()
        //{
        //    MainTitleLabel.Text = MainTitle;
        //    TextDetailLabel.Text = TitleDetail;
        //    ImageSourceLabel.Source = ConnectionImageSource.Source;
        //    base.OnBindingContextChanged();
        //}
    }
    

    Xaml:

    <Label HorizontalTextAlignment="Start"
            VerticalTextAlignment="End"
            FontSize="16"
            FontAttributes="Bold"
            LineBreakMode="WordWrap"
            Grid.Column="0"
            Grid.Row="0"
            Text="{Binding MainTitleText}"/>
    <Label HorizontalTextAlignment="Start"
            VerticalTextAlignment="Center"
            FontSize="14"
            Grid.Column="0"
            Grid.Row="1"
            Text="{Binding TitleDetailText}"/>
    <Image VerticalOptions="Center"
            HorizontalOptions="EndAndExpand"
            HeightRequest="24"
            WidthRequest="24"
            IsOpaque="True"
            Grid.Column="2"
            Grid.Row="0"
            Grid.RowSpan="2"
            Source="{Binding ImageSource}"/>
    

    At last you can use the custom view cell:

    <ListView x:Name="MyListView" HasUnevenRows="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <local:CustomViewCell/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    Set the list view's items source:

    List<CustomViewCellViewModel> list = new List<CustomViewCellViewModel>();
    for (int i = 0; i < 10; i++)
    {
        list.Add(new CustomViewCellViewModel { MainTitleText = "Main", TitleDetailText = "Detail", ImageSource = "image.png" });
    }
    MyListView.ItemsSource = list;
    

    If you want to update the value, change the list's value.

Sign In or Register to comment.