我有两个ViewModel类:PersonViewModel和PersonSearchListViewModel。 PersonViewModel实现的字段之一是通过WCF下载的配置文件映像(在独立存储中本地缓存)。 PersonSearchListViewModel是一个容器类,它包含一个Persons列表。由于加载图像相对较重,因此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对象?
答案 0 :(得分:62)
要更新ViewModel中的DependencyProperty,请使用您用于访问任何其他UIElement的相同调度程序:
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...});
此外,必须在UI线程上实例化BitmapImages。这是因为它使用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);
答案 1 :(得分:2)
我相信你的UI线程存在交叉线程问题。
编辑绑定对象可能会强制更新工作线程上的UI,但无法成功。每当更新绑定类时,您可能需要执行InvokeRequired / Invoke hokey-pokey。
你说你已经知道了,但供参考:
答案 2 :(得分: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 Thread中构造WriteableBitmap,然后加载可以在其他线程中执行。
void DeferImageLoading( Stream imgStream )
{
// we have to give size
var bmp = new WriteableBitmap(80, 80);
imageThread.LoadThumbAsync(imgStream, bmp, this);
}
查看有关此blog post
的更多解释