Custom View Cell

FlorianKrieglFlorianKriegl ATMember ✭✭✭

Hey,
I have a custom view cell for my listview that suppresses the visual tap even when the selection mode is set to none. This custom view cell works perfectly. But now I have a custom list view which should load more items when the user scrolls to the end. When I want to use my custom cell inside this extended listview xf does show the cell but do not suppress the visual tap anymore. Do I have to do some special thing when using a custom listview?

Here is my custom view cell:

    public class CustomViewCell : ViewCell
    {
        public static readonly BindableProperty AllowHighlightProperty =
            BindableProperty.Create(
                "AllowHighlight", typeof(bool), typeof(CustomViewCell),
                defaultValue: true);

        public bool AllowHighlight
        {
            get { return (bool)GetValue(AllowHighlightProperty); }
            set { SetValue(AllowHighlightProperty, value); }
        }
    }

And here is the custom renderer (for iOS):

[assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))]
namespace Questonaut.iOS.CustomRenderer
{
    public class CustomViewCellRenderer : ViewCellRenderer
    {
        UITableViewCell _nativeCell;

        //get access to the associated forms-element and subscribe to property-changed
        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            _nativeCell = base.GetCell(item, reusableCell, tv);

            var formsCell = item as CustomViewCell;

            if (formsCell != null)
            {
                formsCell.PropertyChanged -= OnPropertyChanged;
                formsCell.PropertyChanged += OnPropertyChanged;
            }

            //and, update the style 
            SetStyle(formsCell);

            return _nativeCell;
        }

        void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var formsCell = sender as CustomViewCell;
            if (formsCell == null)
                return;
            //TODO: Trying to find a nicer and more robust way to dispose and unsubscribe :(
            if (_nativeCell == null)
                formsCell.PropertyChanged -= OnPropertyChanged;

            if (e.PropertyName == CustomViewCell.AllowHighlightProperty.PropertyName)
            {
                SetStyle(formsCell);
            }
        }

        private void SetStyle(CustomViewCell formsCell)
        {
            //added this code as sometimes on tap, the separator disappears, if style is updated before tap animation finishes 
            //https://stackoverflow.com/questions/25613117/how-do-you-prevent-uitableviewcellselectionstylenone-from-removing-cell-separato
            Device.StartTimer(TimeSpan.FromMilliseconds(50), () =>
            {
                Device.BeginInvokeOnMainThread(() =>
                {
                    if (formsCell.AllowHighlight)
                    {
                        _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.Default;
                    }
                    else
                        _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None;
                });
                return false;
            });
        }
}

And my extended list view:

 public class ExtendedListView : ListView
    {
        #region constructor
        public ExtendedListView() : this(ListViewCachingStrategy.RecycleElement) { }

        public ExtendedListView(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
        {
            this.ItemAppearing += OnItemAppearing;
            this.ItemTapped += OnItemTapped;
        }
        #endregion

        #region bindable properties
        // ItemAppearing Property and field to allow binding of a custom command in our control
        // directly from our xaml code
        public static readonly BindableProperty ItemApperingCommandProperty =
            BindableProperty.Create(nameof(ItemAppearingCommand), typeof(ICommand), typeof(ExtendedListView), default(ICommand));

        // LoadMoreCommandProperty Property and field, which will be the one we will call
        // If we need to Load more data as our control needs it.
        public static readonly BindableProperty LoadMoreCommandProperty =
            BindableProperty.Create(nameof(LoadMoreCommand), typeof(ICommand), typeof(ExtendedListView), default(ICommand));

        // TappedCommandProperty Property and field, which will be the one we will call
        // If the user taps an item on our listview.
        public static readonly BindableProperty TappedCommandProperty =
            BindableProperty.Create(nameof(TappedCommand), typeof(ICommand), typeof(ExtendedListView), default(ICommand));
        #endregion

        #region properties
        public ICommand ItemAppearingCommand
        {
            get => (ICommand)GetValue(LoadMoreCommandProperty);
            set => SetValue(LoadMoreCommandProperty, value);
        }

        public ICommand LoadMoreCommand
        {
            get => (ICommand)GetValue(LoadMoreCommandProperty);
            set => SetValue(LoadMoreCommandProperty, value);
        }

        public ICommand TappedCommand
        {
            get { return (ICommand)GetValue(TappedCommandProperty); }
            set { SetValue(TappedCommandProperty, value); }
        }
        #endregion

        #region event handler
        /// <summary>
        /// If the new item that is appearing on the screen is the last one in the 
        /// collection, we execute the LoadMoreCommand so our ViewModel knows we
        /// want to load more data for our user from the data service.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnItemAppearing(object sender, ItemVisibilityEventArgs e)
        {
            if (ItemAppearingCommand != null)
            {
                ItemAppearingCommand?.Execute(e.Item);
            }
            if (LoadMoreCommand != null)
            {
                //If its the last item execute command and load more data.
                if (e.Item == ItemsSource.Cast<object>().Last())
                {
                    LoadMoreCommand?.Execute(null);
                }
            }
        }

        private void OnItemTapped(object sender, ItemTappedEventArgs e)
        {
            if (TappedCommand != null)
            {
                TappedCommand?.Execute(e.Item);
            }
        }
        #endregion
    }

And (a part of) the xaml code:

  <controls:ExtendedListView
                    x:Name="timeline"
                    AbsoluteLayout.LayoutBounds="0,0,1,1"
                    AbsoluteLayout.LayoutFlags="All"
                    Footer=""
                    VerticalOptions="FillAndExpand"
                    HasUnevenRows="True"
                    BackgroundColor="Transparent"
                    ItemsSource="{Binding Activities}"
                    LoadMoreCommand="{Binding OnLoadMoreCommand}"
                    SeparatorVisibility="None"
                    SelectionMode="None">
                    <controls:ExtendedListView.ItemTemplate>
                        <DataTemplate>
                            <controls:CustomViewCell x:Name="cello"
                                                     AllowHighlight="false">
                                <ContentView>
                                    <Grid ColumnSpacing="30">

Thanks!

Best Answer

  • FlorianKrieglFlorianKriegl AT ✭✭✭
    Accepted Answer

    @FaizalSaidali
    Thanks for the hint. After some debugging, I found the problem.
    It's the following line

    public ExtendedListView(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
    ...
    

    It seems like there is actually a bug with the caching strategy. There are also a few bugs reported at the git repo. This mean as long as the bug is not resolved I have to do the list view without this feature..

Answers

  • matrixlukanmatrixlukan Member ✭✭

    If your need is just to iterate and build items in ItemSource property then I recommend using RepeaterView.
    You can check this link to see how to implement RepeaterView.

    https://devlinduldulao.pro/how-to-create-repeaterview-in-your-xamarin-forms-app/

  • FaizalSaidaliFaizalSaidali USMember ✭✭✭

    Hi @FlorianKriegl ,
    Can you confirm the SetStyle method is invoking when the new cell created.
    And which XF version you have using? I think, the XF 4.0 have some ListView related issues.

  • FlorianKrieglFlorianKriegl ATMember ✭✭✭

    @FaizalSaidali

    Yes I can confirm that. I've debugged the creating process of the custom view cells and everything is set correctly. If I change the extended list view to the standard on board list view everything works like it should. It just wont work with the custom list view..

    I'm using 4.1.0.618606.

  • FaizalSaidaliFaizalSaidali USMember ✭✭✭

    @FlorianKriegl, I think, your code is extremely perfect. I am also facing some rendering issues in ListView after some commits with missing codes by XF 3.6 and 4.0 release. They can find out and resolved. But still it is not merged to any release.
    I'm not sure your problem comes under XF issue. But you also verify with that.
    Ref: https://github.com/xamarin/Xamarin.Forms/issues

  • FlorianKrieglFlorianKriegl ATMember ✭✭✭
    Accepted Answer

    @FaizalSaidali
    Thanks for the hint. After some debugging, I found the problem.
    It's the following line

    public ExtendedListView(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
    ...
    

    It seems like there is actually a bug with the caching strategy. There are also a few bugs reported at the git repo. This mean as long as the bug is not resolved I have to do the list view without this feature..

  • FaizalSaidaliFaizalSaidali USMember ✭✭✭

    @FlorianKriegl. Great finding. Please mark as answer on your solution it will helps to others

Sign In or Register to comment.