2009-12-17 86 views
26

我有兩個ViewModel類:PersonViewModel和PersonSearchListViewModel。 PersonViewModel實現的一個字段是通過WCF下載的配置文件映像(本地緩存在獨立存儲中)。 PersonSearchListViewModel是一個容納人員列表的容器類。由於加載圖像相對較重,因此PersonSearchListViewModel僅加載當前頁面,下一頁面和上一頁面的圖像(結果在UI上分頁)......以進一步提高圖像加載的負載,從而將圖像加載到另一個線程上。但是,多線程方法會導致跨線程訪問問題。無效的跨線程訪問問題

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(); 
     } 
    } 
} 

你如何處理多線程方式的ViewModel類數據?我知道你必須使用UI上的調度器來更新。你如何更新綁定到UI的ViewModel?

**編輯**

還有一個更奇怪的錯誤發生。在下面的代碼中:

 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); 
      } 
     } 

當我嘗試在後臺線程中創建一個新的BitmapImage對象時出現跨線程錯誤。爲什麼我無法在後臺線程上創建新的BitmapImage對象?

回答

62

爲了以更新視圖模型一個DependencyProperty,使用相同的調度員,你會用它來訪問任何其他的UIElement:

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

此外,BitmapImages必須在UI線程上實例化。這是因爲它使用了DependencyProperties,它只能在UI線程上使用。我試圖在單獨的線程上實例化BitmapImages,但它不起作用。您可以嘗試使用其他方法將圖像存儲在內存中。例如,當您下載圖像時,將其存儲在MemoryStream中。然後,UI線程上的BitmapImage可以將其源設置爲MemoryStream。

您可以嘗試在UI線程上實例化BitmapImages,然後在另一個線程上使用BitmapImage執行其他任何操作......但這樣會變得毛茸茸,我甚至不確定它會工作。下面是一個例子:

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

非常好explanaition,這正是我一直在尋找。我用byte []作爲存儲的意思。 – Ender 2009-12-18 14:59:33

+0

這也幫助了我,當我必須在設置一個屬性後爲多個屬性引發PropertyChanged事件時。 – 2012-03-30 14:59:50

+0

我沒有意識到需要在UI線程上進行BitmapImage。我真的應該想到,因爲一個DependencyProperty將是一個問題,否則。 :: facepalm :: – Paul 2014-04-18 20:11:02

2

我相信你的UI線程存在跨線程問題。

編輯綁定對象可能會強制更新工作線程上的UI,但這無法成功。無論何時更新綁定類,您都可能需要執行InvokeRequired/Invoke hokey-pokey。

你說你知道這一點了,但供參考:

MSDN on thread-safe calls to UI

0

它可以用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); 
       }); 
      } 
     }); 
    } 

但您必須在UI線程中構造WriteableBitmap,然後才能在其他線程中執行加載。

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

見本blog post

+0

我在鏈接的「博客文章」中找不到任何解釋。可能是因爲原來的地址已經被重複使用到了別的地方 - 或者入口被隱藏起來 – juhariis 2015-02-15 10:19:02

+0

juhariis,我更新了博客鏈接,抱歉給您帶來不便 – Ernest 2015-03-03 15:28:55

相關問題