C# 如何在后台加载图像?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/716472/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 22:18:04  来源:igfitidea点击:

How do I load images in the background?

c#wpfimagedelegates

提问by kevindaub

I am trying to load an image in the background and then update the UI. I have been playing with this all day and I don't know what I am missing. I keep getting the following error:

我正在尝试在后台加载图像,然后更新 UI。我一整天都在玩这个,我不知道我错过了什么。我不断收到以下错误:

"The calling thread cannot access this object because a different thread owns it."

“调用线程无法访问此对象,因为另一个线程拥有它。”

I've hunted around following example after example, but I cannot seem to find an answer. I also wrapped the code that is touching the UI in another BeginInvoke.

我在一个又一个例子中四处寻找,但我似乎找不到答案。我还在另一个 BeginInvoke 中包装了接触 UI 的代码。

Update 3: The moral of the story. ImageSource is not thread safe for access.

更新 3:故事的寓意。ImageSource 对于访问不是线程安全的。

Update 2: This has got to be a simple solution :). I tried the cloning, but that didn't result in success, but I did get a different error: "Exception has been thrown by the target of an invocation."

更新 2:这必须是一个简单的解决方案:)。我尝试了克隆,但这并没有成功,但我确实收到了一个不同的错误:“调用的目标抛出了异常。”

Update 1: I tried the BackgroundWorker, but I am still getting the same error, but it is occurring on the brush.ImageSource.Height. Am I signaling the UI correctly? Any suggestions?

更新 1:我尝试了 BackgroundWorker,但我仍然遇到相同的错误,但它发生在 Brush.ImageSource.Height 上。我是否正确地向 UI 发出信号?有什么建议?

Here is my XAML:

这是我的 XAML:

<Window x:Class="Slideshow.Show"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DockPanel>
        <Canvas Background="Black" Name="canvas">
            <Viewbox Name="background" Stretch="Uniform">
                <Rectangle name="background" />
            </Viewbox>
        </Canvas>
    </DockPanel>
</Window>

Here is some of the code behind:

下面是一些背后的代码:

namespace Slideshow
{
    public class Show 
    {
        public Show()
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += 
                new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.RunWorkerAsync();
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            BitmapSource bitmap = e.Result as BitmapSource;

            if (bitmap != null)
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.Normal 
                    (ThreadStart)delegate()
                {
                    Image image = new Image();
                    image.Source = bitmap;
                    background.Child = image;
                 });
            }
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BitmapSource bitmapSource = CreateBrush(GetRandomFile());
            e.Result = bitmapSource;
         }
    }
}

采纳答案by bendewey

What I tend to use the ThreadPool.QueueUserWorkItemto load the image, then when the operation completes I call back to the UI thread using the thread-safe Dispatcherobject. The image source is not thread safe, you will have to use something like the JpegBitmapDecoder, there is also a PngBitmapDecoder.

我倾向于使用ThreadPool.QueueUserWorkItem加载图像,然后当操作完成时,我使用线程安全Dispatcher对象回调 UI 线程。图像源不是线程安全的,你将不得不使用类似的JpegBitmapDecoder,也有PngBitmapDecoder

For Example:

例如:

public Window()
{
    InitializeComponent();

    ThreadPool.QueueUserWorkItem(LoadImage,
         "http://z.about.com/d/animatedtv/1/0/1/m/simp2006_HomerArmsCrossed_f.jpg");
}

public void LoadImage(object uri)
{
    var decoder = new JpegBitmapDecoder(new Uri(uri.ToString()), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
    decoder.Frames[0].Freeze();
    this.Dispatcher.Invoke(DispatcherPriority.Send, new Action<ImageSource>(SetImage), decoder.Frames[0]);
}

public void SetImage(ImageSource source)
{
    this.BackgroundImage.Source = source;
}

回答by Joshua

You want to incorporate multiple threads in your WPF application. As stated in the article below, WPF forces you to do all UI work on the thread that created the UI.

您希望在 WPF 应用程序中合并多个线程。如下文所述,WPF 强制您在创建 UI 的线程上执行所有 UI 工作。

Check this out: http://pjbelfield.wordpress.com/2007/10/29/worker-threads-and-the-dispatcher-in-wpf/

看看这个:http: //pjbelfield.wordpress.com/2007/10/29/worker-threads-and-the-dispatcher-in-wpf/

and for intensive UI work:

对于密集的 UI 工作:

http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/

http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/

回答by vanja.

Your code to load the image (.jpg) into memory should be done in the background. Code to load the image from memory into a WPF control must be done in the normal UI WPF thread. Only slow/long running tasks should be put into a background thread. Once the long task is complete, it must signal the UI thread to update the view with the result of the long task.

将图像 (.jpg) 加载到内存中的代码应该在后台完成。将图像从内存加载到 WPF 控件的代码必须在普通 UI WPF 线程中完成。只有缓慢/长时间运行的任务才应放入后台线程。一旦长任务完成,它必须通知 UI 线程用长任务的结果更新视图。

My favourite way is using the BackgroundWorker class:

我最喜欢的方法是使用 BackgroundWorker 类:

var bg = new System.ComponentModel.BackgroundWorker();
bg.DoWork += Work_Function;
bg.RunWorkerCompleted += UI_Function;
bg.RunWorkerAsync();

void Work_Function(object sender, DoWorkEventArgs e) { ... }
void UI_Function(object sender, RunWorkerCompletedEventArgs e) { ... }

When you call RunWorkerAsync(), first your Work_Function() is called. When it is complete, the UI_Function() is called. The work function may place one variable in the DoWorkEventArgs.Result property, which is accessible from the RunWorkerCompletedEventArgs.Result property.

当您调用 RunWorkerAsync() 时,首先会调用您的 Work_Function()。完成后,将调用 UI_Function()。工作函数可以在 DoWorkEventArgs.Result 属性中放置一个变量,该属性可从 RunWorkerCompletedEventArgs.Result 属性访问。

I think you might also be interested in this article: http://www.codeproject.com/KB/WPF/WPF_Explorer_Tree.aspxIt shows how to set up Lazy Loading in a tree view in WPF. I think the basic concept (add a dummy node, intercept the display of the dummy node by loading the next image instead) will apply to your work.

我想你可能也对这篇文章感兴趣:http://www.codeproject.com/KB/WPF/WPF_Explorer_Tree.aspx它展示了如何在 WPF 的树视图中设置延迟加载。我认为基本概念(添加一个虚拟节点,通过加载下一个图像来拦截虚拟节点的显示)将适用于您的工作。

回答by Samuel

The error you are getting now is good news, it means that BackgroundWorkeris working. You could try cloning it in the RunWorkerCompletedwith brush.Clone();.

你现在得到的错误是个好消息,这意味着它BackgroundWorker正在起作用。你可以尝试在RunWorkerCompletedwith 中克隆它brush.Clone();

Try removing the Dispatcher.BeingInvoke part. The whole point of BackgroundWorker is that the event is marshaled back into the UI thread for you automatically so you don't have to worry.

尝试删除 Dispatcher.BeingInvoke 部分。BackgroundWorker 的全部意义在于,该事件会自动为您编组回 UI 线程,因此您不必担心。

回答by Methos

One more thing we had in our project. Since ImageSource is placed into UI you have to check if it is frozen:

我们项目中还有一件事。由于 ImageSource 被放置在 UI 中,因此您必须检查它是否被冻结:

public void SetImage(ImageSource source)
{
   ImageSource src = null;
   if(!source.IsFrozen)
       src = source.GetAsFrozen();
   else
       src = source; 
   this.BackgroundImage.Source = src;
}