我有一个WPF应用程序,它使用一个组件,该组件会在位图可用时将位图发送到我的应用程序,并在传递给该组件的委托(delegate)中接收这些位图。

我为此过程创建了一个新线程,该线程工作得很好,位图作为MemoryStream出现,我只是在Dispatcher.BeginInvoke方法调用中从该流创建了BitmapSource对象。在拥有BitmapSource对象之后,将它们添加到StackPanel中,以便用户可以看到可用的图像队列。到目前为止,一切都很好...

问题在于这些位图非常大,例如3000x2000 +像素,创建这些位图并将其添加到队列大约需要50毫秒,并且在执行此代码时,BeginInvoke调用中的onde会阻塞UI这次,这将导致非常烦人的行为(要重现此问题,只需每5秒调用Thread.Sleep(50)一次)。

如何解决此问题,以便用户始终保持响应状态?

谢谢!

最佳答案

您可能要考虑两个想法:

  • 不要在Dispatcher.Invoke中创建BitmapSource。实际上,这将在UI线程内创建它,从而减慢速度。而是在后台线程中创建它,将其冻结,然后将冻结的BitmapSource传递给前景线程。
  • 取决于您的应用程序,也许StackPanel不需要完整的3000x2000分辨率?如果是这种情况,请在冻结图像之前考虑缩小其背景线程中的图像的大小。

  • 以下代码执行上面的#1:

    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.IO;
    using System.Net;
    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() {
                // obtain the image memory stream
                WebRequest          request     = WebRequest.Create("http://stackoverflow.com/content/img/so/logo.png");
                WebResponse         response    = request.GetResponse();
                Stream              stream      = response.GetResponseStream();
    
                // create a bitmap source while still in the background thread
                PngBitmapDecoder    decoder     = new PngBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
                BitmapFrame         frame       = decoder.Frames[0];
    
                // freeze the bitmap source, so that we can pass it to the foreground thread
                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();
            }
        }
    }
    

    alt text http://www.freeimagehosting.net/uploads/66bdbce78a.png

    10-07 13:26