C# 无效的跨线程访问问题

C# 无效的跨线程访问问题,c#,silverlight,multithreading,silverlight-3.0,C#,Silverlight,Multithreading,Silverlight 3.0,我有两个ViewModel类:PersonViewModel和PersonSearchListViewModel。PersonViewModel实现的一个字段是通过WCF(本地缓存在独立存储中)下载的配置文件映像。PersonSearchListViewModel是一个容器类,用于保存人员列表。由于加载图像相对较重,PersonSearchListViewModel只加载当前、下一页和上一页的图像(结果在UI上分页)…为了进一步提高图像的负载,我将图像的负载放在另一个线程上。然而,多线程方法会导

我有两个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"));
    }
}
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();
        }
    }
}
PersonSearchListViewModel:

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"));
    }
}
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上的dispatcher进行更新。如何更新绑定到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);
            }
        }
public void GetBitmap(int-imageID,操作回调)
{
//从服务器获取
bitmapCallback=回调;
memorialFileServiceClient.GetImageCompleted+=新事件处理程序(OnGetBitmapHandler);
memorialFileServiceClient.GetImageAsync(imageID);
}
public void OnGetBitmapHandler(对象发送方,GetImageCompletedEventArgs imageArgs)
{
如果(!imageArgs.Cancelled)
{
//我在这里得到了交叉线程错误
System.Windows.Media.Imaging.BitmapImage bi=新的BitmapImage();
ConvertToBitmapFromBuffer(bi、imageArgs.Result.Image);
//回电
调用(bi);
}
}

尝试在后台线程中创建新的BitmapImage对象时,出现跨线程错误。为什么我不能在后台线程上创建新的BitmapImage对象

我相信您的UI线程存在跨线程问题

编辑绑定对象可能会强制更新工作线程上的UI,但这不会成功。无论何时更新绑定类,都可能需要执行invokererequired/Invoke hokey pokey

你说你已经知道了,但仅供参考:


要更新ViewModel中的DependencyProperty,请使用与访问任何其他UIElement相同的dispatcher:

System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...});
此外,必须在UI线程上实例化位图图像。这是因为它使用DependencyProperties,而DependencyProperties只能在UI线程上使用。我尝试在不同的线程上实例化BitMapImage,但它就是不起作用。您可以尝试使用其他方法将图像存储在内存中。例如,下载图像时,将其存储在MemoryStream中。然后UI线程上的BitmapImage可以将其源设置为MemoryStream

您可以尝试在UI线程上实例化BitmapImage,然后在另一个线程上使用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);

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

查看更多关于此的解释

太好了,这正是我想要的。我使用字节[]作为存储的平均值。这也有助于我在设置一个属性后为多个属性引发PropertyChanged事件。我没有意识到需要在UI线程上生成BitmapImage。我真的应该想到这一点,因为如果不是这样的话,从属财产将是一个问题:facepalm::我在链接的“博客帖子”中没有找到任何解释。可能是原来的地址已经被重新使用到其他地方了-或者条目很隐蔽,我已经更新了博客链接,很抱歉给您带来不便