首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在WPF中的后台线程中加载图像

在WPF中的后台线程中加载图像
EN

Stack Overflow用户
提问于 2011-03-25 23:09:46
回答 2查看 10.4K关注 0票数 18

在这个网站和其他论坛上已经有很多关于这个问题的问题,但是我还没有找到一个真正有效的解决方案。

我想做的是:

  • 在我的WPF应用程序中,我想加载一个图像。
  • 图像来自web上的任意URI。
  • 图像可以是任何格式。
  • 如果我多次加载同一图像,我想使用标准的windows internet缓存。

H 19图像加载和解码应该同步进行,但不能同时发生在UI线程上。H 210H 111,最终我应该以一些我可以应用于一个源属性的东西结束。H 212F 213

我尝试过的事情:

  • 在BackgroundWorker上使用WebClient.OpenRead()。工作正常,但不使用缓存。WebClient.CachePolicy只影响背景工作者上的特定WebClient WebClient WebRequest,而不是WebClient,并设置WebRequest.DefaultCachePolicy。这正确地使用了缓存,但我还没有看到一个示例,它没有给我一半time.
  • Creating中的损坏图像一个BackgroundWorker中的BitmapImage,设置BitmapImage.UriSource并尝试处理BitmapImage.DownloadCompleted。如果设置了immediately.

,这似乎使用了缓存,但是似乎没有处理DownloadCompleted,因为BackgroundWorker返回DownloadCompleted

几个月来,我一直在为这件事苦苦挣扎,我开始认为这是不可能的,但你可能比我聪明。你认为如何?

EN

回答 2

Stack Overflow用户

发布于 2011-03-26 01:28:50

我已经用几种方法来处理这个问题,包括WebClient和BitmapImage。

编辑:最初的建议是使用BitmapImage(Uri, RequestCachePolicy)构造函数,但是我意识到我测试这个方法的项目只使用本地文件,而不是web。改变指南使用我的其他测试的网页技术。

您应该在后台线程上运行下载和解码,因为在加载过程中,无论是同步的还是在下载映像之后,解码映像都需要很短但很长的时间。如果您正在加载许多图像,这可能会导致UI线程停止运行。(这里还有其他一些复杂的地方,比如DelayCreation,但它们不适用于您的问题。)

有几种加载映像的方法,但我发现在BackgroundWorker中从web加载时,您需要使用WebClient或类似的类自己下载数据。

请注意,BitmapImage在内部使用一个WebClient,而且它有很多错误处理和凭据设置以及其他我们必须针对不同情况需要解决的问题。我提供了这个片段,但它只在有限的情况下进行了测试。如果您正在处理代理、凭据或其他场景,则必须对此进行一些改进。

代码语言:javascript
复制
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
    Uri uri = e.Argument as Uri;

    using (WebClient webClient = new WebClient())
    {
        webClient.Proxy = null;  //avoids dynamic proxy discovery delay
        webClient.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Default);
        try
        {
            byte[] imageBytes = null;

            imageBytes = webClient.DownloadData(uri);

            if (imageBytes == null)
            {
                e.Result = null;
                return;
            } 
            MemoryStream imageStream = new MemoryStream(imageBytes);
            BitmapImage image = new BitmapImage();

            image.BeginInit();
            image.StreamSource = imageStream;
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.EndInit();

            image.Freeze();
            imageStream.Close();

            e.Result = image;
        }
        catch (WebException ex)
        {
            //do something to report the exception
            e.Result = ex;
        }
    }
};

worker.RunWorkerCompleted += (s, e) =>
    {
        BitmapImage bitmapImage = e.Result as BitmapImage;
        if (bitmapImage != null)
        {
            myImage.Source = bitmapImage;
        }
        worker.Dispose();
    };

worker.RunWorkerAsync(imageUri);

我在一个简单的项目中对此进行了测试,它运行得很好。我并不是说它是否击中了缓存,但从MSDN、其他论坛问题和中我可以看出,它应该是在访问缓存。WebClient封装WebRequest (包装HTTPWebRequest等等),缓存设置通过每个层传递。

