Forum Xamarin.Forms

ListView Renderers

ArmandCharbonnierArmandCharbonnier FRMember ✭✭
edited December 2015 in Xamarin.Forms

Hi everyone,

I've made a sample App to test Xamarin.Forms ListView renderers because i've got some updating issues in a "production app" and i noticed something weird with bindings.

I'm using Xamarin.Forms 2.0, Xamarin.Android(Android 5+ Lollipops) and Xamarin.IOS (iOS 8+) and I followed this documentation
https://developer.xamarin.com/guides/cross-platform/xamarin-forms/custom-renderer/listview/

Here's what i've got:

  • A NativeListView subclass of ListView
  • An Android ListViewRenderer with his Adapter and a customCell.axml
  • An iOS ListViewRenderer with his Source and customCell.cs
  • A CellModel.cs which has only 2 properties (Title, Description)
  • A simple ViewModel (inherits from ViewModelBase because i use MVVMLight) containing an ObservableCollection, and 3 commands (Add, Reset, Clear).
  • A simple ContentPage with the NativeListView inside bound on the ViewModel collection property, and 3 buttons bound to the VM Commands.

Here's what happens:

  • If I use a Xamarin.Forms ListView bounds to the viewmodel, it's working fine, the page/listview are updated according to the VM's datasource.
  • If i use the NativeListView bounds to the viewmodel, and a renderer, both on iOS and Android, when I add / remove an item from the VM's datasource, it doesn't update at all. I never go in the Renderer OnElementPropertyChanged.
  • If i use the NativeListView bounds to the viewmodel and a renderer, both on iOS and Android, when I set the VM's datasource property, it does update the ListView.

So in my opinion it's not working as expected. I shouldn't have to reset the VM's datasource property each time i want to add / remove an object.

Am i missing something ?

I've attached the sample project below if you want to see the code.

Thanks,

Best regards,

Armand

PS : In the example, they use an Items BindableProperty on the NativeListView, but the result was the same so i just removed it and used the build in ItemSource ListView property.

