Bug 56793 - ListView with uneven row hight not updating layout if items change size after item has been deleted
Summary: ListView with uneven row hight not updating layout if items change size after...
Status: CONFIRMED
Alias: None
Product: Forms
Classification: Xamarin
Component: Forms ()
Version: 2.3.4
Hardware: PC Windows
: Normal normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2017-05-24 10:10 UTC by staircase
Modified: 2017-12-19 09:55 UTC (History)
4 users (show)

Tags: ac ios android listview HasUnevenRows viewcell
Is this bug a regression?: ---
Last known good build:


Attachments
Two screenshots of app. Left is working and right is broken (see entries 7 (not expanded) and 8 (expanded when shouldn't be)) (40.25 KB, image/png)
2017-05-24 10:10 UTC, staircase
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report for Bug 56793 on Developer Community or GitHub if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: Developer Community HTML or GitHub Markdown
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:
Status:
CONFIRMED

Description staircase 2017-05-24 10:10:56 UTC
Created attachment 22413 [details]
Two screenshots of app. Left is working and right is broken (see entries 7 (not expanded) and 8 (expanded when shouldn't be))

I am developing an app that uses a list view to show a list of appointments. The currently selected appointment and any that meet certain conditions should be expanded and show extra information and others should just show a summarised version. I have implemented this using a ListView with it's ItemTemplate set to a DataTemplate containing a StackLayout which then contains various elements. Some of these elements have their IsVisible property bound to a bool property on the view model that is updated depending on whether the item should currently be expanded. The ListView has it's HashUnevenRows set to true and RowHeight set to -1.

When an item is deleted from the items collection is correctly is removed but after that point any other item below it that was on screen (except for the last item which does always seem to work) will not change in size even though the "expand" property is definitely being changed and the extra items appear but overlayed on the other elements that were always there. If the items are scrolled to off the screen they usually start working again.

I have developed a small test app that shows the same behaviour but is much simpler and only performs actions in response to gui interaction to remove any possibility of it being multi threading issues. This simple app has a button to remove the top item and a list view containing numbered items that if not selected are just their number and if selected also show "Expanded" above the number. After items have been deleted the size of the rows will no longer update and stays at the height they had when the item was deleted. Causing the originally selected row to have white space above the number and any new rows to either now show the "Expanded" or to show it overlayed over the number.

Full source code is available at https://github.com/staircase27/XamarinFormsAndroidListViewError/tree/master

The main pages .xaml file contains

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ListViewError.MainPage"
             x:Name="Page"
             BindingContext="{x:Reference Page}">
    <StackLayout Orientation="Vertical">
        <Button Clicked="MenuItem_OnClicked" Text="Remove First Item" />
        <ListView ItemsSource="{Binding Items}" ItemSelected="ListView_OnItemSelected" HasUnevenRows="True" RowHeight="-1">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Vertical">
                            <Label IsVisible="{Binding Expand}" Text="Expanded" VerticalTextAlignment="Center"
                                   HorizontalTextAlignment="Center" />
                            <Label Text="{Binding Id}" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

and the relevant parts of the code behind file are

public partial class MainPage
{
    public MainPage()
    {
        _items = new ObservableCollection<Item>(Enumerable.Range(0, 10).Select(i => new Item(i)));
        InitializeComponent();
    }


    public IEnumerable<Item> Items => _items;


    public sealed class Item : INotifyPropertyChanged
    {
        public Item(int id)
        {
            Id = id;
            Expand = false;
        }


        public int Id { get; }

        public bool Expand
        {
            get { return _expand; }
            set
            {
                if (value == _expand) return;
                _expand = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;


        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }


        private bool _expand;
    }


    private void ListView_OnItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        if (_selectedItem != null)
            _selectedItem.Expand = false;
        _selectedItem = e.SelectedItem as Item;
        if (_selectedItem != null)
            _selectedItem.Expand = true;
    }


    private void MenuItem_OnClicked(object sender, EventArgs e) => _items.RemoveAt(0);

    private Item _selectedItem;
    private readonly ObservableCollection<Item> _items;
}

This code has been tested using Xamarin 4.4.0.34 and Xamarin.Forms NuGet Package 2.3.4.224 but the same behaviour was seen with slightly older versions too. Both were developed using Visual Studio 2015. (Have tried to update to 2.3.5 but experiences issue due to anothe bug I have also listed.)

In the demo app I have tried the only workaround that I could think of which is to reset the ItemsSource on the ListView which does fix the issue but also loses the current selection. I have also worked around that issues by resetting the the SelectedItem which does cause the selected item to be picked back up again. Neither of these is a good option for my main app though as changes to the collection come from multiple places and the collection is provided by an external component so watching for changes is not easy. Also as the list in my main app is relatively large and there are many components in the gui per item reloaded the entire list for any change to the collection is likely to be very expensive and make the app hard to use. This workaround is also included in the respository linked to above and requires changing the MenuItem_OnClicked function to

        _items.RemoveAt(0);
        Item selectedItem = _selectedItem;
        ListView.SelectedItem = null;
        ListView.ItemsSource = null;
        ListView.ItemsSource = Items;
        ListView.SelectedItem = selectedItem;

NOTE: This code and issue has not been tested on IOS or UWP as I do not currently have access to these platforms to test on.

This has previously been asked on Stack overflow (https://stackoverflow.com/questions/43275295/xamarin-forms-listview-with-uneven-row-hight-not-updating-layout-if-items-change) and xamarin forums (https://forums.xamarin.com/discussion/92611/listview-with-uneven-row-hight-not-updating-layout-if-items-change-size-after-item-has-been-deleted) but has received no responses on either. At that time I could not locate this bug tracker so didn't post it here.
Comment 1 staircase 2017-05-24 16:29:02 UTC
Been checked and is still present in Xamarin.Forms 2.3.4.247
Comment 2 Jimmy [MSFT] 2017-05-25 21:31:48 UTC
I was able to reproduce the issue described with the project linked to in the description. This is affecting Android and iOS; it works without issue on UWP. And it appears to be more broken on iOS because the cell does not expand correctly even _before_ the first item is removed. 

I am confirming this report so the team can investigate further. Thanks!


### Version Test
2.3.6.108   BAD
2.3.5-pre3  BAD
2.3.4.247   BAD
Comment 3 Namyslaw Szymaniuk 2017-12-19 09:43:49 UTC
+1... any news? Again ticket that is *CONFIRMED* and that's it?
Comment 4 Namyslaw Szymaniuk 2017-12-19 09:55:10 UTC
The only cked up workaround is to set ItemSource to null, and then again to correct collection of objects... But it's ungly and bad way.