ListView space issue when HasUnevenRows=true. Need a solution.

Hi All,

I'm working on a simple listview structure. My requirement is basically to show elements in list. I want HasUnevenRows=true because few elements are single lined & few are multi lined.
Here is the code snippet I'm using.
<ListView ItemsSource="{Binding FireOrExplosionList}" HasUnevenRows="true" Grid.Row="2"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Label Text="{Binding Path=.}" TextColor="{StaticResource TextColor}" /> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

But here the problem is that I'm getting space in the list which is not required. You can see the below image where the space in marked in a rectangle.

Now If I'm changing the code to the following & removing the HasUnevenRows property then the space is not coming but rows in listview are not looking good as they are taking fixed height.
<ListView ItemsSource="{Binding FireOrExplosionList}" RowHeight="25" Grid.Row="2"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Label Text="{Binding Path=.}" TextColor="{StaticResource TextColor}" /> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

So my requirement is to enable HasUnevenRows property but get rid of the unwanted space.
Please provide a solution for this.

Thanks,
Priyabrata

Posts

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @PriyabrataDash: (and at all other readers of this thread)
    Have you found a solution for this problem?

    I show some (bound) ListViews on a page (without XAML).
    The ListViews are added on a StackLayout, the data (text) have variable length.
    So... according to the variable length, I set HasUnevenRows to true (what works).
    The problem is, that the whole height of the ListView seems to be calculated wrong in XF (or in the StackLayout).
    Like in the example of Priyabrata, the contend is showed correct in the ListView, but a lot of unneeded space is added (and showed) at the end of the ListView.
    I have tried everything (LayoutOption's) that came to my mind for the ListView and for the StackLayout - nothing works.
    Sure.. I can set a RowHight and a HighRequest for the ListView, but I don't want to do it, as it should adjust the Height automatically also e.g. on a tablet with more space (so that an item only takes 1 Line instead if 3 Lines like on a Phone).

    I use HasUnevenRows also on other ListViews where it works, but on these pages, the ListView takes more space as the whole page. Maybe this is a bug that only occurs, if the ListView (Height) is smaller then the Page (Height)?

    Can someone help me...?

    Thanks

  • RaphaelSchindlerRaphaelSchindler USMember ✭✭✭

    Is this happening when you use ScrollView inside of a StackLayout? If so the ScrollView takes the complete space of the StackLayout as far as I can say that. And the calculation for the height is buggy or something.
    It should work if you set the height of the surrounding StackLayout.

    Something like this should be ok:

    //untested code ;)
    var scrollView = new ScrollView();
    scrollView.ItemsSource = //Your items;
    
    var mainStack = new StackLayout
    {
        HeightRequest = 300,
        Children = 
        {
            scrollView
        }
    };
    Content = mainStack;
    
  • SpaceMonkeySpaceMonkey GBMember ✭✭✭

    Oh man, you don't know how long I've fiddled with this and never found a solution...
    It happens when I use a Grid layout for my viewcell, but disappears when I use nested StackLayouts, but that's too expensive.
    It also happened in other cases, one time I spent so much time trying to fix it only to discover it's because I render an image which is too big and it gets scaled down to fit, even though it gets scaled down anyway, it still causes the row to have extra space...

    Bottom line: Using Xamarin.Forms cross platform viewcells is:

    • Slow as hell
    • Has this shitty problem with having extra space in some rows seemingly randomly (by that I don't mean actually random, but it depends on little details that shouldn't affect it and you wouldn't be able to guess i.e. buggy)
  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @RaphaelSchindler:
    Thanks for your reply :smile:

    But... unfortunately it's not as easy.... :smirk:

    Short code-snippets:

    var oScrollView = new ScrollView { HorizontalOptions = LayoutOptions.Fill, Orientation = ScrollOrientation.Vertical, };
    var ErfassenStack = new StackLayout() { Orientation = StackOrientation.Vertical, Padding = new Thickness(5, 20, 0, 10) }; 
    ErfassenStack.VerticalOptions = LayoutOptions.Start; // Neu 01.06.2015
    //
    // Add VARIOUS controls to ErfassenStack
    //
    var LV_CB_Interessen = new ListView(); // { HeightRequest = 40 };
    LV_CB_Interessen.SeparatorVisibility = SeparatorVisibility.None; 
    //
    var L_Interessen = new List<GV.MehrfachSelektion>();
    int IntIndex = 0;
    var EintragInteressen = new GV.MehrfachSelektion() { cIconPfad = "", cText = "Interesse an Portalneuerungen", bSelektiert = GV.oBenutzerdaten.lPBN_Interesse_Portalneuerungen, iIndex = IntIndex, cWebServiceAufruf = "" };
    IntIndex = IntIndex + 1; 
    // Add further Entrys
    //LV_CB_Interessen.HeightRequest = 45 * IntIndex; (manually calculate LV Height
    LV_CB_Interessen.HasUnevenRows = true; // Calculate the height automatically depending on data
    LV_CB_Interessen.HeightRequest = 180; // Set Height manually what I don’t want
    LV_CB_Interessen.ItemTemplate = new DataTemplate(typeof(CheckViewCell)); // SL with Image and Label (text
    LV_CB_Interessen.ItemsSource = L_Interessen;
    //
    // Set Page-Content
    //
    oScrollView.Content = ErfassenStack;
    Content = oScrollView;
    

    The page is a registration page with various controls. As the page has to be scrolled, the content is set to ScrollView.
    In the example-code, a ListView LV_CB_Interessen is added to the ErfassenStack (SL).
    The LV_CB_Interessen then is bound to a List with some "selection-entrys" whereby a DataTemplate is used (check-image to show, if an item is selected or not and text to the text) => This is in fact a "MultiSelect-ListView".
    Problem:
    I have to care, that the whole ListViews (all contained entry's) are showed in the ScrollView (as Scrolling of a ListView in a ScrollView don't work since a few versions in Android :wink: )
    If I set HasUnEvenRows to true and don't set a HighRequest to the ListView (what would be the/my target), the rows are showed/calculated correct, but the whole ListView takes way to much space (a lot of empty space after the ListView).
    If I set a HighRequest, it is overtaked.
    But then, I have to set it depending on the platform and also the device (phone / tablet) as on a iPhone, more space is needed to show all entry's as on a Android-phone whereby on a Tablet in landscape orientation less space is needed (as the entry is showed in just one line).

    So the (my) base-problem is, that the HeighRequest of the LV is not calculated correct automatically (with HasUnevenRows = true).

    Hope this explain my problem :blush:

  • RaphaelSchindlerRaphaelSchindler USMember ✭✭✭

    Can you show me a screenshot because I have no idea what you are doing^^

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭
    edited June 2015

    @RaphaelSchindle:

    Yes of course :)
    See .pdf

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @RaphaelSchindler:
    I think, your username in my last posting have cut the last char...

    Do you have any idea..?
    Else, I have - at least for now - to implement the (bad) workaround (set a fix HeightRequest to the LV's for each platform) :cry:

  • RaphaelSchindlerRaphaelSchindler USMember ✭✭✭

    @FredyWenger Ah dang you're using ListView() inside of ScrollView(). Now I get it :smile: Well, I don't know if that helps but try to nest the ListView inside of a StackLayout and set the Height of the StackLayout to a value that is comfortable for you. Or maybe you don't have to explicitly set the Height. Those redrawing and calculating of dynamic things like ListView or ScrollView is really not perferct right now.

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @RaphaelSchindler:

    As you can see in my code-snipped, the LV already is in a StackLayout...
    But I have tried to add one more SL.. same behavior... :cry:
    So... think, that this is a XF bug (Height(Request) of ListView with HasUnEvenRows is calculated wrong) and implement now my (bad!) workaround (as I don't have the time to wait...).

    Nevertheless... thanks for our try to help...:wink:

    And... If someone else have a solution... please post here :smiley:

  • GeorgeCookGeorgeCook PEUniversity ✭✭✭
    edited June 2015

    Please make your voice heard on my bugzilla bug. I asked them to revisit the viewcell height. It seems like a one time thing (so it uses whatever height is in the viecell, as opposed to being able to calculate it per list view item) and in any case I want to have a delegate so I can sue it with the fast grid cell techniques I've published.

    https://bugzilla.xamarin.com/show_bug.cgi?id=29820

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @GeorgeCook:
    Added a comment...

  • PriyabrataDashPriyabrataDash USMember ✭✭

    To All, I communicated with Xamarin support regarding this & seems like this is a known issue to them.
    This issue was supposed to be addressed in Release 1.4, but still it's pending.
    What we can do for this is raise our voice count more in BugZilla.

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @PriyabrataDash :
    Thanks for your posting here.
    Can you post a link to the bug in BugZilla?

  • BrunoPasquiniBrunoPasquini USMember ✭✭

    Any update on this?

  • PriyabrataDashPriyabrataDash USMember ✭✭

    All issues related to ListView are fixed for iOS & Android platform in the latest build as per the statements by Xamarin.
    Nothing specific they mentioned about XForms though.
    @BrunoPasquini, could you please check if the problem still persists in latest XForms build.

    @FredyWenger : I couldn't able to find the BugZilla issue for this one.I'm searching more & if possible you also search this one by keywords in BugZilla. Thanks.

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @PriyabrataDash:
    I don't have filled a additional bug, but have added a comment to the bug of George here:
    https://bugzilla.xamarin.com/show_bug.cgi?id=29820

    But I had a look at my code (as I don't have remembered me, what I have done to solve the problem) :wink:
    I still have the ugly workaround in place:

    • Set the needed Height for one entry for each platform
    • Calculate and set the Height for the ListView according to the items-count in the List (ItemSource)
      => This is ugly, but works
  • PriyabrataDashPriyabrataDash USMember ✭✭

    Sorry, But I could not stop myself from laughing after reading this work around.
    I must say you are clever & good in maths. :smile:
    If possible share the code. Thanks for sharing your fix for this.

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @PriyabrataDash:

    Sorry, But I could not stop myself from laughing after reading that you have asked for posting the code :wink:
    => This is no solution, only a workaround for exactly my problem
    As I wrote in my old posting, I have the problem only on specific page with some specific ListViews that are smaller as the Page-Height and contains a special ViewCell (text + check-icon).
    I use ListViews on various other pages (with dynamic Input) without problems.
    On the problem-page, I use ListViews as "Single-Checkbox" (tap = select / unselect), "Radiobuttons" (tap select the tapped entry and unselect all other entries), and "Multiselect-ListViews/Checkboxes" (select n out from n).
    Therefore, I know exactly what I have to show (static items).

    But... O.K....

    Example: "calculate" Height:

    int iheight = 0;
    if (Device.OS == TargetPlatform.iOS)
    { iheight = 40;}
    //
     if (Device.OS == TargetPlatform.Android)
    { iheight = 45;}
    //
     if (Device.OS == TargetPlatform.WinPhone)
    { iheight = 45;}
    //
    iCount = List.Count;
    LV_RB_Geschlecht.HeightRequest = 45 * iCount;
    //
    

    Example: Fix value for ListView ("LV_CB_AlterDarfAngezeigtWerden"):

        if (Device.Idiom == TargetIdiom.Phone)
             { 
                if (Device.OS == TargetPlatform.iOS)
                 {
                       LV_CB_AlterDarfAngezeigtWerden.HeightRequest = 70; // needs two lines for text
                   }
                 if (Device.OS == TargetPlatform.Android)
                    {
                       LV_CB_AlterDarfAngezeigtWerden.HeightRequest = 60; // needs two lines for text
                     }
                  if (Device.OS == TargetPlatform.WinPhone)
                     {
                        LV_CB_AlterDarfAngezeigtWerden.HeightRequest = 60; // needs two lines for text
                     }
                }
          else // Tablet 
               {
                   LV_CB_AlterDarfAngezeigtWerden.HeightRequest = 45; //  text need only one line
               }
    
  • NeelamNeelam USMember

    Hello All,
    Did any one get the proper solution?

    @FredyWenger : what about you?

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @Neelam:
    I have redesigned my app completely, of cause the many problems that occurs, if you want to use a ListView in a ScrollView :disappointed: .
    I ended up to add PopUp's for any ListView, I have to show from a ScrollView (if you show the ListView on a PopUp, you don't have this problem).

    You can find the details here:
    http://forums.xamarin.com/discussion/33587/how-to-use-a-listview-in-a-scrollview-with-xlabs-popup-control#latest

    And.. you can find more links to documentations, if you click my profile...

    Hope this helps :sunglasses:

  • NeelamNeelam USMember

    @FredyWenger I am not finding any code for PopUp's for any ListView. :neutral:

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @Neelam:

    The information's are on the page1 of the mentioned thread, direct link to page1.
    http://forums.xamarin.com/discussion/33587/how-to-use-a-listview-in-a-scrollview-with-xlabs-popup-control/p1

    You have to click the attached .pdf to see the informations :sunglasses:

  • PabloBiagioliPabloBiagioli USMember ✭✭

    Thanks @FredyWenger,

    I followed your advice with the tweak. I also had to make a bit of an ugly hack to make the list shrink a bit.
    My ListView has an x:Name, and I'm putting the HeightRequest within PropertyChanged Event, and the event setting within the ContentPage constructor.
    I noticed the event was being called multiple times with the Height Property and the Y Property as well, so I supposed it would be a good idea to hack and slash there.

    public MyListPage ()
            {
                InitializeComponent ();
                myListView.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e)
                {
                    var size = ((ObservableCollection<MyItemGroup>)myListView.ItemsSource).Count;
                    if (size == 3) {
                        if (Device.OS.Equals (TargetPlatform.Android)) {
                            locationsListView.HeightRequest = size * androidRowHeight;
                        } else {
                            locationsListView.HeightRequest = size * iOSRowHeight;
                        }
                    } else{
                        //This will leverage the FillAndExpand mechanism
                        locationsListView.HeightRequest = -1; 
                    }
                };
        }
    

    Hope this is helpful as well.

  • RaymondKellyRaymondKelly USMember ✭✭✭

    @FredyWenger @PabloBiagioli Uneven rows and scroll view is still broken. It cannot calculate height correctly and ScrollTo does not work properly because of it. Has anyone made a more elegant workaround when using ScrollTo? Thanks.

  • batmacibatmaci DEMember ✭✭✭✭✭

    @FredyWenger said:
    @PriyabrataDash:

    Sorry, But I could not stop myself from laughing after reading that you have asked for posting the code :wink:
    => This is no solution, only a workaround for exactly my problem
    As I wrote in my old posting, I have the problem only on specific page with some specific ListViews that are smaller as the Page-Height and contains a special ViewCell (text + check-icon).
    I use ListViews on various other pages (with dynamic Input) without problems.
    On the problem-page, I use ListViews as "Single-Checkbox" (tap = select / unselect), "Radiobuttons" (tap select the tapped entry and unselect all other entries), and "Multiselect-ListViews/Checkboxes" (select n out from n).
    Therefore, I know exactly what I have to show (static items).

    But... O.K....

    Example: "calculate" Height:

    int iheight = 0;
    if (Device.OS == TargetPlatform.iOS)
    { iheight = 40;}
    //
    if (Device.OS == TargetPlatform.Android)
    { iheight = 45;}
    //
    if (Device.OS == TargetPlatform.WinPhone)
    { iheight = 45;}
    //
    iCount = List.Count;
    LV_RB_Geschlecht.HeightRequest = 45 * iCount;
    //

    Example: Fix value for ListView ("LV_CB_AlterDarfAngezeigtWerden"):

      
        if (Device.Idiom == TargetIdiom.Phone)
             { 
                if (Device.OS == TargetPlatform.iOS)
                 {
                       LV_CB_AlterDarfAngezeigtWerden.HeightRequest = 70; // needs two lines for text
                   }
                 if (Device.OS == TargetPlatform.Android)
                    {
                       LV_CB_AlterDarfAngezeigtWerden.HeightRequest = 60; // needs two lines for text
                     }
                  if (Device.OS == TargetPlatform.WinPhone)
                     {
                        LV_CB_AlterDarfAngezeigtWerden.HeightRequest = 60; // needs two lines for text
                     }
                }
          else // Tablet 
               {
                   LV_CB_AlterDarfAngezeigtWerden.HeightRequest = 45; //  text need only one line
               }
        
    

    But this will only work if you dont have unevenrows :( how can we make it working when we have uneven rows

  • JaraJara PHMember ✭✭

    It's 2017 now, and the ListView with HasUnevenRows set to True still a problem for iOS

  • BuddhimaKudagamaBuddhimaKudagama USMember ✭✭

    I also faced to this issue, A solution is, we have to set the Height of the list view according to the height of number of rows manually in code behind.

    Below link describes how I did fix it in the normal way...
    https://xamarinsharp.com/2017/05/16/listview-height-issue-in-xamarin-forms-how-to-solve-it/

    And also I have tried to calculate the height using MVVM when changing the Item source of the List View. It also became successful.

    And I wrote an article that how to do it. and the article consists of my code.

    https://xamarinsharp.com/2017/05/20/xamarin-forms-listview-height-change-dynamically-using-mvvm-and-also-solve-empty-space-issue/

  • knottydevknottydev USMember ✭✭

    Can't believe this is still such an issue!!! Anyway, using @BuddhimaKudagama 's suggestion but with binding and strictly MVVM. While that solution claims to be MVVM, i'd argue that it's a little fringe since we're defining observable collections in code behind. But, more than that, i prefer to use binding and keep everything clean in an easy to read viewModel class which provides 99% of what the view needs to simply bind to. This is what I did:

    1. In your viewmodel (assuming it implements PropertyChangedEventHandler), add a new read only property that is going to control your ListView height. In the setter for your observable collection, make sure you call the OnPropertyChanged event for the new property ("ModelDetailLVHeight" in this case). Otherwise, the height won't change when your list data does.

    ViewModel for my specific page:

    public class viewModelOptions : ViewModelBase //Must implement an PropertyChangedEventHandler
    {
        private ModelDetails details;
        public ModelDetails Details //observable collection that is displayed in the ListView
        {
            get
            {
                return details;
            }
            set
            {
                details = value;
                OnPropertyChanged();
                OnPropertyChanged("ModelDetailLVHeight");
            }
        }
    
        public int ModelDetailLVHeight //You will bind to this
        {
            get
            {
                int retval = 0;
                if (details != null) //make sure you handle null and return zero
                {
                    retval = 40*details.Count;
                }
                return retval;
            }
        }
    }
    
    1. In your listview, simply bind to the new property.

          <ListView x:Name="listView_ModelDetail" HeightRequest="{Binding ModelDetailLVHeight}"
                      ItemsSource="{Binding Details, Mode=TwoWay}">
                      <ListView.ItemTemplate>
                          <DataTemplate>
                              <ViewCell>
                                  <Grid>
                                      <Label Text="{Binding Desc}"/>
                                      <Switch IsToggled="{Binding Filter}"/>
                                  </Grid>
                              </ViewCell>
                          </DataTemplate>
                      </ListView.ItemTemplate>
          </ListView>
      
  • Another solution might be to write a behavior to calculate row height and thus also the height of the ListView. Like this:

    public class ListViewAutoSizeBehavior : Behavior
    {
    ListView _ListView;
    ITemplatedItemsView Cells => _ListView;
    private readonly int _extraPaddingPerRow;

        public ListViewAutoSizeBehavior()
        {
            switch (Device.RuntimePlatform)
            {
                default:
                    _extraPaddingPerRow = 2;
                    break;
                case Device.Android:
                    _extraPaddingPerRow = 6;
                    break;
                case Device.iOS:
                    _extraPaddingPerRow = 4;
                    break;
                case Device.WinPhone:
                    _extraPaddingPerRow = 2;
                    break;
            }
        }
    
        protected override void OnAttachedTo(ListView bindable)
        {
            bindable.ItemAppearing += AppearanceChanged;
            bindable.ItemDisappearing += AppearanceChanged;
            _ListView = bindable;
        }
    
        protected override void OnDetachingFrom(ListView bindable)
        {
            bindable.ItemAppearing -= AppearanceChanged;
            bindable.ItemDisappearing -= AppearanceChanged;
            _ListView = null;
        }
    
        private void AppearanceChanged(object sender, ItemVisibilityEventArgs e)
        {
            UpdateHeight(e.Item);
        }
    
        private void UpdateHeight(object item)
        {
            if (_ListView == null || _ListView.ItemsSource == null) return;
    
            int itemsCount = _ListView.ItemsSource.Cast<object>().Count();
    
            if (_ListView.HasUnevenRows)
            {
                double height;
                if ((height = _ListView.HeightRequest) == (double)VisualElement.HeightRequestProperty.DefaultValue)
                {
                    height = 0;
                }
    
                height += MeasureRowHeight(item);
                SetHeight((height + _extraPaddingPerRow) * itemsCount + _extraPaddingPerRow);
            }
            else if (_ListView.RowHeight == (int)ListView.RowHeightProperty.DefaultValue)
            {
                var height = MeasureRowHeight(item);
                _ListView.RowHeight = (height + _extraPaddingPerRow);
                SetHeight((height + _extraPaddingPerRow) * itemsCount + _extraPaddingPerRow);
            }
        }
    
        private int MeasureRowHeight(object item)
        {
            var template = _ListView.ItemTemplate;
            var cell = (Cell)template.CreateContent();
            cell.BindingContext = item;
    
            var height = cell.RenderHeight;
            var mod = height % 1;
    
            if (mod > 0)
            {
                height = height - mod + 1;
            }
    
            return (int)height;
        }
    
        private void SetHeight(double height)
        {
            //TODO if header or footer is string etc.
            if (_ListView.Header is VisualElement header)
            {
                height += header.Height;
            }
    
            if (_ListView.Footer is VisualElement footer)
            {
                height += footer.Height;
            }
    
            _ListView.HeightRequest = height;
        }
    }
    
Sign In or Register to comment.