在最近的項目中,遇到一個關於WPF中同時加載多張圖片時,內存占用非常高的問題。
問題背景:
在一個ListView中同時加載多張圖片,注意:我們需要加載的圖片分辨率非常高。
代碼:
XAML:
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Button Content="Load" Width="100" Height="35" Margin="0,10" Click="Button_Click"/> <ListView Grid.Row="1" x:Name="lvImages"> <ListView.ItemTemplate> <DataTemplate> <Image Source="{Binding ImageSource}" MaxWidth="800"/> </DataTemplate> </ListView.ItemTemplate> <ListView.Template> <ControlTemplate> <Grid> <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Hidden"> <ItemsPresenter /> </ScrollViewer> </Grid> </ControlTemplate> </ListView.Template> <ListView.ItemsPanel> <ItemsPanelTemplate> <StackPanel IsItemsHost="True" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.IsVirtualizing="True"/> </ItemsPanelTemplate> </ListView.ItemsPanel> </ListView> </Grid>
C#:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { lvImages.Items.Clear(); // Image folder location: D:\Pics string[] files = System.IO.Directory.GetFiles(@"D:\Pics"); List<ImageSourceModel> models = new List<ImageSourceModel>(); foreach(var path in files) { BitmapImage image = new BitmapImage(); image.BeginInit(); image.UriSource = new System.Uri(path); image.EndInit(); image.Freeze(); models.Add(new ImageSourceModel() { ImageSource = image }); } lvImages.ItemsSource = models; } } public class ImageSourceModel { public ImageSource ImageSource { get; set; } }
內存占用情況(此時只加載了20張圖片,內存占用>1G):
優化方案:
1. 初始加載時,只加載部分圖片並顯示。當ScrollViewer滾動到底部時,再加載一部分。關於這個方案,可以參考 WPF MVVM模式下實現ListView下拉顯示更多內容;
但是這並不能解決最終內存占用過高的情況。
2. 給圖片設置DecodePixelWidth屬性,
BitmapImage image = new BitmapImage(); image.BeginInit(); image.UriSource = new System.Uri(path); image.DecodePixelWidth = 800; image.EndInit(); image.Freeze(); models.Add(new ImageSourceModel() { ImageSource = image });
此時的內存占用如圖
內存降低的非常顯著,此時同樣多的圖片內存占用只有40M左右。
最終我們可以把優化方案1和優化方案2結合起來。這樣在加載多張圖片時不會出現卡頓的現象。另外從用戶體驗的角度我們可以在圖片顯示出來前,先用一個Loading的動畫效果過渡下。
感謝您的閱讀。代碼和測試圖片請點擊這里下載。