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
Not very familiar with this sort of applications but does the api gets called again after you scroll down and back up?
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.
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 😄
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;
}
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
I have tried this a couple days ago, but Im getting an error
I can try once more wait a sec
Also try ScrollViewer.CanContentScroll="False"
Found this on stackoverflow: https://stackoverflow.com/questions/31658655/wpf-listview-vertical-scrollbar
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...
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
sure
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;
}
does this support debugging like how we do in web api's where we get the time of the executed line?
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
ya.. kinda thought so... wanted to profile xaml code. its just like how you cant debug html code 😄
whats the resolution of the images?
35x35, they are small, but i need ~140 images per record...
i see
can we do like a batch update in here?
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
like load 5-10 records upfront and then after a few seconds load everything else
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
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
Hmm, so how to disable scrollviewer? Also in my <Page ../> I have RenderOptions.BitmapScalingMode="HighQuality" line, so it works for whole content
just remove the scrollviewer props
not able to find or think any more tweaks
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
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
i read an issue where one contributor commented that the support has been added. tho i wasn;t sure
Yeah I saw this, but can't find a way to use it in WPF sadly. Im trying to find something similar
oh : (
is CollectionView available in wpf?
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 -...
actually i read this differences...https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/collectionview/?view=net-maui-8.0
CollectionView - .NET MAUI
The .NET MAUI CollectionView displays a scrollable list of selectable data items, using different layout specifications.
Hmm, I can try, but I've never used it before
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?
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
You can possibly queue up a set of tasks to load images in batches and update, dunno I'm just spitballing
I guess if nothing else works, I'll have to do as you say
I'm a wpf hobbyist tbh
so there are lots of people with pro expereince