代码之家  ›  专栏  ›  技术社区  ›  devuxer

如何在WPF中将BitmapImage从后台线程传递到UI线程?

  •  22
  • devuxer  · 技术社区  · 16 年前

    我有一个生成一系列 BitmapImage 物体。每次后台线程生成完位图后,我都会向用户显示这个位图。问题是如何通过考试 位图图像 从后台线程到UI线程。

    Image 元素:

    <Image Source="{Binding GeneratedImage}" />
    

    我的视图模型有一个属性 GeneratedImage

    private BitmapImage _generatedImage;
    
    public BitmapImage GeneratedImage
    {
        get { return _generatedImage; }
        set
        {
            if (value == _generatedImage) return;
            _generatedImage= value;
            RaisePropertyChanged("GeneratedImage");
        }
    }
    

    public void InitiateGenerateImages(List<Coordinate> coordinates)
    {
        ThreadStart generatorThreadStarter = delegate { GenerateImages(coordinates); };
        var generatorThread = new Thread(generatorThreadStarter);
        generatorThread.ApartmentState = ApartmentState.STA;
        generatorThread.IsBackground = true;
        generatorThread.Start();
    }
    
    private void GenerateImages(List<Coordinate> coordinates)
    {
        foreach (var coordinate in coordinates)
        {
            var backgroundThreadImage = GenerateImage(coordinate);
            // I'm stuck here...how do I pass this to the UI thread?
        }
    }
    

    我想通过考试 backgroundThreadImage 到UI线程,它将成为 uiThreadImage ,然后设置 GeneratedImage = uiThreadImage 所以视图可以更新。我看了一些处理WPF的例子 Dispatcher

    3 回复  |  直到 16 年前
        1
  •  13
  •   Simon Fox    16 年前

    下面使用dispatcher在UI线程上执行操作委托。这使用了一个同步模型,即 Dispatcher.BeginInvoke 将异步执行委托。

    var backgroundThreadImage = GenerateImage(coordinate);
    
    GeneratedImage.Dispatcher.Invoke(
            DispatcherPriority.Normal,
            new Action(() =>
                {
                    GeneratedImage = backgroundThreadImage;
                }));
    

    更新 BitmapImage Freezable.Freeze 然后在dispatcher委托中分配给GenerateImage(由于冻结,BitmapImage变为只读,因此线程安全)。另一个选项是将图像加载到后台线程的MemoryStream中,然后使用该流和BitmapImage的StreamSource属性在dispatcher委托的UI线程上创建BitmapImage。

        2
  •  10
  •   Ray Burns    16 年前

    你需要做两件事:

    1. 冻结BitmapImage以便可以将其移动到UI线程,然后

    public void InitiateGenerateImages(List<Coordinate> coordinates)   
    {
      var dispatcher = Dispatcher.CurrentDispatcher;
    
      var generatorThreadStarter = new ThreadStart(() =>
         GenerateImages(coordinates, dispatcher));
    
      ...
    

    Application.Current.Dispatcher 获取当前调度员。缺点是:

    1. 您失去了独立于构造的应用程序对象使用视图模型的能力。

    在generator线程中,在生成图像后添加一个冻结调用,然后使用Dispatcher转换到UI线程来设置图像:

    var backgroundThreadImage = GenerateImage(coordinate);
    
    backgroundThreadImage.Freeze();
    
    dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
    {
       GeneratedImage = backgroundThreadImage;
    }));
    

      var dispatcher = Dispatcher.CurrentDispatcher;
    
      var generatorThreadStarter = new ThreadStart(() =>
         GenerateImages(coordinates, dispatcher));
    

      var generatorThreadStarter = new ThreadStart(() =>
         GenerateImages(coordinates, Dispatcher.CurrentDispatcher));
    
        3
  •  0
  •   Maxim V. Pavlov    14 年前

    在后台线程处理流。

    例如,在后台线程中:

    var artUri = new Uri("MyProject;component/../Images/artwork.placeholder.png", UriKind.Relative);
    
    StreamResourceInfo albumArtPlaceholder = Application.GetResourceStream(artUri);
    
    var _defaultArtPlaceholderStream = albumArtPlaceholder.Stream;
    
    SendStreamToDispatcher(_defaultArtPlaceholderStream);
    

    void SendStreamToDispatcher(Stream imgStream)
    {
         dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
         {
            var imageToDisplay = new BitmapImage();
            imageToDisplay.SetSource(imgStream);
    
            //Use you bitmap image obtained from a background thread as you wish!
         })); 
    }