2012-02-25 19 views
7

Xem xét tệp XAML mẫu sau, hiển thị 1000 người đầu tiên của Facebook, bắt đầu bằng markz là người thứ tư. Lưu ý rằng đây chỉ là một mẫu. Bất kỳ Cửa sổ nào có 1000 phần tử, bất kể bạn xây dựng nó như thế nào, là một minh chứng tốt.Tại sao <Nguồn hình ảnh = '...'> quá chậm và tôi có thể làm gì với nó?

<Window x:Class="SO.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:clr="clr-namespace:System;assembly=mscorlib" 
     Title="MainWindow" Height="350" Width="525"> 
    <ListBox ItemsSource="{Binding}"> 
     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <Image Source="{Binding}" /> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
    </ListBox> 
</Window> 

Và mã sau:

public partial class MainWindow : Window 
{ 
    public MainWindow() { 
     InitializeComponent(); 
     string[] urls = new string[1000]; 
     for (int i = 0; i < 1000; ++i) { 
      urls[i] = "http://graph.facebook.com/" + i + "/picture"; 
     } 
     this.DataContext = urls; 
    } 
} 

Trên một máy tính để bàn rất hợp lý và kết nối tốc độ cao, chương trình cực kỳ chậm. Đang cố gắng cuộn bằng ScrollBar ... nói ở giữa, sẽ mất 30 giây. Việc nhấn phím 'Home' và 'End' sẽ mất nhiều thời gian.

Đây không phải là sự cố lần đầu tiên chỉ nhận được hình ảnh đến bộ nhớ cache. Đi qua lại và nhìn thấy hình ảnh đã được trình bày là hơi nhanh hơn nhưng thường rất chậm. Dường như không có gì được lưu trữ trong bộ nhớ cache, đóng ứng dụng và khởi động lại nó, mọi thứ lại chậm lại.

Mã HTML tương đương đang chạy nhanh. Một số chậm đi lần đầu tiên, nhưng sau đó tất cả mọi thứ là rất nhanh.

Điều gì đang xảy ra? Có yếu tố nào sử dụng bất kỳ bộ nhớ đệm nào cả? Danh sách có thực hiện tìm nạp trước các hình ảnh hiện không được trình bày không? Có anyway để nói với nó để làm gì? Có thực sự là giải pháp duy nhất của tôi là tự quản lý các đối tượng Bitmap, cùng với bộ nhớ đệm và logic tìm nạp trước? Nếu vậy, bất kỳ công việc nào trước đây tôi có thể kết hợp?

EDIT (tóm tắt):

  1. @ H.B. câu trả lời để tắt ảo hóa sẽ cho bạn kết quả tốt nhất. Toàn bộ hộp danh sách được hiển thị ngay sau khi tải Cửa sổ và không có hình ảnh nào được tính lại
  2. @Phil mã hoạt động tuyệt vời và cải thiện hiệu suất, đặc biệt khi chuyển qua lại.
  3. Nếu không có bất kỳ mã bổ sung nào, WPF sẽ không lưu trữ hình ảnh giữa lời gọi. Bộ nhớ cache WinINET được sử dụng NOT. Mặc dù yêu cầu đi kèm với hướng dẫn Cache trong Tiêu đề HTTP, WPF không làm gì với nó.

Trả lời

6

ListBoxes ảo hóa các mục theo mặc định, vì vậy nếu bạn cuộn xuống các mục được tạo khi đang di chuyển. Lúc đầu, nó cần phải tải về hình ảnh, sau đó nó giải mã nó. Nếu bạn đã cuộn qua tất cả các hình ảnh có thể được lưu trong bộ nhớ cache nhưng ListBox sẽ vẫn tạo lại các điều khiển Image và do đó hình ảnh cần phải được giải mã một lần nữa.

Bạn có thể tắt ảo hóa bằng cách thiết lập VirtualizingStackPanel.IsVirtualizing attached property-false trên ListBox sau đó tất cả mọi thứ sẽ được nạp ngay lập tức, hoặc bạn có thể thay đổi VirtualizationMode để Recycling thì Images (và chứa ListBoxItems) sẽ không được vứt bỏ một lần tạo.

+0

Cảm ơn bạn H.B. +1 và chấp nhận. – Uri

3

Cách khác là thêm bộ nhớ đệm hình ảnh của riêng bạn để hình ảnh chỉ tải xuống một lần.

Sử dụng ví dụ của tôi bạn sẽ đặt này trong constructor của bạn

this.DataContext = new ViewModel(); 

Lớp sau đây sẽ lưu trữ các url và sau đó tải về hình ảnh khi tài sản hình ảnh lần đầu tiên được truy cập.

public class CachingImage 
{ 
    private readonly Uri _uri; 
    public CachingImage(string uriString) 
    { 
     _uri = new Uri(uriString, UriKind.RelativeOrAbsolute); 
    } 

    private BitmapImage _image; 

    public ImageSource Image 
    { 
     get 
     { 
      if (_image == null) 
      { 
       _image = new BitmapImage(_uri); 
       _image.DownloadCompleted += (sender, args) => ((BitmapImage)sender).Freeze(); 
      } 

      return _image; 
     } 
    } 
} 

Dưới đây là mô hình điểm

public class ViewModel 
{ 
    public ViewModel() 
    { 
     Images = Enumerable.Range(1, 1000).Select(i => new CachingImage("http://graph.facebook.com/" + i + "/picture")); 
    } 

    public IEnumerable<CachingImage> Images { get; private set; } 
    ... 

và tất nhiên bạn sẽ cần phải thay đổi XAML của bạn hơi

<ListBox ItemsSource="{Binding}"> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <Image Source="{Binding Image}" /> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 
+1

Cảm ơn bạn @Phil. +1. Đó là một cuộc gọi gần để chấp nhận. – Uri

Các vấn đề liên quan