C
C#•7mo ago
Mimal99

UI is lagging while scrolling a listView

Hi, I wanted to create an application that retrieves data from the API and displays it. I'm having trouble displaying data because my UI lags when scrolling the ListView. My item template is quite complex and I am displaying multiple images (35 x 35 pixels). When I disabled images it worked much better, but when I added only 2 images per record in the ListView it worked the same as before. I can't fit all the code here, but here's how it works. I receive data from the API and serialize it into a large object (class is generated from JSON response), then filter all the data and retrieve only the fields I want to use. Then i set the fields of my 2nd, smaller, object that is displayed in the ListView. I store each image locally to speed up the loading process. When I tried to change all my bindings to async, I noticed that when I scroll down and up, all the data reloads. I suspect this may be the problem, but I don't know how to solve it. Here is part of my XAML: <ListView Grid.Row="1" Name="List" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ItemsSource="{Binding ViewModel.MyList}"> <ListView.ItemContainerStyle> <Style TargetType="{x:Type ListViewItem}"> <Setter Property="Background" Value="Transparent" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListViewItem}"> <ContentPresenter /> </ControlTemplate> </Setter.Value> </Setter> </Style> </ListView.ItemContainerStyle> <ListView.ItemTemplate> <DataTemplate DataType="{x:Type models:MyClassObject}"> <Grid>... a lot of controls here ... <//////>
34 Replies
Dharmang
Dharmang•7mo ago
Not very familiar with this sort of applications but does the api gets called again after you scroll down and back up?
Mimal99
Mimal99•7mo ago
At this point my app has 2 buttons. First for retrieving data and storing objects in the list. Second, updating the ListView source to lists of objects. So I could even skip the API part, create the object manually and then add it to the list, but that doesn't change anything.
Dharmang
Dharmang•7mo ago
I see When you store images locally, do you mean you have used IMemoryCache? if not i was gonna suggest it to cache the data. this is just my 2 cents, i am not very familiar. you can wait for some expert advice 😄
Mimal99
Mimal99•7mo ago
Huh, I don't think so. Images are stored in Assets folder ant here is my method to load them. When Im filtering data from API, Im using displayObject.Image1 = GetImageFromFile(path, APIObject.ID); So here is the code and Ill read something about IMemoryCache. Code: public static BitmapImage GetImageFromFile(string path, string fileName) { if (storedImages.ContainsKey(fileName)) { return storedImages[fileName]; } string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path); string finalPant = $"{tempPath}\{fileName}"; var image = new BitmapImage(new Uri(finalPant, UriKind.Absolute)); image.DecodePixelHeight = 35; image.DecodePixelWidth = 35; image.CacheOption = BitmapCacheOption.OnLoad; image.Freeze(); storedImages[fileName] = image; return image; }
Dharmang
Dharmang•7mo ago
can you try: does image.EndInit() along with freeze change anything? like load times. in your case its on a button click so it shouldn't be much of a difference
Mimal99
Mimal99•7mo ago
I have tried this a couple days ago, but Im getting an error I can try once more wait a sec
Dharmang
Dharmang•7mo ago
Also try ScrollViewer.CanContentScroll="False" Found this on stackoverflow: https://stackoverflow.com/questions/31658655/wpf-listview-vertical-scrollbar
By setting CanContentScroll to false, you're telling the ScrollViewer to stop using logical scrolling, which also deactivates virtualization.
By setting CanContentScroll to false, you're telling the ScrollViewer to stop using logical scrolling, which also deactivates virtualization.
Both properties work oppositely
Stack Overflow
WPF listview vertical scrollbar
I've got a listview control with a gridview as it's view property. One of it's columns is dedicated to display a really long text. That column cell template is set to a TextBlock. Whenever listvie...
Mimal99
Mimal99•7mo ago
System.InvalidOperationException: 'To modify a specific value of type 'System.Windows.Media.Imaging.BitmapImage', set the IsFrozen attribute to 'false'.' With ScrollViewer.CanContentScroll="False" it works same as before In a while I will try to modify my method to use IMemoryCache and see if that helps
Dharmang
Dharmang•7mo ago
sure
Mimal99
Mimal99•7mo ago
Now its maybe a little bit faster, but still laggy. Here is the method: private static readonly ObjectCache memoryCache = MemoryCache.Default; public static BitmapImage GetImageFromFile(string path, string fileName) { if (memoryCache.Contains(fileName) && memoryCache[fileName] is BitmapImage cachedImage) { return cachedImage; } string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path); string finalPath = Path.Combine(tempPath, fileName); var image = new BitmapImage(new Uri(finalPath, UriKind.Absolute)) { DecodePixelHeight = 35, DecodePixelWidth = 35, CacheOption = BitmapCacheOption.OnLoad }; image.Freeze(); memoryCache.Set(fileName, image, new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(30) }); return image; }
Dharmang
Dharmang•7mo ago
does this support debugging like how we do in web api's where we get the time of the executed line?
Mimal99
Mimal99•7mo ago
Kinda, I have added stopwatches to my whole API class, so it's write elapsed time to console, but I can't add a stopwatch to XAML code. C# part stores the data in objects, it takes about 30 second to prepare, but its fine. XAML loads this data by <Image source="{Binding imageProperty}"/> and the only tool I have is PerformanceProfiler, but it's not so helpful. I can send you the results, but I don't understand how it works so
Dharmang
Dharmang•7mo ago
ya.. kinda thought so... wanted to profile xaml code. its just like how you cant debug html code 😄 whats the resolution of the images?
Mimal99
Mimal99•7mo ago
35x35, they are small, but i need ~140 images per record...
Dharmang
Dharmang•7mo ago
i see can we do like a batch update in here?
Mimal99
Mimal99•7mo ago
By default I display the main part - 22 images. In each record there is a button that shows the details, and here is the rest
Dharmang
Dharmang•7mo ago
like load 5-10 records upfront and then after a few seconds load everything else
Mimal99
Mimal99•7mo ago
Hmm, I think only with Async Bindings, but then its "lazy load". When Im scrolling there are a lot of empty spaces etc. for a couple of seconds. I don't know why ListView cant somehow "remember" all the records instead of rendering them every time But Im beginner in wpf, so maybe there is another way
Dharmang
Dharmang•7mo ago
ya. also as I read in this article. completely removing scrollviewer is better. it wont work with you want to do anyways Also found one more thing: <Image RenderOptions.BitmapScalingMode="HighQuality" /> - Enables hardware image loading
Mimal99
Mimal99•7mo ago
Hmm, so how to disable scrollviewer? Also in my <Page ../> I have RenderOptions.BitmapScalingMode="HighQuality" line, so it works for whole content
Dharmang
Dharmang•7mo ago
just remove the scrollviewer props not able to find or think any more tweaks OhNo
Dharmang
Dharmang•7mo ago
finally you can try this as well: Lol -> 3rd party https://github.com/luberda-molinet/FFImageLoading
GitHub
GitHub - luberda-molinet/FFImageLoading: Image loading, caching & t...
Image loading, caching & transforming library for Xamarin and Windows - GitHub - luberda-molinet/FFImageLoading: Image loading, caching & transforming library for Xamarin and Windows
Mimal99
Mimal99•7mo ago
Without the scrollviever it works the same. Ill try this package I think it doesn't support WPF or I'm too stupid to use it
Dharmang
Dharmang•7mo ago
i read an issue where one contributor commented that the support has been added. tho i wasn;t sure
Mimal99
Mimal99•7mo ago
Yeah I saw this, but can't find a way to use it in WPF sadly. Im trying to find something similar
Dharmang
Dharmang•7mo ago
oh : ( is CollectionView available in wpf?
Mimal99
Mimal99•7mo ago
Is that what you had in mind? https://www.youtube.com/watch?v=fBKW-spQboc
SingletonSean
YouTube
Filtering, Sorting, and Grouping w/ Collection Views - EASY WPF (.N...
Learn how to use collection views in WPF to quickly implement filtering, sorting, and grouping on a collection of items bound to the view. I also point out some interesting behavior regarding WPF default collection views. TIMESTAMPS: 0:00 - Introduction 0:12 - Demo Introduction 0:49 - CollectionView Setup 2:05 - Filtering 4:39 - Grouping 5:48 -...
Dharmang
Dharmang•7mo ago
CollectionView - .NET MAUI
The .NET MAUI CollectionView displays a scrollable list of selectable data items, using different layout specifications.
Mimal99
Mimal99•7mo ago
Hmm, I can try, but I've never used it before
Mayor McCheese
Mayor McCheese•7mo ago
Can you try to use a static asset that's a loading image, and whence the image loads from the file, change out the image?
Mimal99
Mimal99•7mo ago
But then it will work like async binding, but will show temporary image instead of blank space. When all the bindings was async, scrolling was still laggy and images were loading one by one (that looks not so good) I have tried CollectionView and it helped a little bit maybe. When im scrolling just in the middle it works a bit smoother, but when i scroll down to end its the same When Im scrolling, Im getting some error messages in output console. Maybe it will help. System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=itemsImages[6]; DataItem=null; target element is 'ImageBrush' (HashCode=3730161); target property is 'ImageSource' (type 'ImageSource') Also something like this: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=MatchList'. BindingExpression:Path=ActualWidth; DataItem=null; target element is 'Grid' (Name=''); target property is 'Width' (type 'Double') I don't know if it's important, but it only appears when scrolling
Mayor McCheese
Mayor McCheese•7mo ago
You can possibly queue up a set of tasks to load images in batches and update, dunno I'm just spitballing
Mimal99
Mimal99•7mo ago
I guess if nothing else works, I'll have to do as you say
Mayor McCheese
Mayor McCheese•7mo ago
I'm a wpf hobbyist tbh so there are lots of people with pro expereince