简体   繁体   中英

Create and update UI elements Async Wpf

I'm trying to load several images async into the 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.

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

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?

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;
                }));
            }
        }

The trick is to freeze the 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();

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.

Hope it will help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM