2009-12-17 3 views
26

Tôi có hai lớp ViewModel: PersonViewModel và PersonSearchListViewModel. Một trong những trường thực hiện PersonViewModel là một ảnh hồ sơ được tải xuống thông qua WCF (được lưu trữ cục bộ trong bộ nhớ riêng). Lớp PersonSearchListViewModel là một lớp chứa chứa danh sách Người. Kể từ khi tải hình ảnh là tương đối nặng, PersonSearchListViewModel chỉ tải hình ảnh cho trang hiện tại, tiếp theo và trước đó (kết quả được phân trang trên giao diện người dùng) ... để cải thiện hơn nữa tải của hình ảnh tôi đặt tải hình ảnh trên một chủ đề khác. Tuy nhiên phương pháp tiếp cận đa luồng gây ra các vấn đề truy cập luồng chéo.Vấn đề truy cập chuỗi chéo không hợp lệ

PersonViewModel:

public void RetrieveProfileImage() 
{ 
    Image profileImage = MemorialDataModel.GetImagePerPerson(Person); 
    if (profileImage != null) 
    { 
     MemorialDataModel.ImageManager imgManager = new MemorialDataModel.ImageManager(); 
     imgManager.GetBitmap(profileImage, LoadProfileBitmap); 
    } 
} 

private void LoadProfileBitmap(BitmapImage bi) 
{ 
    ProfileImage = bi; 
    // update 
    IsProfileImageLoaded = true; 
} 

private BitmapImage profileImage; 
public BitmapImage ProfileImage 
{ 
    get 
    { 
     return profileImage; 
    } 
    set 
    { 
     profileImage = value; 
     RaisePropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("ProfileImage")); 
    } 
} 

PersonSearchListViewModel:

private void LoadImages() 
{ 
    // load new images 
    Thread loadImagesThread = new Thread(new ThreadStart(LoadImagesProcess)); 
    loadImagesThread.Start(); 

    //LoadImagesProcess(); If executed on the same thread everything works fine 
} 

private void LoadImagesProcess() 
{ 
    int skipRecords = (PageIndex * PageSize); 
    int returnRecords; 

    if (skipRecords != 0) 
    { 
     returnRecords = 3 * PageSize; // page before, cur page and next page 
    } 
    else 
    { 
     returnRecords = 2 * PageSize; // cur page and next page 
    } 

    var persons = this.persons.Skip(skipRecords).Take(returnRecords); 

    // load images 
    foreach (PersonViewModel pvm in persons) 
    { 
     if (!pvm.IsProfileImageLoaded) 
     { 
      pvm.RetrieveProfileImage(); 
     } 
    } 
} 

Làm thế nào để bạn xử lý dữ liệu trong lớp ViewModel theo cách đa luồng? Tôi biết bạn phải sử dụng điều phối viên trên giao diện người dùng để cập nhật. Làm cách nào để cập nhật ViewModel bị ràng buộc với giao diện người dùng?

** CHỈNH SỬA **

Ngoài ra còn có một lỗi lạ khác xảy ra. Trong mã bên dưới:

 public void GetBitmap(int imageID, Action<BitmapImage> callback) 
     { 
      // Get from server 
      bitmapCallback = callback; 

      memorialFileServiceClient.GetImageCompleted += new EventHandler<GetImageCompletedEventArgs>(OnGetBitmapHandler); 
      memorialFileServiceClient.GetImageAsync(imageID); 
     } 

     public void OnGetBitmapHandler(object sender, GetImageCompletedEventArgs imageArgs) 
     { 
      if (!imageArgs.Cancelled) 
      { 
       // I get cross-thread error right here 
       System.Windows.Media.Imaging.BitmapImage bi = new BitmapImage(); 
       ConvertToBitmapFromBuffer(bi, imageArgs.Result.Image); 

       // call call back 
       bitmapCallback.Invoke(bi); 
      } 
     } 

Tôi gặp phải lỗi chéo khi cố gắng tạo đối tượng BitmapImage mới trong chuỗi nền. Tại sao tôi không thể tạo một đối tượng BitmapImage mới trên một chủ đề nền?

Trả lời

62

Để cập nhật một thuộc tính phụ thuộc trong một ViewModel, sử dụng phối cùng bạn sẽ sử dụng để truy cập vào bất kỳ UIElement khác:

System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...}); 

Ngoài ra, BitmapImages phải được khởi tạo trên chuỗi giao diện người dùng. Điều này là do nó sử dụng DependencyProperties, chỉ có thể được sử dụng trên luồng UI. Tôi đã cố gắng instantiating BitmapImages trên các chủ đề riêng biệt và nó chỉ không hoạt động. Bạn có thể thử sử dụng một số phương tiện khác để lưu trữ hình ảnh trong bộ nhớ. Ví dụ, khi bạn tải xuống hình ảnh, lưu trữ nó trong một MemoryStream. Sau đó, một BitmapImage trên thread UI có thể thiết lập nguồn của nó cho MemoryStream.

Bạn có thể thử khởi tạo BitmapImages trên chuỗi giao diện người dùng và sau đó thực hiện mọi thứ khác với BitmapImage trên một chuỗi khác ... nhưng điều đó sẽ có lông và tôi thậm chí không chắc chắn nó sẽ hoạt động. Sau đây là ví dụ:

System.Windows.Media.Imaging.BitmapImage bi = null; 
using(AutoResetEvent are = new AutoResetEvent(false)) 
{ 
    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => 
    { 
     bi = new BitmapImage(); 
     are.Set(); 
    }); 
    are.WaitOne(); 
} 

ConvertToBitmapFromBuffer(bi, imageArgs.Result.Image); 
bitmapCallback.Invoke(bi); 
+0

Tuyệt vời, đó chính xác là những gì tôi đang tìm kiếm. Tôi đã đi với byte [] như là một phương tiện lưu trữ. – Ender

+0

điều này cũng đã giúp tôi khi tôi đã phải nâng cao một sự kiện PropertyChanged cho nhiều thuộc tính sau khi một được thiết lập. –

+0

Tôi không nhận ra một BitmapImage cần thiết được thực hiện trên chuỗi giao diện người dùng. Tôi thực sự nên nghĩ rằng vì DependencyProperty sẽ là một vấn đề khác. :: facepalm :: – Paul

2

Tôi tin rằng bạn đang gặp vấn đề về luồng chéo với chuỗi giao diện người dùng.

Chỉnh sửa đối tượng bị ràng buộc có thể buộc cập nhật giao diện người dùng trên chuỗi công nhân, không thể thành công. Bạn có thể cần phải thực hiện InvokeRequired/Invoke hokey-pokey bất cứ khi nào bạn cập nhật lớp bị ràng buộc.

Bạn nói rằng bạn đã biết điều này, nhưng để tham khảo:

MSDN on thread-safe calls to UI

0

Có thể đạt được với WriteableBitmap.

public void LoadThumbAsync(Stream src, 
        WriteableBitmap bmp, object argument) 
    { 
     ThreadPool.QueueUserWorkItem(callback => 
     { 
      bmp.LoadJpeg(src); 
      src.Dispose(); 
      if (ImageLoaded != null) 
      { 
       Deployment.Current.Dispatcher.BeginInvoke(() => 
       { 
        ImageLoaded(bmp, argument); 
       }); 
      } 
     }); 
    } 

Nhưng bạn phải xây dựng WriteableBitmap trong Giao diện người dùng, sau đó tải có thể được thực hiện trong chuỗi khác.

void DeferImageLoading(Stream imgStream) 
    { 
     // we have to give size 
     var bmp = new WriteableBitmap(80, 80); 
     imageThread.LoadThumbAsync(imgStream, bmp, this); 
    } 

Xem explanaition thêm về điều này blog post

+0

Tôi không tìm thấy lời giải thích nào trong "bài đăng blog" được liên kết. Có thể là địa chỉ ban đầu đã được sử dụng lại cho một thứ khác - hoặc mục nhập được ẩn là – juhariis

+0

juhariis, tôi đã cập nhật liên kết blog, xin lỗi vì sự bất tiện – Ernest