WPF,BitmapFrames&跨线程问题

时间:2009-06-29 20:45:28

标签: .net wpf multithreading

我在主UI线程以外的线程上创建了一些BitmapFrame; 在创建它们之后的某个时间(以及它们的创建者线程完成),我试图在主线程中使用它们作为某些Image控件的源代码。

但是我得到了这个InvalidOperationException:调用线程无法访问该对象,因为另一个线程拥有它。

需要一些帮助,如何从主线程中访问(和使用)它们?

自第二个线程完成以来,我没有看到如何使用Dispatcher.Invoke。

提前谢谢。

1 个答案:

答案 0 :(得分:2)

您必须确保两件事:

  1. 通过调用BitmapFrame.Freeze()来冻结BitmapFrame。这会将帧变为只读,并使其可供其他线程使用。

  2. 您可能已经这样做了:要让UI线程知道框架已准备就绪,请使用Dispatcher.Invoke,而不是直接设置属性或调用UI对象的方法。

  3. 要回答Teodor的问题,如果仍在更改BitmapFrame,冻结可能会失败。当您使用BitmapFrame.Create(Uri)时,似乎会发生这种情况。以下代码似乎通过使用解码器来避免此问题。如果您以不同方式创建BitmapFrame,一般规则是您必须让它在冻结之前完成初始化,下载,解码或以其他方式更改。断开所有绑定。

    Window1.xaml

    <Window x:Class="BitmapFrameDemo.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Window1" Height="300" Width="300">
        <Grid>
            <Image Name="image"/>
        </Grid>
    </Window>
    

    Window1.xaml.cs

    using System;
    using System.Threading;
    using System.Windows;
    using System.Windows.Media.Imaging;
    using System.Windows.Threading;
    
    namespace BitmapFrameDemo {
        public partial class Window1 : Window {
            private Thread       thread     = null;
            private Dispatcher   dispatcher = null;
    
            private void ThreadMain() {
                PngBitmapDecoder decoder    = new PngBitmapDecoder(
                    new Uri("http://stackoverflow.com/content/img/so/logo.png"),
                    BitmapCreateOptions.None,
                    BitmapCacheOption.Default);
                BitmapFrame      frame      = decoder.Frames[0];
                BitmapFrame      frozen     = (BitmapFrame) frame.GetAsFrozen();
                dispatcher.Invoke(
                    new Action(() => { image.Source = frozen; }),
                    new object[] { });
            }
    
            public Window1() {
                InitializeComponent();
    
                dispatcher = Dispatcher.CurrentDispatcher;
                thread = new Thread(new ThreadStart(this.ThreadMain));
                thread.Start();
            }
        }
    }