dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
21.98k stars 1.72k forks source link

.NET MAUI Android ListView issues #16137

Open GitOguz opened 1 year ago

GitOguz commented 1 year ago

Description

I have an ObservableCollection bound to ListView item source. When I remove certain object from the collection I'm getting invisible items in ListView. Also when I scroll through listview items there will be gaps occurring in the list. Items would be invisible.

I have a screeenrecording Screen recording

The Listview lives in a stacklayout with an datatemplate.

I'm using VS Version 17.7.0 Preview 2.0

Each compilation time I will get this message. Am I using an old SDK version? Is there a newer version where this issue maybe is resolved?

C:\Program Files\dotnet\sdk\7.0.400-preview.23274.1\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targets(287,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy

Steps to Reproduce

Create .NET MAUI App for Android Create mainpage add a listview in stacklayout Create ObservableCollection Bind the Artikelen collection to the ListView Add 30 sample data in to the collection Remove an items from Artikelen collection observe the listview behaviour.

    public class VoorraadKast {
        public string KastId { get; set; }
        public ObservableCollection<Artikel> Artikelen { get; set; }
    }
public class Artikel : INotifyPropertyChanged {
    private string _ArtikelId;
    public string ArtikelId {
        get { return _ArtikelId; }
        set { 
            _ArtikelId = value;
            NotifyPropertyChanged("ArtikelId");
        }
    }

    private int _Aantal;
    public int Aantal {
        get { return _Aantal; }
        set {
            _Aantal = value;
            NotifyPropertyChanged("Aantal");
        }
    }
    private string _ArtikelName;
    public string ArtikelName 
    {
        get { return _ArtikelName;}
        set {
            _ArtikelName = value;
            NotifyPropertyChanged("ArtikelName");
        }
    }
    public string KastId { get; set; }
    public string Seperator { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string name) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

Link to public reproduction project repository

dont have

Version with bug

7.0.86

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android 13

Did you find any workaround?

No

Relevant log output

No response

ghost commented 1 year ago

Hi @GitOguz. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

GitOguz commented 1 year ago

Link to repo Sample project

rachelkang commented 1 year ago

@GitOguz Have you tried using CollectionView? Are you only seeing these issues with ListView?

ghost commented 1 year ago

Hi @GitOguz. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

GitOguz commented 1 year ago

@GitOguz Have you tried using CollectionView? Are you only seeing these issues with ListView?

I didn't test is with CollectionView in my case. I will test that also. Currently this issue is with ListView.

JohnHDev commented 1 year ago

@rachelkang It could be that this PR fixes this issue: https://github.com/dotnet/maui/pull/13669

But that PR appears to have only gone into .NET 8, is that correct? Can we get it merged back into .NET 7 pls? This bug is very easy to replicate, at least for me on iOS. Display a list view in the iOS simulator with 5-10 rows displayed. Toggle dark mode in the simulator, most will disappear. We can't even think of migrating to .NET MAUI with these issues in the production version of .NET, and shouldn't have to wait for November.

GitOguz commented 1 year ago

Hi @GitOguz. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

I've switched to CollectionView. My god that works like a charm. In my case CollectionView solves my issues but the bug in ListView remains.

rachelkang commented 1 year ago

@JohnHDev https://github.com/dotnet/maui/pull/15036 will be considered for backport.

@GitOguz glad to hear CollectionView works well for you! It's definitely the recommended alternative

ghost commented 1 year ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

XamlTest commented 1 year ago

Verified this on Visual Studio Enterprise 17.7.0 Preview 6.0. Repro on Android 13.0-API33(8.0.100-preview.6.23330.14), not repro on Windows 11 and iOS 16.4 with below Project: ListViewTest.zip

FlavioGoncalves-Cayas commented 7 months ago

I still experience this issue on version 8.0.6. And no, switching to CollectionView is not an option as CollectionView does not have ContextActions. And no, SwipeView is not a replacement for ContextActions as SwipeView has other issues and is not standard Android-Platform behavior.

GV1072 commented 6 months ago

I am also getting same issue and posted issue but still not fixed. Please fix as we are not able to migrate to .NET Maui.

Thanks in Advance

jonathanantoine commented 2 months ago

Any update :) ?

rafageist commented 1 month ago

Hello @GitOguz,

I encountered the same issue with ListView on Android. As a workaround, I modified the removal method to recreate the ObservableCollection after removing an item. Here’s the adjusted code:

public void RemoveArtikel(Artikel artikel) 
{
    var art = voorraadkasten.FirstOrDefault(k => k.KastId == artikel.KastId);

    if (art != null)
    {
        art.Artikelen.Remove(artikel);
        art.Artikelen = new ObservableCollection<Artikel>(art.Artikelen);

        if (art.Artikelen.Count == 0)
        {
            voorraadkasten.Remove(art);
        }
    }

    gescandItems.ItemsSource = art.Artikelen;
}

Additionally, it's important to ensure thread-safe access to the collection:


private readonly object _lock = new object();

// ...

voorraadkasten = new ObservableCollection<VoorraadKast>();

BindingBase.EnableCollectionSynchronization(voorraadkasten, _lock, ObservableCollectionCallback);

// ...

private void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)
{
    lock (context)
    {
        accessMethod();
    }
}

Recreating the collection after each modification ensures the ListView updates correctly and using BindingBase.EnableCollectionSynchronization helps to handle concurrent access safely.

Also, when initializing the list with a large number of items, ensure you set the ItemsSource after populating the collection to avoid delays:

for (int i = 0; i < 2000; i++)
{
    voorraadKast.Artikelen.Add(new Artikel { KastId = "ABC12345", Aantal = i, ArtikelId = "1234556789", ArtikelName = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", Seperator = "O" });
}

// Set the ItemsSource after populating the collection
gescandItems.ItemsSource = voorraadKast.Artikelen;

Setting the ItemsSource after the collection is fully populated significantly improves performance.

Regards

GitOguz commented 1 month ago

@rafageist Thank you for your tips. Creating the collection over and over again and binding it to the ItemSource isn't that affecting the performance? If you scroll in a big list and remove an item in the middle of the list and recreate the collection and reattach it, won't the list jump back to the top again? You won't be able to keep track of your current location in the list. The user will have to scroll down back to the last position to continue.

Also Im getting the feelling that some action are random run on the UI thread and some on the back by the system. The thread-safe way is indeed the best way to do it.

rafageist commented 1 month ago

Hello @GitOguz

Before providing my tips, I downloaded your project, implemented the suggested changes, and conducted all necessary tests. In response to your questions:

  1. I tested with up to 5000 items, and it was extremely fast in displaying, scrolling, and deleting an item. There was no noticeable difference in performance between deleting an item from a list of 60 and a list of 5000.

  2. When an item is deleted, the scroll position is maintained, and it does not jump back to the top. Therefore, the user does not need to scroll back down to their previous position.

Additionally, to address the concern about random thread, I ran some tests and found that using Dispatcher.Dispatch to set the ItemsSource on the main thread is faster most of the time. Here are the results:

Without Dispatcher.Dispatch: 50-90 ms With Dispatcher.Dispatch: 15-30 ms:

Dispatcher.Dispatch(() => {
    gescandItems.ItemsSource = art.Artikelen;
});

More exhaustive tests could be done by devoting more time to this. Remember, it is a workaround, not a solution.

Thank you for your comments. Regards