异步创建和更新UI元素

本文关键字:UI 元素 更新 创建 异步 | 更新日期: 2023-09-27 18:04:38

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

当一个图像被加载时(一个位图是通过路径创建的),该图像应该被设置为窗口上矩形的填充。

当我把bitmapimage的创建也放在Dispatcher中时。调用方法,代码工作。但很明显,我希望繁重的工作(创建位图)在线程中完成,而不是在调用中完成。

我已经尝试了几种解决方案,包括后台工作人员,但我不能让它工作。现在我有以下代码:

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

我得到以下异常:

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

什么线索吗?

异步创建和更新UI元素

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

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

诀窍是冻结位图以允许其他线程访问。因此,您还必须确保位图在创建时立即加载,因为默认行为是惰性加载。

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

我尝试了下面的代码,它为我工作。

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是我在xaml中的网格。