2つのViewModelクラスがあります。PersonViewModelとPersonSearchListViewModelです。 PersonViewModelが実装するフィールドの1つは、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オブジェクトを作成できないのはなぜですか?
ViewModelのDependencyPropertyを更新するには、他のUIElementへのアクセスに使用するのと同じディスパッチャーを使用します。
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...});
また、BitmapImagesはUIスレッドでインスタンス化する必要があります。これは、UIスレッドでのみ使用できるDependencyPropertiesを使用するためです。別のスレッドで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);
UIスレッドにクロススレッドの問題があると思います。
バインドされたオブジェクトを編集すると、ワーカースレッドのUIが強制的に更新される可能性があり、成功しません。バインドされたクラスを更新するたびに、InvokeRequired/Invoke hokey-pokeyを実行する必要がある可能性があります。
あなたはすでにこれを知っていると言いましたが、参考までに:
これは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);
}
これについての詳しい説明を見る ブログ投稿