2013-04-16 58 views
7

Tôi muốn tải Gravatar-Images và đặt chúng từ mã phía sau thành Điều khiển hình ảnh WPF. Vì vậy, các mã trông nhưĐặt WPF Tải ảnh không đồng bộ

imgGravatar.Source = GetGravatarImage(email); 

đâu GetGravatarImage trông giống như:

BitmapImage bi = new BitmapImage(); 
bi.BeginInit(); 
bi.UriSource = new Uri(GravatarImage.GetURL("http://www.gravatar.com/avatar.php?gravatar_id=" + email) , UriKind.Absolute); 
bi.EndInit(); 
return bi; 

Thật không may này khóa GUI khi kết nối mạng chậm. Có cách nào để chỉ định nguồn hình ảnh và cho phép tải hình ảnh trong nền mà không chặn giao diện người dùng không?

Cảm ơn!

Trả lời

0

Bạn có thể muốn xem xét câu hỏi này:

Link

Và tôi sẽ khuyên bạn nên khởi động một nhiệm vụ Asynchrone, và đặt mã mà cần phải được async trong nhiệm vụ đó.

19

Tôi đề nghị bạn sử dụng Binding trên imgGravatar của bạn từ XAML. Đặt IsAsync = true trên đó và WPF sẽ tự động sử dụng một chuỗi từ nhóm chủ đề để kéo hình ảnh của bạn. Bạn có thể đóng gói logic giải quyết vào một IValueConverter và chỉ đơn giản là liên kết email như Nguồn

trong XAML:

<Window.Resouces> 
    <local:ImgConverter x:Key="imgConverter" /> 
</Window.Resource> 

... 


<Image x:Name="imgGravatar" 
     Source="{Binding Path=Email, 
         Converter={StaticResource imgConverter}, 
         IsAsync=true}" /> 

trong Code:

public class ImgConverter : IValueConverter 
{ 
    public override object Convert(object value, ...) 
    { 
     if (value != null) 
     { 
      BitmapImage bi = new BitmapImage(); 
      bi.BeginInit(); 
      bi.UriSource = new Uri( 
       GravatarImage.GetURL(
        "http://www.gravatar.com/avatar.php?gravatar_id=" + 
         value.ToString()) , UriKind.Absolute 
       ); 
      bi.EndInit(); 
      return bi;     
     } 
     else 
     { 
      return null; 
     } 

    } 
} 
+1

BitmapImage cũng có [hàm tạo với tham số Uri] (http://msdn.microsoft.com/en-us/library/ms602473.aspx), giúp bạn lưu các cuộc gọi BeginInit/EndInit. – Clemens

+0

+1 không biết về nó, gợi ý hay. Tôi chỉ sao chép mã từ tác giả và không nghĩ cách tối ưu hóa việc tải xuống thực tế, thay vào đó tôi tập trung vào giải quyết vấn đề khối UI một cách thanh lịch (và dĩ nhiên nó chặn, nếu mã không đồng bộ). Tôi nghĩ rằng để sử dụng lặp đi lặp lại, bộ chuyển đổi cũng có thể nhận được url đầy đủ từ ràng buộc để được tái sử dụng nhiều hơn, thay vì email. Tôi không thích sử dụng 'ThreadPool' bởi vì bạn phải quản lý việc điều phối tất cả các thời gian. Điều này làm cho nó không đủ thanh lịch với tôi cho một ứng dụng MVVM thuần túy. – JanW

+0

Bạn có thể giải thích tại sao việc tạo BitmapImage sẽ chặn khi không được gọi là không đồng bộ. Tại sao nó lại có thuộc tính 'IsDownloading' và' DownloadCompleted'. Nếu bạn chỉ cần đặt thuộc tính 'Nguồn' của Hình ảnh trong XAML thành URL có tham chiếu hình ảnh lớn trên web, giao diện người dùng sẽ không chặn. Thay vào đó, WPF tải hình ảnh xuống dưới nền và hiển thị nó ngay sau khi quá trình tải xuống hoàn tất. – Clemens

6

Tôi không thể hiểu tại sao mã của bạn sẽ chặn giao diện người dùng, vì BitmapImage hỗ trợ tải xuống dữ liệu hình ảnh trong nền. Đó là lý do tại sao nó có thuộc tính IsDownloading và sự kiện DownloadCompleted.

Dù sao, mã sau đây cho thấy cách đơn giản để tải xuống và tạo hình ảnh hoàn toàn trong một chuỗi riêng biệt (từ ThreadPool). Nó sử dụng một cá thể WebClient để tải xuống toàn bộ bộ đệm hình ảnh, trước khi tạo một BitmapImage từ bộ đệm đó. Sau khi BitmapImage được tạo, nó gọi Freeze để làm cho nó có thể truy cập được từ chuỗi giao diện người dùng. Cuối cùng, nó gán thuộc tính Source của Điều khiển hình ảnh trong chuỗi giao diện người dùng bằng cách gọi số Dispatcher.BeginInvoke.

ThreadPool.QueueUserWorkItem(
    o => 
    { 
     var webClient = new WebClient(); 
     var url = GravatarImage.GetURL("http://www.gravatar.com/avatar.php?gravatar_id=" + email); 
     var buffer = webClient.DownloadData(url); 
     var bitmapImage = new BitmapImage(); 

     using (var stream = new MemoryStream(buffer)) 
     { 
      bitmapImage.BeginInit(); 
      bitmapImage.CacheOption = BitmapCacheOption.OnLoad; 
      bitmapImage.StreamSource = stream; 
      bitmapImage.EndInit(); 
      bitmapImage.Freeze(); 
     } 

     Dispatcher.BeginInvoke((Action)(() => image.Source = bitmapImage)); 
    }); 
Các vấn đề liên quan