Posts

  • VitorLeitaoVitorLeitao USMember

    You might be forgetting to call NotifyDatasetChange when you delete/edit an article on the native list view

  • ArmandCharbonnierArmandCharbonnier FRMember ✭✭
    edited December 2015

    Hi @VitorLeitao,

    Thanks, but where should i call this ? And shouldn't that be automatic when you edit an ObservableCollection with listview databinding ? Because without any renderer, the view is notified. But when i'm using the renderer, i never reach any property changed event in the Renderer.

    According to the documentation, OnElementPropertyChanged of the renderer has to be overriden to be able to catch any change with databinding.

  • VitorLeitaoVitorLeitao USMember

    On our case (we have a native list also) we have to subscribe to the CollectionChange (or CollectionChanged, I am not sure if it has the d) of the ObservableCollection, and on the handler we have to call the ADAPTER.NotifyDatasetChanged (all this on the Android app). This makes it perform the update on the list.

    If you are using MVVMLight you have a helper called GetAdapter (an extension for ObservableCollection) which gives you an adapter, in which you pass a function that should return the view for the cell.

  • @VitorLeitao ,

    Thank i know what you mean, i wanted to try your solution but, i'm using Xamarin.Forms and my ViewModel / ObservableCollection are in the PCL, not in the Xamarin.Android project. I can't find the GetAdapter extension because I guess it's an MVVMLight Xamarin.Android extension :/

    I do think it's a notifier / raise event issue, but i can't find where or what to notify at the moment. Shame it's not automatic as the XF original ListView though.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Are you handling the CollectionChanged event in your renderer?

  • ArmandCharbonnierArmandCharbonnier FRMember ✭✭
    edited December 2015

    @adamkemp

    No i'm not, it wasn't in the doc so i thought i would work without doing it ? And my ObservableCollection is in my ViewModel, how should i access that in the renderer ?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    The docs don't cover how to do completely custom ListView renderers.

    The renderer can access it's PCL element using its Element property. There are two scenarios you need to handle. First, the ItemsSource property could change. That you handle using the PropertyChanged event. Second, the contents of the collection could change. For that you need to check when a new collection is assigned (in the property changed handler) if that collection implements INotifyCollectionChanged, and it so attach to the CollectionChanged event. That event handler notifies you that smaller changes were made to the collection, and you have to adjust the renderer accordingly.

    The built in ListView renderer does all of this for you, and since it's not trivial to implement you would likely be better off finding a way to use it instead of trying to replace it.

  • Thanks, @adamkemp,

    I see now, and yes it was missing. I know the build it ListView does it all, but i have a huge data source and it's not really smooth on my phone when i'm scrolling so i'd rather render it natively.

    I think the Xamarin Team should add this collection changed handling in the documentation because it's a common use case. You won't replace the whole collection each time you use it but rather handle the change inside it in my opinion.

    I'll try your solution and tell you if it get it to work properly !

  • adamkempadamkemp USInsider, Developer Group Leader mod

    As I said, the documentation doesn't cover the complexities of building a ListView renderer. That's a more advanced topic than most people will need.

    Have you tried the RecycleElement option in Forms 2.0?

  • @adamkemp i just implemented what you suggested and it works fine.

    Thanks for your answers :).

    No i didn't try it yet, i'll make some comparison when i'll be over with renderers !

  • adamkempadamkemp USInsider, Developer Group Leader mod

    I recommend trying it immediately. If it's fast enough you could save yourself a lot of time by avoiding writing your own renderer.

  • Hi again @adamkemp,

    Well today i made a comparison between 3 listview with a tab Page containing a page with a listview of each types : RetainElement, RecycleElement, Native.

    Each listview is bound to a ViewModel datasource of 1000 elements. I've made a simple cell with 1 icons and 2 texts. And tested only on Android atm. It seems the RecycleElement is a little bit smoother, but really it's nothing compared to the Native ListView, especially for the Icon rendering when doing a fast scroll.

    So i guess i'm going to stay with the renderer, now it's done it's nothing really hard to reproduce. By the way i stil have a problem with the iOS renderer. The listview does update itself when i'm adding / removing element from my ViewModel's ObservableCollection, but i've to touch / scroll the listview to make the new item appear. Maybe there's still something missing to make it refresh UI ? On android there's no problem the UI is synced with the ViewModel.

    Thanks,

    Best regards,

    Aramnd

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Sounds like you need to tell the table view on iOS to reload the affected rows. As I said, implementing a ListView renderer is complicated. I'm. It going to be able to help you with every little thing. Once you fix that issue in nearly certain there will be others because what you're doing is not a simple thing to do. Hopefully you don't find in the end that once you have all the correct behavior it's just as slow as the stock renderer. As one of the chief architects here likes to say, it's really easy to write fast code if it doesn't have to be correct. The hard part is making it fast and correct.

  • Thanks @adamkemp,

    I know it's difficult but i'm doing it on purpose to learn more about renderer, and because with the tests i made, the native listview is performing better than the stock ListView. I do see the change with the native one so it worth a try. By the way i do agree with you that making a renderer is not easy and you have to get through a lot of issues. But really at the moment, on Android it's working perfect, and i just have this little bug on iOS and i hope if i fix this, it's won't bring any other !

    I'll try to reload the affected row. It's weird how each device platform has their own bugs. It's automatically refreshing on Android but not iOS !

    And i also agree with your last sentence. I personnaly don't like to write fast code and i don't like when it's not perfect, so i do take my time to make sure it's optimized and correct, if it's not, i'll try something else ! :)

    Thanks,

    Best regards,

    Armand

  • Hi everyone,

    I've finally got the iOS renderer working. For people interested, you just have to put ''' Control.ReloadData(); ''' in the CollectionChanged Handler of the iOS renderer to make the Listview reload the UI. On Android it's done by default so you don't have to precise it.

    Now everything works fine. If someone is interested in a full example of NativeListView Renderer with CollectionChanged and ItemClick event handler. I can provide a sample in zip format.

    Thanks for people who helped,

    Best regards,

    Armand

  • adamkempadamkemp USInsider, Developer Group Leader mod

    If you are reloading the entire table view every time the collection changes then you may find performance is not what you want it to be. ReloadData tells the table view to update every (visible) row. You could probably in most cases call ReloadRows based on the type of change made to the collection, which would cause it to only reload the changed (visible) rows.

    As an example of a case that might perform significantly better, consider if you repeatedly add a new item to the collection. The ReloadData approach would update all visible rows with every single update, but the ReloadRows approach would reload only the minimum necessary.

  • @adamkemp ,

    Thank for your reply, i didn't know there was ReloadRows. I'll try it and tell you if it's better :)

    Best regards,

  • PrasanjitBiswasPrasanjitBiswas USMember ✭✭

    @ArmandCharbonnier said:
    Hi everyone,

    I've finally got the iOS renderer working. For people interested, you just have to put ''' Control.ReloadData(); ''' in the CollectionChanged Handler of the iOS renderer to make the Listview reload the UI. On Android it's done by default so you don't have to precise it.

    Now everything works fine. If someone is interested in a full example of NativeListView Renderer with CollectionChanged and ItemClick event handler. I can provide a sample in zip format.

    Thanks for people who helped,

    Best regards,

    Armand

    Hey can you please provide the sample. I'm using custom listview and getting confused where to bind data for each platform.

  • klakla Member

    @ArmandCharbonnier said:
    Hi everyone,

    I've finally got the iOS renderer working. For people interested, you just have to put ''' Control.ReloadData(); ''' in the CollectionChanged Handler of the iOS renderer to make the Listview reload the UI. On Android it's done by default so you don't have to precise it.

    Now everything works fine. If someone is interested in a full example of NativeListView Renderer with CollectionChanged and ItemClick event handler. I can provide a sample in zip format.

    Thanks for people who helped,

    Best regards,

    Armand

    Hi Armand!
    could you provide example how you did. i am new with it.
    Thanks
    K-la

Sign In or Register to comment.