C# 从线程方法获取值

C# 从线程方法获取值,c#,wpf,multithreading,C#,Wpf,Multithreading,我试图访问一个数据库,获取一个图像并返回它,这样它就可以显示在一个Border对象中。(所以我也将字节[]数据转换为图像) 此过程需要足够的时间来注意UI已冻结 下面是一些代码: ImageBrush imgb = new ImageBrush(); imgb.ImageSource = GlobalDB.GetImage(album.name, 400); // Size is 400px, this is the time-consuming part. AlbumArt.Backgroun

我试图访问一个数据库,获取一个图像并返回它,这样它就可以显示在一个Border对象中。(所以我也将字节[]数据转换为图像) 此过程需要足够的时间来注意UI已冻结

下面是一些代码:

ImageBrush imgb = new ImageBrush();
imgb.ImageSource = GlobalDB.GetImage(album.name, 400); // Size is 400px, this is the time-consuming part.
AlbumArt.Background = imgb;
我尝试使用backgroundworker,但这给了我一个异常,说我不能从不同的线程调用。我得到了这个异常,因为BackgroundWorker.RunWorkerCompleted显然属于另一个线程(?)

添加到最后一位:Runworkercompleted完成了以下操作:

AlbumArt.Background = imgb;
我现在不知道该怎么办。
如有任何建议,将不胜感激

不确定GetImage正在做什么,但您可以将代码封装到任务中:

Task.Factory.StartNew(() => {
    ImageBrush imgb = new ImageBrush();
    imgb.ImageSource = GlobalDB.GetImage(album.name, 400);
    AlbumArt.Background = imgb;
});

RunWorkerCompleted
仅在UI线程上调用

我在您的代码中看到的唯一问题是
imgb
是在后台线程中创建的,WPF有一个约束,即DependencyProperty源和Dependency对象应该在同一线程上创建。

因此,在将imgb分配给后台调用之前,请在其上调用
Freeze()

imgb.Freeze();
AlbumArt.Background = imgb;

原因被冻结的对象被允许跨线程访问。它们不是线程仿射的

正如在另一个答案中所建议的,您也可以使用异步任务。但是,在冻结ImageBrush之后,必须将
Background
属性的赋值分派给UI线程

Task.Factory.StartNew(() =>
{
    var imgb = new ImageBrush(GlobalDB.GetImage(album.name, 400));
    imgb.Freeze();

    Dispatcher.BeginInvoke(new Action(() => AlbumArt.Background = imgb));
});

我得问问。你的视图模型在哪里?Rohit Vats的回答或多或少会让工作完成,但是你并不是按照惯用的方式来处理这个问题。您应该有一个带有如下代码的ViewModel:

public class AlbumViewModel: BaseViewModel // BaseViewModel is your code that implements INotifyPropertyChanged
{
private string name
public string Name
{
   get{ return name;}
   set
   {
      if(name != value)
      {
          name = value;
          FirePropertyChanged("Name");
          LoadBackgroundImageAsync(value);
      }
   }

}

private ImageSource backgroundImage;
public ImageSource BackgroundImage
{
     get{return backgroundImage;}
     private set
     {
       if(backgroundImage != value)
       {
           background = value;
           FirePropertyChanged("BackgroundImage");
       }
     }
}
private Task LoadBackgroundImageAsync(string name)
{
    var retv = new Task(()=>
    {
        var source = GlobalDB.GetImage(name, 400);
        source.Freeze();
        BackgroundImage = source;
    });
    retv.Start();
    return retv;
}
}

然后只需绑定到属性,让WPF担心更新UI。

这不起作用,因为无法从任务线程访问
AlbumArt.Background
。您必须将分配分派到UI线程。这是正确的,但需要注意的是,这只适用于.net 4+,在3.5中,您必须自己将更新封送到UI线程。@Yaur-这不是真的。这就是后台工作程序从一开始就实现的方式,与.Net版本无关<代码>BackgroundRoker在调用RunWorkerAsync时捕获SynchronizationContext。然后,它使用捕获的同步上下文执行RunWorkerCompleted。因此,如果从UI线程调用RunWorkerAsync,则RunWorkerCompleted将仅在UI线程上调用。如果您直接处理视图属性(实际上不应该这样做),那么您必须始终担心封送到UI线程
BackgroundWorker
是实现这一点的一种方法。。。尽管在您的回答中没有提到,并且假设图像源是在背景线程上创建的,那么您拥有的代码在连续行中没有有效的上下文。离开我的+1,因为OP缺少的关键是冻结。你的两种说法都是矛盾的。首先,你说BW不适用于3.5及更低版本。现在你们说BW是一种在UI线程上整理错误内容的方法(这就是为什么在回答中并没有提到)。也没有提到在BW的DoWork处理程序中获取图像,因为OP已经知道了,正如他在问题中提到的。无论如何,如果OP已经理解了代码中的问题,我们就结束这一切。谢谢你的+1。真的很感激:)我觉得我错过了很多。因为我只是在实践中学习。我想在我用数据绑定做任何不必要的事情之前,我必须仔细阅读这些东西。谢谢你的回答!看来有几件事要读。