Search code examples
uwpuwp-xamlwinui

Stop UWP ListView from scrolling to top when updating ObservableCollection


When I update my ObservableCollection which is bound to my ListView it automatically scrolls to the top.

My code that gets the data currently looks like this with records being the ObsevableCollection:

        public async Task getData()
        {

            var client = new HttpClient();
            HttpResponseMessage response = await client.GetAsync(new Uri("https://api.nomics.com/v1/currencies/ticker?key=<api key>&limit=10"));
            var jsonString = await response.Content.ReadAsStringAsync();
            JsonArray root = JsonValue.Parse(jsonString).GetArray();
            records.Clear();
            for (uint i = 0; i < root.Count; i++)
            {
                string id = root.GetObjectAt(i).GetNamedString("id");
                string name = root.GetObjectAt(i).GetNamedString("name");
                decimal price = decimal.Parse(root.GetObjectAt(i).GetNamedString("price"));
                records.Add(new Coin {
                    id = id,
                    name = name,
                    price = Math.Round(price, 4),
                    logo = "https://cryptoicon-api.vercel.app/api/icon/" + id.ToLower()
                });
            };
           
        }

My XAML-Layout:

 <ListView x:Name="CoinsLV" Grid.Row="1" IsItemClickEnabled="True" ItemClick="listView_ItemClick" ScrollViewer.VerticalScrollMode="Auto">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Padding="5">
                        <Image Width="50" Height="50">
                            <Image.Source>
                                <BitmapImage UriSource="{Binding logo}" />
                            </Image.Source>
                        </Image>
                        <StackPanel>
                        <TextBlock Text="{Binding name}" 
                           Margin="20,0,0,0"
                           FontSize="18" 
                           FontWeight="SemiBold"
                           Foreground="DarkGray" />
                        <TextBlock Text="{Binding price}" 
                           Margin="20,0,0,0"
                           FontSize="20"
                           Foreground="White" 
                           Opacity="1" />
                    </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <ItemsStackPanel ItemsUpdatingScrollMode="KeepItemsInView" />
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
        </ListView>

Is there any way to disable this behavior cause it's causing really a really bad UX. I've tried updating every single item individually but couldn't get that to work. Thanks.


Solution

  • I can reproduce your issue. The reason is you use Books.Clear() to clear the datasource before add item, which causes the ItemsSource of ListView is null, so that the listView will scroll to top.

    To solve this issue, you need to create a collection to record the previous items, then you could remove these previous items from the total collection.

    As follows:

    Xaml code:

    <ListView IsItemClickEnabled="True" ScrollViewer.VerticalScrollMode="Auto" ItemsSource="{x:Bind Books}" Height="600">
    …
    </ListView> 
    

    Code behind:

    public sealed partial class MainPage : Page
        {
            public ObservableCollection<Book> Books;
            public ObservableCollection<Book> OldBooks;
            public MainPage()
            {
                this.InitializeComponent();
                Books =new ObservableCollection<Book>()
                {
                        new Book(){logo=new Uri("ms-appx:///Assets/2.JPG"), name="Chinese",price=25},
                        new Book(){logo=new Uri("ms-appx:///Assets/2.JPG"), name="English",price=26},
                       ……
               };
                OldBooks = new ObservableCollection<Book>();
                foreach (var book in Books)
                {
                    OldBooks.Add(book);
                }
            }
    
            private void Button_Click(object sender, RoutedEventArgs e) //update button
            {
              
                Books.Add(new Book() { logo = new Uri("ms-appx:///Assets/1.JPG"), name = "Math", price = 20 });
                 ……
                Books.Add(new Book() { logo = new Uri("ms-appx:///Assets/1.JPG"), name = "Chenstry", price = 30 });      
    
                foreach(var item in OldBooks)
                {
                    Books.Remove(item);
                }
            }
        }
        public class Book
        {
            public Uri logo { get; set; }
            public string name { get; set; }
            public int price { get; set; }
        }