Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement IBoxedCell pt2 #32

Open
pfbwitz opened this issue Jan 18, 2016 · 6 comments
Open

implement IBoxedCell pt2 #32

pfbwitz opened this issue Jan 18, 2016 · 6 comments

Comments

@pfbwitz
Copy link

pfbwitz commented Jan 18, 2016

Accidentally closed issue: #31

Sorry!

Original issue:

"Hello,

I'm implementing this library on both Android and iOS (same solution/project). It works fine (super smooth!) in my Android project, but on iOS, my list doesn't load (fast)images and whenever I scroll, the app will crash with this message:

System.InvalidOperationException: Implement IBoxedCell on cell renderer: TwinTechs.Controls.NativeCell, TwinTechsLib.iOS, Version=1.0.5858.22559, Culture=neutral, PublicKeyToken=null

Google isn't helping me at all (it's as if IBoxedCell doesn't exist at all). It's working in the sample project, but it will crash everytime I try to run it on iOS."

Your reply:

"Can you give me the code for your cell and list please?"

My reply:

"Hello,

Thank you for your reply. I have two implementations of your fastcell. I'm using it for the complex fastcell I built, however it's easier to explain with the simplecell, since it's the same problem. Here's the xaml

<?xml version="1.0" encoding="utf-8" ?>
<controls1:FastCell xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:cells="clr-namespace:floricodeapp.App.Views.Cells;assembly=floricodeapp.App"
             xmlns:controls1="clr-namespace:TwinTechs.Controls;assembly=TwinTechsForms"
             x:Class="floricodeapp.App.Views.Cells.MyFastCell">
    <cells:IndexedStackLayout x:Name="Container" VerticalOptions="FillAndExpand" Orientation="Horizontal" Spacing="5" Padding="0" HorizontalOptions="FillAndExpand">
    <controls1:FastImage Aspect="AspectFill" x:Name="Image"/>
    <StackLayout Padding="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
      <Label x:Name ="Omschrijving" VerticalOptions="Start" HorizontalOptions="StartAndExpand" FontAttributes="Bold" LineBreakMode="TailTruncation" />
      <StackLayout Orientation="Horizontal" VerticalOptions="EndAndExpand" Spacing="0">
        <Label x:Name="VbnCode" />
        <BoxView WidthRequest="8" HeightRequest="1" />
        <Image Source="rhs_kleur" HorizontalOptions="Start" VerticalOptions="Center" x:Name="Rhs1" />
        <Label x:Name="RhsKleur1" />
        <BoxView WidthRequest="8" HeightRequest="1" />
        <Image Source="rhs_kleur" HorizontalOptions="Start" VerticalOptions="Center" x:Name="Rhs2" />
        <Label x:Name="RhsKleur2" />
      </StackLayout>
    </StackLayout>
        <Image x:Name="FavImage" Source="fave.png" HorizontalOptions="EndAndExpand" VerticalOptions="Center" Aspect="AspectFit" />
        <Image x:Name="IsNew" HorizontalOptions="End" VerticalOptions="Start" Source="nieuw.png"/>
        </cells:IndexedStackLayout>
</controls1:FastCell>

The accompanying C#:

class IndexedStackLayout : StackLayout
    {
        public int Index { get; set; }
    }

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MyFastCell : FastCell
    {
        protected override void InitializeCell()
        {
            InitializeComponent();

            var item = BindingContext as SearchProductModel;
            var gesture = new TapGestureRecognizer { CommandParameter = item.ParentPage };
            gesture.Tapped += GestureOnTapped;
            Container.GestureRecognizers.Add(gesture);
        }

        protected override void SetupCell(bool isRecycled)
        {
            var item = BindingContext as SearchProductModel;

            if (item != null)
            {
                Container.Index = item.Index;
                Height = Constants.ThumbRenderSize;
                Image.ImageUrl = string.Format(Constants.ThumbnailUrl, item.VbnCode) ?? "";

                if (Device.OS == TargetPlatform.Android)
                {
                    Image.VerticalOptions = LayoutOptions.Center;
                    Image.HorizontalOptions = LayoutOptions.Start;
                    Image.HeightRequest = Constants.ThumbRenderSize;
                    Image.WidthRequest = Constants.ThumbRenderSize;
                }

                Omschrijving.Text= item.Omschrijving;
                Omschrijving.FontSize = Device.GetNamedSize(NamedSize.Small, typeof (Label));

                IsNew.Opacity = item.Opacity;

                Rhs1.BackgroundColor = item.RhsKleur1Color;
                Rhs1.IsVisible = item.Rhs1Visible;

                Rhs2.BackgroundColor = item.RhsKleur2Color;
                Rhs2.IsVisible = item.Rhs2Visible;

                RhsKleur1.TextColor = Constants.DetailTextColor;
                RhsKleur1.Text = item.RhsKleur1;
                RhsKleur2.TextColor = Constants.DetailTextColor;
                RhsKleur2.Text = item.RhsKleur2;

                FavImage.IsVisible = item.IsFavorite;

                VbnCode.Text = item.VbnCodeWithPoundSign;
                VbnCode.TextColor = Constants.DetailTextColor;
            }
        }

        private void GestureOnTapped(object sender, EventArgs eventArgs)
        {
            var s = (IndexedStackLayout)sender;
            var parent = (PeterListView)((TappedEventArgs)eventArgs).Parameter;

            parent.Navigation.PushAsync(new ProductPage(parent.Producten.Select(p => p.VbnCode).ToList(), s.Index, parent.TotalCount, parent.ParentPage, this));
        }
    }

