StackLayout dynamic size

purplehazepurplehaze GBMember ✭✭

I can't post much code as it's all NDA and all that, but hopefully I can paint the picture properly. I essentially have a page with a ListView on it that represents a list of forum posts. The ListView view cell is setup to take a ForumPostView control which has title, message, photo, etc on it. I want to be able to show replies to the forum post in the form of any number of ForumPostReply controls. My ForumPostView control has a stacklayout which, when the BindingContext object (a post message) has any replies, dynamically adds replies to the stacklayout.

Everything works great, it all looks perfect but I have an issue. If my screen opens with a post that has a reply on it, it doesn't draw correctly. If I scroll the list down, it reshuffles and then draws itself correctly. The reply is kind of there but you can tell that the outer ForumPostView object hasn't expanded itself to cater for the reply. Scrolling down so the post is out of sight causes the layout to fix itself and when you scroll back, it looks right.

Any ideas? I appreciate it's difficult to tell without code but it seems like my ForumPostView doesn't draw itself initially until I guess it invalidates...

Answers

  • JohnHardmanJohnHardman GBUniversity mod

    @purplehaze

    If you cannot post the intended-for-production code because the the NDA, create a small sample that demonstrates the same problem and then post that code.

  • purplehazepurplehaze GBMember ✭✭

    Hopefully a couple of pics will help. This is a snapshot of the list after the page appears:

    And after scrolling the list down then back up again (so the cell in question invalidates itself just before it reappears I guess):

    I don't think I'm doing anything out of the ordinary, I'm not doing any size changed processing or anything unusual. The list of posts is an UnevenRows=true normal ListView. The ItemsSource of the listview (done with XAML binding) is an Observable collection of 'ForumPost' objects. Each ForumPost object itself has an obvservable collection of ForumPosts for the replies (it can only go 2 levels deep, so a reply can't have a further reply).

    The ItemTemplate for the main ListView is a ForumPostView which has bindings to the members in the ForumPost object. The replies list is a StackLayout custom control which has a bindable property for setting the replies. When that list changes, it adds a ForumMessageReply object to the stack:

    using System;
    using System.Collections.Generic;
    using Xamarin.Forms;
    using System.Collections.ObjectModel;
    using MB.Models;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    
    namespace MB.Views.Forum
    {
        public partial class ForumReplyStack : StackLayout, INotifyPropertyChanged
        {
            private ObservableCollection<ForumPost> replies;
    
            public ForumReplyStack()
            {
                InitializeComponent();
            }
    
            public static readonly BindableProperty RepliesProperty = BindableProperty.Create(
                              propertyName: "Replies",
                              returnType: typeof(ObservableCollection<ForumPost>),
                              declaringType: typeof(ForumReplyStack),
                              defaultValue: null,
                              defaultBindingMode: BindingMode.OneWay,
                              propertyChanged: RepliesPropertyChanged);
    
    
            private static void RepliesPropertyChanged(BindableObject bindable, object oldValue, object newValue)
            {
                var control = (ForumReplyStack)bindable;
                control.replies = (ObservableCollection<ForumPost>)newValue;
                control.replies.CollectionChanged += control.Replies_CollectionChanged;
            }
    
            public void Replies_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                for (int i = 0; i < e.NewItems.Count; i++)
                {
                    ForumMessageReply reply = new ForumMessageReply();
                    if (this.Children.Count % 4 == 0)
                        reply.BackColour = "#D6F2D3";
                    else if (this.Children.Count % 4 == 1)
                        reply.BackColour = "#F8C9D2";
                    else if (this.Children.Count % 4 == 2)
                        reply.BackColour = "#ADD3D9";
                    else if (this.Children.Count % 4 == 3)
                        reply.BackColour = "#FFFFFF";
    
                    reply.BindingContext = e.NewItems[i];
                    this.Children.Add(reply);
                }
    
                OnPropertyChanged("Replies");
            }
        }
    }
    

    This way I can add different colours for replies (not currently wired up).

    Everything works fine once the list has scrolled so the offending message with reply is hidden and re-shown.

  • JohnHardmanJohnHardman GBUniversity mod

    @purplehaze

    Is this just happening on iOS by any chance?

  • purplehazepurplehaze GBMember ✭✭
    It is - I’ve onlu tested on iOS at the moment - is it a known issue?
  • purplehazepurplehaze GBMember ✭✭

    Hi - apologies for bumping but I've been on this for a week now, tried practically everything, trying to force redraws, measure invalidations, etc, but can't get it to draw properly initially.

    Any ideas at all?

  • JohnHardmanJohnHardman GBUniversity mod

    @purplehaze said:
    It is - I’ve onlu tested on iOS at the moment - is it a known issue?

    When last I looked into this, it was a major pain point on iOS (Android and UWP worked just fine). If you search the forums you will find many threads about it. I will be re-visiting this at some point fairly soon, but I was hoping that in the intervening months (years?) that something would have been done in the XF source code to make it a non-issue. If it's still an issue, it might be worth trying CollectionView instead.

    However, in the meantime, perhaps somebody who has looked into this recently can comment?

    cc. @JGoldberger - can one of the support team comment pls?

  • purplehazepurplehaze GBMember ✭✭
    Thanks for the reply. I’ve tried so many different things to get it working, it feels like I’ve exhausted every avenue so maybe it is a bug in XF.

    I did fry the CollectionView and that does actually work but I get a strange redraw issue as it’s appearing on the screen, everything kind of shuffles into place.

    Is there something in XF that suppresses redrawing of the screen until everything is measured and laid out?
  • purplehazepurplehaze GBMember ✭✭
    edited June 18

    Really disappointed with Xamarin Forms. It fundamentally doesn't allow me to produce the requirements I need. I've tried absolutely everything. The ONLY way it can work which is incredibly ugly is to ScrollTo the bottom of the list (or at least a page down) and ScrollTo back to the top with animations on.

    Is there a fix due for this? Surely it can't be production-ready with such a glaring ListView bug?

  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai
    edited June 21

    @purplehaze

    Yes, uneven rows has been an issue with Xam.Forms on iOS. The reason is that an iOS UITableView does not natively support uneven row heights. Here is a comment I made on this on Stack overflow a couple of years ago:
    https://stackoverflow.com/a/44600089/2913599

    HasUnevenRows won't work by default on iOS. See this comment on a bug report:

    iOS does not support automatically sizing row cells, you must set a ListView.RowHeight or a Cell.Height. Unfortunately, we did not specifically disable this from working on Android or Windows Phone (where it just works without our intervention), and it has lead some to believe that iOS is broken in this respect.
    Unfortunately this is a limitation of the iOS platform and therefore is the intended behavior.

    It may also be helpful to look over the docs for HasUnevenRows property.

    When the app developer sets the ListView.HasUnevenRows property to true, the behavior of the list view still depends on the ListView.RowHeight property. First, if the developer either does not set the ListView.RowHeight property or sets it to -1, list view items are autosized to fit their contents. This is the desired behavior and the intended use case for a ListView.HasUnevenRows value of true, as noted above. Second, if the developer sets the ListView.RowHeight property to 0 or to a negative value other than -1, then all rows in the ListView will, irrespective of the height of their content, have the default height for the system. Third, and finally, if the developer sets ListView.RowHeight to a positive value, then all rows in the ListView will, irrespective of the height of their content, be as tall as ListView.RowHeight, as if ListView.HasUnevenRows had been set to false.

    And here is a blog post about the issue:
    https://peterfoot.net/2017/12/08/listview-adventures-auto-sizing-uneven-rows/

    Also, according to the docs, a CollectionView seems like your better choice for this scenario:
    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/#unevenrows

    Important

    CollectionView is a view for presenting lists of data using different layout specifications. It aims to provide a more flexible, and performant alternative to ListView. For more information, see Xamarin.Forms CollectionView.

    I hope this helps.

    If you do need further assistance with this, or another issue, and want to share code, you can open a free Xamarin support case. Then we can provide a secure file transfer platform under NDA. You would work via email one on one with a Xamarin support engineer who can engage with the Xamarin Forms engineering team. You can open a free Xamarin support case here: https://support.microsoft.com/en-us/supportforbusiness/productselection?sapId=211dd84f-3474-c3c5-79bf-66db630c92a6

  • purplehazepurplehaze GBMember ✭✭

    @JGoldberger thanks very much for the detailed comment.
    As strange as it sounds, I’m glad it is a bug and you haven’t revealed what I’ve not done as I’ve decided to scrap the list view completely and go with a StackLayout instead.

    I realised I didn’t actually want the rows of my list view selectable and had in fact created my own renderable with rows selectable switched off. I need pull to refresh which you don’t get with a stacklayout but I got around that by using a PullToRefreshLayout library I found on NuGet. All controls resize quickly and it works perfectly.

    I did also try collectionview and that did redraw correctly but I found some issues with binding where controls were mysteriously disappearing - at the point I had lost so much faith in Xamarin forms I was close to trying to use something else.

    Anyway, StackLayout works great and I don’t think it would take much of a change to make it work like a ListView if you wanted to by just changing the colour of the StackLayout element that has been clicked on.

    Thanks all

  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai

    It's actually not considered a bug. Note that the bug report I linked was closed as RESOLVED INVALID and the XF engineer's comment that:

    Unfortunately this is a limitation of the iOS platform and therefore is the intended behavior.

    I am glad to hear that you found a path forward. And yes, sometimes rolling your own solution can be easier than trying to force a built in control to work as you would like when it was not intended to work that way, in this case supporting uneven rows on iOS.

  • batmacibatmaci DEMember ✭✭✭✭✭
    If nothing worked, you may try using sfListView it works fine on ios for me.
  • JohnHardmanJohnHardman GBUniversity mod
    edited June 26

    @JGoldberger said:
    It's actually not considered a bug. Note that the bug report I linked was closed as RESOLVED INVALID and the XF engineer's comment that:

    Unfortunately this is a limitation of the iOS platform and therefore is the intended behavior.

    That was 2015. How Xamarin.Forms is viewed has changed since then, with it now being deemed an Enterprise-capable framework rather than just a prototyping tool. I'd say that uneven rows in ListView on iOS needs re-visiting. If the dev team try fobbing us all off again (as used to happen in the Bugzilla days) with the current equivalent of RESOLVED INVALID, perhaps pointing out that SyncFusion manage to make it work might change the developer's view. Nothing like a third party doing something better for spurring on some change/improvement.

    SyncFusion 1, Xamarin 0.

    cc @DavidOrtinau @JohnMiller

  • batmacibatmaci DEMember ✭✭✭✭✭
    Yes but I would like to phrase here that sflistview natively doesnt implement android,ios or uwp native lists. It uses a custom layout with scroll view and other layouts like stack layout or data grid etc. They achieved a good performance with some tricky way of implementing a good recycling.
  • JohnHardmanJohnHardman GBUniversity mod
    edited June 26

    @batmaci said:
    Yes but I would like to phrase here that sflistview natively doesnt implement android,ios or uwp native lists. It uses a custom layout with scroll view and other layouts like stack layout or data grid etc. They achieved a good performance with some tricky way of implementing a good recycling.

    Understood. Am I correct in thinking that their list supports virtualisation? If so, whether it's native or not is not the important thing, the important thing is that they have a list view that does virtualisation and uneven row heights. As an app developer, I don't care what's under the hood, as long as the functionality is available that I need, with the performance that I need.

  • purplehazepurplehaze GBMember ✭✭
    It may not be natively supported in iOS, but what’s odd about that is that is does actually work once the list has refreshed, i.e. you scroll down and then back up again, everything is fine from that point on.

    My implementation with PullToRefreshLayout is a bit buggy I think, it sometimes flickers quite badly. Still contemplating what to do about it
  • JohnHardmanJohnHardman GBUniversity mod

    @purplehaze said:
    It may not be natively supported in iOS, but what’s odd about that is that is does actually work once the list has refreshed, i.e. you scroll down and then back up again, everything is fine from that point on.

    Yes, which has always been the reason I thought it should not be that difficult a change for Xamarin to make. The layout/sizing stuff happens when a ViewCell scrolls into view. Why should it be so difficult if we want to change the size (e.g. by changing the IsVisible state on some nested Views) when a ViewCell is already in view?

  • batmacibatmaci DEMember ✭✭✭✭✭
    @JohnHardman yes i wanted to say virtualization. I agree with what you said as long as you dont target xbox, ios and android tv app. Because native lists allow you to scroll using remote control or xbox controller but sflistview doesnt, you must use mouse pointer. So your app wont look native on xbox or tv
  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai

    @purplehaze @JohnHardman

    So I just did a quick and simple test and HasUnevenRows is working for me as expected on iOS. So there might be something about how @purplehaze is adding the replies.

    @purplehaze If you want support to look into this issue and get a bug filed if need be, we need to reproduce what you are seeing. If you can please open a free support case so you can provide reproduction code under NDA, or more ideally create a complete simple test project that reproduces the issue without including any of your intellectual property that is under NDA, we can assist here.

    You can open a free Xamarin support case here: https://support.microsoft.com/en-us/supportforbusiness/productselection?sapId=211dd84f-3474-c3c5-79bf-66db630c92a6

Sign In or Register to comment.