BitmapImage BeginInit/EndInit对确保您可以同时设置所需的设置,然后在EndInit期间执行它。如果需要设置任何其他属性,则应该使用空构造函数,并像上面那样写出BeginInit/EndInit对,在调用EndInit之前设置所需的内容。

我通常还设置了这个选项,它强制它在EndInit期间将图像加载到内存中:

代码语言:javascript
复制
image.CacheOption = BitmapCacheOption.OnLoad;

为了获得更好的运行时性能,这将牺牲更高的内存使用量。如果您这样做,那么BitmapImage将在EndInit中同步加载,除非BitmapImage需要从URL异步下载。

进一步注释:

如果BitmapImage是一个绝对Uri,并且是一个http或https方案,那么UriSource将异步下载。您可以通过在BitmapImage.IsDownloading之后检查EndInit属性来判断它是否正在下载。有DownloadCompleted、DownloadFailed和DownloadProgress事件,但要让它们在后台线程上触发,您必须格外小心。由于BitmapImage只公开一种异步方法,因此您必须添加一个WPF等效于DoEvents()的WPF循环,以便在下载完成之前保持线程的活力。This thread展示了在这个片段中工作的DoEvents的代码:

代码语言:javascript
复制
worker.DoWork += (s, e) =>
    {
        Uri uri = e.Argument as Uri;
        BitmapImage image = new BitmapImage();

        image.BeginInit();
        image.UriSource = uri;
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.Default);
        image.EndInit();

        while (image.IsDownloading)
        {
            DoEvents(); //Method from thread linked above
        }
        image.Freeze();
        e.Result = image;
    };

虽然上述方法有效,但由于DoEvents(),它具有代码气味,而且它不允许您配置WebClient代理或其他可能有助于提高性能的东西。上面的第一个例子是推荐的。

票数 13
EN

Stack Overflow用户

发布于 2011-03-26 19:13:28

BitmapImage需要异步支持其所有事件和内部。在后台线程上调用Dispatcher.Run(),will...well运行线程的调度程序。(BitmapImage是从DispatcherObject继承的,所以它需要一个分配器。如果创建BitmapImage的线程还没有一个dispatcher,那么将根据需要创建一个新线程。很酷。)。

重要的安全提示:如果BitmapImage从缓存(老鼠)中提取数据,它不会引发任何事件。

这对我来说很管用..。

代码语言:javascript
复制
     var worker = new BackgroundWorker() { WorkerReportsProgress = true };

     // DoWork runs on a brackground thread...no thouchy uiy.
     worker.DoWork += (sender, args) =>
     {
        var uri = args.Argument as Uri;
        var image = new BitmapImage();

        image.BeginInit();
        image.DownloadProgress += (s, e) => worker.ReportProgress(e.Progress);
        image.DownloadFailed += (s, e) => Dispatcher.CurrentDispatcher.InvokeShutdown();
        image.DecodeFailed += (s, e) => Dispatcher.CurrentDispatcher.InvokeShutdown();
        image.DownloadCompleted += (s, e) =>
        {
           image.Freeze();
           args.Result = image;
           Dispatcher.CurrentDispatcher.InvokeShutdown();
        };
        image.UriSource = uri;
        image.EndInit();

        // !!! if IsDownloading == false the image is cached and NO events will fire !!!

        if (image.IsDownloading == false)
        {
           image.Freeze();
           args.Result = image;
        }
        else
        {
           // block until InvokeShutdown() is called. 
           Dispatcher.Run();
        }
     };

     // ProgressChanged runs on the UI thread
     worker.ProgressChanged += (s, args) => progressBar.Value = args.ProgressPercentage;

     // RunWorkerCompleted runs on the UI thread
     worker.RunWorkerCompleted += (s, args) =>
     {
        if (args.Error == null)
        {
           uiImage.Source = args.Result as BitmapImage;
        }
     };

     var imageUri = new Uri(@"http://farm6.static.flickr.com/5204/5275574073_1c5b004117_b.jpg");

     worker.RunWorkerAsync(imageUri);
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5439042

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档