创建和更新 UI 元素 Async Wpf

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15506527/
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-09-13 08:10:51  来源:igfitidea点击:

Create and update UI elements Async Wpf

c#wpfmultithreadingasynchronous

提问by middelpat

I'm trying to load several images async into the ui.

我正在尝试将多个图像异步加载到 ui 中。

When an image is loaded (a bitmap is created form a path), the image should be set as the fill of a rectangle on the window.

加载图像时(从路径创建位图),图像应设置为窗口上矩形的填充。

When I put the creation of the bitmapimage also in the Dispatcher.Invoke method, the code works. But obviously I want the heavy work (creation of the bitmap) done in the tread and not in the invoke.

当我将位图图像的创建也放在 Dispatcher.Invoke 方法中时,代码就起作用了。但显然我希望在胎面而不是在调用中完成繁重的工作(创建位图)。

I've tried several solutions including backgroundworker but I can't get it to work. Right now I have the following code:

我已经尝试了多种解决方案,包括 backgroundworker,但我无法让它工作。现在我有以下代码:

private void Window_ContentRendered(object sender, EventArgs e)
{
    Thread loadThread = new Thread(new ThreadStart(LoadImagesAsync));
    loadThread.Start();
}

private void LoadImagesAsync()
{
    IEnumerable<string> images = System.IO.Directory.GetFiles(IMAGE_FOLDER, "*.jpg").Skip(_PageNumber * NUMBER_OF_IMAGES).Take(NUMBER_OF_IMAGES);

    for (int i = 0; i < NUMBER_OF_IMAGES; i++)
    {
        var bitm = new BitmapImage(new Uri(images.ElementAt(i)));                

        this.Dispatcher.Invoke(() =>
        {
            Grid grid = (Grid)grd_photoBox.Children[i];

            var rectangle = (from e in grid.Children.OfType<Rectangle>()
                                where e is Rectangle
                                select e).First();

            ImageBrush brush = new ImageBrush(bitm);

            rectangle.Fill = brush;
        });
    }
}

I get the following exception:

我收到以下异常:

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

Any clues?

有什么线索吗?

回答by SynerCoder

In case of big images, use the following code, it will directly rescale the images and load the in other thread then the main thread which makes sure the images are loaded instantly. Edit the following values to change the load format:

在大图像的情况下,使用以下代码,它将直接重新缩放图像并在其他线程中加载,然后在主线程中加载以确保图像立即加载。编辑以下值以更改加载格式:

bitm.DecodePixelWidth = 200;
bitm.DecodePixelHeight = 100;


private void LoadImagesAsync()
        {
            IEnumerable<string> images = System.IO.Directory.GetFiles(IMAGE_FOLDER, "*.jpg").Skip(_PageNumber * NUMBER_OF_IMAGES).Take(NUMBER_OF_IMAGES);

            for (int i = 0; i < NUMBER_OF_IMAGES; i++)
            {
                int j = i;
                var bitm = new BitmapImage();

                bitm.BeginInit();
                bitm.CacheOption = BitmapCacheOption.OnLoad;
                bitm.UriSource = new Uri(images.ElementAt(i));
                bitm.DecodePixelWidth = 200;
                bitm.DecodePixelHeight = 100;
                bitm.EndInit();

                ImageBrush brush = new ImageBrush(bitm);
                brush.Freeze();

                this.Dispatcher.BeginInvoke(new Action(() => 
                {
                    Grid grid = (Grid)grd_photoBox.Children[j];

                    var rectangle = (from e in grid.Children.OfType<Rectangle>()
                                        where e is Rectangle
                                        select e).First();

                    rectangle.Fill = brush;
                }));
            }
        }

回答by Clemens

The trick is to freezethe bitmap to allow access from another thread. Therefore you also have to make sure that the bitmap is loaded immediately on creation, as the default behaviour is to load lazily.

诀窍是冻结位图以允许从另一个线程访问。因此,您还必须确保位图在创建时立即加载,因为默认行为是延迟加载。

var bitm = new BitmapImage();
bitm.BeginInit();
bitm.CacheOption = BitmapCacheOption.OnLoad; // load immediately
bitm.UriSource = new Uri(images.ElementAt(i));
bitm.EndInit();
bitm.Freeze();

回答by Mukesh Rawat

I tried below code and it is working for me.

我尝试了下面的代码,它对我有用。

Task<IEnumerable<string>>.Factory.StartNew(() => System.IO.Directory.GetFiles(
            imagePath,
            "*.jpg")).
            ContinueWith(task =>
                        {
                            foreach (var item in task.Result)
                            {
                                this.Dispatcher.BeginInvoke((Action)(() =>
                                {
                                    var img = new Image
                                                    {
                                                        Source =
                                                            new BitmapImage(
                                                            new Uri(item))
                                                    };
                                    LayoutRoot.Children.Add(img);

                                }));

                            }
                        });

LayoutRoot is my grid in xaml.

LayoutRoot 是我在 xaml 中的网格。

Hope it will help.

希望它会有所帮助。