【问题标题】:c# wpf Updating UI source from BlockingCollection with Dispatcherc# wpf 使用 Dispatcher 从 BlockingCollection 更新 UI 源
【发布时间】:2013-04-11 01:13:02
【问题描述】:

这是我的问题。
我在 BlockingCollection 中加载了一些 BitmapImages

    public void blockingProducer(BitmapImage imgBSource)
    {
        if (!collection.IsAddingCompleted)
            collection.Add(imgBSource);
    }

加载发生在背景工作线程中。

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        String filepath; int imgCount = 0;

        for (int i = 1; i < 10; i++)
        {
            imgCount++;

            filepath = "Snap";
            filepath += imgCount;
            filepath += ".bmp";

            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                label1.Content = "Snap" + imgCount + " loaded.";
            }), DispatcherPriority.Normal);

            BitmapImage imgSource = new BitmapImage();
            imgSource.BeginInit();
            imgSource.UriSource = new Uri(filepath, UriKind.Relative);
            imgSource.CacheOption = BitmapCacheOption.OnLoad;
            imgSource.EndInit();

            blockingProducer(imgSource);
        }
    }

调试这部分代码一切正常,现在问题来了……

完成加载图像后,我想在 UI 中一张一张地显示它们。我正在使用调度程序来执行此操作,但我总是收到消息,告诉我被调用的线程无法访问该对象,因为它属于不同的线程。

    public void display(BlockingCollection<BitmapImage> results)
    {
        foreach (BitmapImage item in collection.GetConsumingEnumerable())
        {
            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                this.dstSource.Source = item;
                Thread.Sleep(250);
            }), DispatcherPriority.Background);
        }
    }

debug 指责错误在这里

                this.dstSource.Source = item;

我正在尝试一切,但无法找出问题所在。有人知道吗?

【问题讨论】:

    标签: c# wpf user-interface dispatcher blockingcollection


    【解决方案1】:

    您必须在加载图像后调用Freeze 以使其他线程可以访问它们:

    BitmapImage imgSource = new BitmapImage();
    imgSource.BeginInit();
    imgSource.UriSource = new Uri(filepath, UriKind.Relative);
    imgSource.CacheOption = BitmapCacheOption.OnLoad;
    imgSource.EndInit();
    imgSource.Freeze(); // here
    

    据我了解BitmapCacheOption.OnLoad 标志,它仅在从流中加载 BitmapImage 时有效。 BitmapCacheOption 中的备注部分说:

    如果你想关闭一个缓存选项,请将 CacheOption 设置为 BitmapCacheOption.OnLoad 用于创建 BitmapImage 的流。默认 OnDemand 缓存 选项保留对流的访问,直到需要图像,并且 清理由垃圾收集器处理。

    从 Uri 创建的 BitmapImage 可以异步加载(请参阅IsDownloading 属性)。因此,Freeze 可能无法在此类 BitmapImage 上调用,因为在 EndInit 之后下载可能仍在进行中。我想它仍然适用于您的情况,因为您正在从文件 Uris 加载 BitmapImages,这似乎是立即完成的。

    为了避免这个潜在的问题,您可以从 FileStream 创建 BitmapImage:

    var imgSource = new BitmapImage();
    
    using (var stream = new FileStream(filepath, FileMode.Open))
    {
        imgSource.BeginInit();
        imgSource.StreamSource = stream;
        imgSource.CacheOption = BitmapCacheOption.OnLoad;
        imgSource.EndInit();
        imgSource.Freeze();
    }
    

    【讨论】:

    • 嗨,谢谢它的工作。最后一期。将图像加载到 BlockingCollection 后,我开始显示它们,但只显示最后一张图像。知道发生了什么吗?谢谢。
    • 这肯定是因为您在传递给 Dispatcher 的委托方法中执行了 Sleep 调用。您必须在 BeginInvoke 之前调用它。但是,更好的解决方案是使用DispatcherTimer 一张一张地显示图像。
    • 嗨克莱门斯,我删除了睡眠,但它仍然不起作用。奇怪的事实是,当我第二次调用加载线程时,图像就会显示出来。但是一旦我调用显示函数,什么都没有发生。所以我第一次运行程序时只显示最后一张图像。从现在开始,每次我调用加载线程时,都会显示图像,而当我调用显示线程时,什么都不会发生。
    • 尝试在 DispatcherTimer 中拉取并显示下一个图像。
    • 嗨 Clemens .. 我尝试了 DispatcherTimer,但现在似乎没有任何效果。我在下面发布了代码。任何想法?谢谢。
    【解决方案2】:

    对于未来的读者,这是我用来解决问题的代码。

        public void display(BlockingCollection<BitmapImage> collection)
        {
            if (collection.IsCompleted || collection.Count != 0)
            {
                BitmapImage item = collection.Take();
                this.Dispatcher.BeginInvoke(new Action(() =>
                {
                    this.dstSource.Source = item;
    
                }), DispatcherPriority.Normal);
            }
            else
            {
                dispatcherTimer.Stop();
            }
        }
    
        public void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            display(collection);
        }
    
        public void configureDispatcherTimer()
        {
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
            TimeSpan interval = TimeSpan.FromMilliseconds(150);
            dispatcherTimer.Interval = interval;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-23
      • 1970-01-01
      • 2023-03-05
      相关资源
      最近更新 更多