【问题标题】:How can i show items being added one-by-one to ListView as items is added to its ItemsSource? [duplicate]当项目被添加到它的 ItemsSource 时,我如何显示被逐一添加到 ListView 的项目? [复制]
【发布时间】:2019-07-03 15:31:47
【问题描述】:

我在 WPF 中使用 ListView 来显示一些图像,这是通过使用 Image 对象填充 ObservableCollection 并将该列表设置为 ItemsSource 来完成的。我的实现如下所示:

    public partial class MainWindow : Window
{
    public ObservableCollection<Image> imageList;

    public MainWindow()
    {
        InitializeComponent();
        imageList = new ObservableCollection<Image>();
        ImageView.ItemsSource = imageList;
    }

    private void loadImages(object sender, RoutedEventArgs e)
    {
        List<string> filePaths = Directory.GetFiles(@"C:\SomeImages")
        foreach (string filePath in filePaths)
        {
            Image image = new Image();
            BitmapImage bimage = new BitmapImage();
            bimage.BeginInit();
            bimage.UriSource = new Uri(filePath, UriKind.Absolute);
            bimage.EndInit();
            image.Source = bimage;
            imageList.Add(image);
        }
    }
}

我的目的是让 UI 更新一次 foreach 循环的每次迭代,因为可观察列表已填满。然而,在将控制权返回给 UI 之前,foreach 循环会完全填满列表,然后同时显示所有图片。由于大约有 100 张图片,这会使程序滞后几秒钟。

有什么方法可以让程序一次更新 UI 一个元素,并且还可能保持 UI 响应?

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    听起来您需要以 asnyc 方式加载图像

    private async void loadImages(object sender, RoutedEventArgs e)
    {
        await Task.Run(() =>
        {
            var filePaths = Directory.EnumerateFiles(@"C:\SomeImages");
    
            foreach (string filePath in filePaths)
            {
                var bimage = new BitmapImage();
                bimage.BeginInit();
                bimage.CacheOption = BitmapCacheOption.OnLoad;
                bimage.UriSource = new Uri(filePath, UriKind.Absolute);
                bimage.EndInit();
                bimage.Freeze();
    
                Dispatcher.Invoke(() => imageList.Add(new Image { Source = bimage }));
            }
        });
    }
    

    但您不应该在代码后面创建 UI 元素。将 Image 元素移动到 ListBox 的 ItemTemplate:

    <ListBox x:Name="ImageView">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Image Source="{Binding}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    把后面的代码改成这样:

    private readonly ObservableCollection<ImageSource> imageList
        = new ObservableCollection<ImageSource>();
    
    public MainWindow()
    {
        InitializeComponent();
        ImageView.ItemsSource = imageList;
    }
    

    并将 BitmapImages 直接添加到列表中:

    Dispatcher.Invoke(() => imageList.Add(bimage)); 
    

    【讨论】:

    • 我认为更好的方法是在Dispatcher.Invoke 中只做imageList.Add(image),其余的可以异步完成。它会更顺畅
    • @KrzysztofSkowronek 你是对的,所有相关代码仍然会在 UI 线程中运行。我已经完全重写了答案,这样它才有意义。
    • 是的,现在看来是对的 :)
    • @Clemens 感谢您的编辑,我学到了更多东西
    • 谢谢,这是一个很好的方法。但是,只有通过将 ImageView.Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, (NoArgDelegate) delegate { }); 添加到另一个调用调用并将 private delegate void NoArgDelegate(); 定义为字段来强制 ImageView 更新,我才能使其工作。
    猜你喜欢
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-28
    • 2020-09-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多