The Viewmodel:

    public class SearchProductKleurModel : BaseModel
    {
        public int VbnCode { get; set; }
        public string RhsCode { get; set; }
        public int R { get; set; }
        public int G { get; set; }
        public int B { get; set; }
    }

    public class SearchProductModel : BaseModel
    {
        public byte[] Icon;

        public PeterListView ParentPage { get; set; }
        public int Index { get; set; }

        private List<SearchProductKleurModel> _kleuren;
        public List<SearchProductKleurModel> Kleuren
        {
            get
            {
                if (_kleuren == null)
                {
                    _kleuren = new List<SearchProductKleurModel>();
                    if (!string.IsNullOrEmpty(RhsKleur))
                    {
                        var arr = RhsKleur.Split(new []{'~'},StringSplitOptions.RemoveEmptyEntries);

                        foreach (var details in arr)
                        {
                            var detail = details.Split('_');
                            int r, g, b;
                            r = g = b = 255;
                            try
                            {
                                if (detail.Count() > 1)
                                {
                                    b = Convert.ToInt32(detail[3]);
                                    g = Convert.ToInt32(detail[2]);
                                    r = Convert.ToInt32(detail[1]);
                                }
                                _kleuren.Add(new SearchProductKleurModel
                                {
                                    VbnCode = VbnCode,
                                    B = b,
                                    G = g,
                                    R = r,
                                    RhsCode = detail[0]
                                });
                            }
                            catch (Exception ex)
                            {
                                //throw ex;
                            }
                        }
                    }

                }
                return _kleuren;
            }
        }

        public Color RhsKleur1Color
        {
            get
            {
                var s = Color.White;
                if (Kleuren.Any())
                {
                    var k = Kleuren.First();
                    s = Color.FromRgb(k.R, k.G, k.B);
                }
                return s;
            }
        }

        public Color RhsKleur2Color
        {
            get
            {
                var s = Color.White;
                if (Kleuren.Count > 1)
                {
                    var k = Kleuren.ElementAt(1);
                    s = Color.FromRgb(k.R, k.G, k.B);
                }
                return s;
            }
        }

        public string RhsKleur1
        {
            get
            {
                var s = string.Empty;
                if (Kleuren.Any())
                {
                    s = Kleuren.First().RhsCode;
                }
                return s;
            }
        }

        public string RhsKleur2
        {
            get
            {
                var s = string.Empty;
                if (Kleuren.Count > 1)
                {
                    s = Kleuren.ElementAt(1).RhsCode;
                }
                return s;
            }
        }

        private string _omschrijving;
        public string Omschrijving
        {
            get { return _omschrijving; }
            set
            {
                _omschrijving = value;
                OnPropertyChanged();
            }
        }

        private int _vbnCode;
        public int VbnCode
        {
            get { return _vbnCode; }
            set
            {
                _vbnCode = value;
                OnPropertyChanged();
            }
        }

        public string VbnCodeWithPoundSign
        {
            get { return "#" + _vbnCode; }
        }

        private string _rhskleur;
        public string RhsKleur
        {
            get { return _rhskleur; }
            set
            {
                _rhskleur = value;
                OnPropertyChanged();
            }
        }

        private bool _isnieuw;
        public bool IsNieuw
        {
            get { return _isnieuw; }
            set
            {
                _isnieuw = value;
                OnPropertyChanged();
            }
        }

        public string Thumbnail { get; set; }
        private IWebServiceHelper _wsHelper;
        public ImageSource ImageSource
        {
            get
            {
                return new UriImageSource{ CachingEnabled = false, Uri=new Uri(Thumbnail)};
//                return ImageSource.FromUri(new Uri(Thumbnail));
            }
        }

        public int Opacity
        {
            get { return IsNieuw ? 1 : 0; }
        }

        public bool Rhs1Visible
        {
            get { return Kleuren.Any(); }
        }

        public bool Rhs2Visible
        {
            get { return Kleuren.Any() && Kleuren.Count > 1; }
        }

        private bool _isFavorite;
        public bool IsFavorite
        {
            get { return _isFavorite; }
            set
            {
                _isFavorite = value;
                OnPropertyChanged();
            }
        }

        public override string ToString()
        {
            return Omschrijving;
        }
    }

