CustomeViewCellRenderer doesn't work when set CachingStrategy="RecycleElement"

Xie Steven 831 Reputation points
2021-05-18T09:42:12.823+00:00

Hi,

I want to change the selected background color of ListView.

I made a custom ViewCell and renderer.

[assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
namespace basmob.Droid.Renders
{
    public class ExtendedViewCellRenderer : ViewCellRenderer
    {

        private Android.Views.View _cellCore;
        private bool _selected;
        private Drawable _unselectedBackground;
        protected override Android.Views.View GetCellCore(Cell item,
                                                          Android.Views.View convertView,
                                                          ViewGroup parent,
                                                          Context context)
        {
            _cellCore = base.GetCellCore(item, convertView, parent, context);
            _unselectedBackground = _cellCore.Background;
            _selected = false;
            return _cellCore;
        }

        protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            base.OnCellPropertyChanged(sender, args);

            if (args.PropertyName == "IsSelected")
            {
                _selected = !_selected;

                if (_selected)
                {
                    var extendedViewCell = sender as ExtendedViewCell;
                    _cellCore.SetBackgroundColor(extendedViewCell.SelectedBackgroundColor.ToAndroid());
                }
                else
                {
                    _cellCore.SetBackground(_unselectedBackground);
                }
            }
        }
    }
}

public class ExtendedViewCell : ViewCell
    {
        public static readonly BindableProperty SelectedBackgroundColorProperty =
            BindableProperty.Create("SelectedBackgroundColor",
                                    typeof(Color),
                                    typeof(ExtendedViewCell),
                                    Color.Default);

        public Color SelectedBackgroundColor
        {
            get { return (Color)GetValue(SelectedBackgroundColorProperty); }
            set { SetValue(SelectedBackgroundColorProperty, value); }
        }
    }

If I set CachingStrategy="RetainElement", the selected background color will have strange behavior. For example, I selected one item, the background is correct at that time. Then, If I scroll down and up the list, the background color will revert to the default color(Orange), and many other items' background color will be the color that I set in 'ExtendedViewCellRenderer'.

If I set CachingStrategy="RecycleElement", I did not see that strange behavior, but the the custom color will never be set for item. After debugging, I found the 'OnCellPropertyChanged' handler method will always be invoked by the 'Index' property name, instead of the 'IsSelected'.

Please help me do troubleshooting. My final target is to set the selected item background color. If you have a better way, please tell me.

I'm using Xamarin.Forms 5.0.0.2012

Thanks,
Steven

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,377 questions
0 comments No comments
{count} votes

Accepted answer
  1. JarvanZhang 23,971 Reputation points
    2021-05-19T04:02:32.247+00:00

    Hello,​

    Welcome to our Microsoft Q&A platform!

    CustomeViewCellRenderer doesn't work when set CachingStrategy="RecycleElement"

    This is because when developers specify RecycleElement, OnElementChanged events will not raised when cells are recycled. Check the doc:
    https://learn.microsoft.com/en-us/dotnet/api/xamarin.forms.listviewcachingstrategy?view=xamarin-forms#Xamarin_Forms_ListViewCachingStrategy_RetainElement

    To change the background color of the selected items, try to set data binding for the BackgroundColor of the item's layout control. Then detect the ItemTapped to update the value.

       <ListView ItemsSource="{Binding DataCollection}" ItemTapped="ListView_ItemTapped" CachingStrategy="RecycleElement">  
           <ListView.ItemTemplate >  
               <DataTemplate>  
                   <ViewCell>  
                       <StackLayout BackgroundColor="{Binding SelectedColor}">  
                           <!--the views-->  
                       </StackLayout>  
                   </ViewCell>  
               </DataTemplate>  
           </ListView.ItemTemplate>  
       </ListView>  
         
       //page.xaml.cs  
       public partial class TestPage : ContentPage  
       {  
           TestPageViewModel viewModel;  
           public TestPage()  
           {  
               InitializeComponent();  
         
               viewModel = new TestPageViewModel();  
               BindingContext = viewModel;  
           }  
         
           private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)  
           {  
               foreach (var item in viewModel.DataCollection)  
               {  
                   item.SelectedColor = Color.White;  
               }  
               var model = e.Item as TestPageModel;  
               model.SelectedColor = Color.Red;  
           }  
       }  
         
       //model class  
       public class TestPageModel : INotifyPropertyChanged  
       {  
           ...  
           private Color selectedColor;  
           public Color SelectedColor  
           {  
               get  
               {  
                   return selectedColor;  
               }  
               set  
               {  
                   if (selectedColor != value)  
                   {  
                       selectedColor = value;  
                       NotifyPropertyChanged("SelectedColor");  
                   }  
               }  
           }  
           protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")  
           {  
               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
           }  
           public event PropertyChangedEventHandler PropertyChanged;  
       }  
    

    Best Regards,

    Jarvan Zhang


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.