The listview is simple custom control I made to enable infinite scrolling. It's simple and tiny, but does the job as well as examples I found elsewhere with a lot more code and complexity:

 public class InfiniteListView<T> : ListView
    {
        public bool CurrentlyAdding;

        public InfiniteListView()
            : base(ListViewCachingStrategy.RecycleElement)
        {
            ItemAppearing += (sender, args) =>
            {
                var s = ItemsSource.Cast<T>().ToList();
                if (CurrentlyAdding || !s.Any()) 
                    return;

                if (s.IndexOf((T)args.Item) == s.Count - 1)
                    OnEndReached(null);
            };
        }

        public event EventHandler EndReached;
        private void OnEndReached(EventArgs e)
        {
            var handler = EndReached;
            if (handler != null)
                handler(this, e);
        }

        public void SelectItem(int index)
        {
            try
            {
                ScrollTo(ItemsSource.Cast<T>().ElementAt(index), ScrollToPosition.Center, false);
            }
            catch
            {
            }
        }
    }

The listview isn't directly place on a page, but rather in a contentview, embedded on a page:

  public void LoadList()
        {
            InfiniteList.Clear();
            InfiniteList = new PeterListView(this, this is FavorietenPage);
            _listHolder.Content = InfiniteList;
            InfiniteList.Reset();
        }

The contentview which holds the list is a xaml-file. The list is embedded like this:

<controls:InfiniteListView x:Name="listView" x:TypeArguments="model:SearchProductModel" />

The list is bound to data with an Init()-method:

private void Init(HomePage parentPage)
        {
            ParentPage = parentPage;
            Producten = new ObservableCollection<SearchProductModel>();
            LoadData();
            if(Device.OS == TargetPlatform.Android)
                listView.ItemTemplate = new DataTemplate(typeof(MyFastCell));
            else
                listView.ItemTemplate = new DataTemplate(typeof(MyCell));

            listView.ItemsSource = Producten;
            listView.EndReached += (sender, args) =>
            {
                if (Producten.Count < TotalCount)
                {
                    listView.CurrentlyAdding = true;
                    AddProducts(Producten.Count);
                }
            };
            listView.ItemTapped  += ListViewOnItemTapped;
            listView.ItemSelected += (sender, args) => listView.SelectedItem = null; 

            BindingContext = this;
        }

The LoadData()- and AddProducts()-methods simply add data to the ObservableCollection.

public ObservableCollection<SearchProductModel> Producten { get; set; }

I hope this is enough information. Now I'm forced to utilize the regular Viewcells on iOS, making the Android-version of the app perform much better. (iOS takes 3-5 seconds to load a new dataset from the observablecollection)"

@pfbwitz
Copy link
Author

pfbwitz commented Jan 18, 2016

iboxedcell

@arielbh
Copy link

arielbh commented Jan 18, 2016

Having the same issue.

@pfbwitz
Copy link
Author

pfbwitz commented Jan 18, 2016

Fixed it for me!

I changed the constructor of my listview as such:

  public InfiniteListView()
            //: base(ListViewCachingStrategy.RecycleElement)
        {

Inheriting the default constructor of the ListView fixed the issue for me :)

@arielbh
Copy link

arielbh commented Jan 18, 2016

@pfbwitz wait, you removed the RecycleElement strategy from your ListView?

@pfbwitz
Copy link
Author

pfbwitz commented Jan 18, 2016

Yep. I did exactly that. I reckon the fast cell renderer manages it's own recycling and perhaps in conflicted with the Xamarin Forms cachingstrategy.

@arielbh
Copy link

arielbh commented Jan 18, 2016

I've started reviewing @georgejecook's code. I'm still unconvinced this is the way to go. I hope @georgejecook will drop by and explain it for us as he is done on many occasions via forum and posts :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants